组合不等长的向量

Ale*_*lec 5 julia

x = [1, 2, 3, 4]
y = [1, 2]
Run Code Online (Sandbox Code Playgroud)

如果我希望能够对两个填充默认值的向量进行操作,有哪些策略?

例如想要执行以下操作并隐式填写0missing

x + y        # would like [2, 4, 3, 4] 
Run Code Online (Sandbox Code Playgroud)

理想情况下,我希望以通用的方式执行此操作,以便我可以对两者进行任意操作。

Ted*_*ing 3

不管 Julia 是否有内置的东西可以做到这一点,请记住 Julia 很快。这意味着您可以编写代码来支持这种需求。

\n
extend!(x, y::Vector, default=0) = extend!(x, length(y), default)\nextend!(x, n::Int, default=0) = begin\n    while length(x) < n\n        push!(x, default)\n    end\n    x\nend\n
Run Code Online (Sandbox Code Playgroud)\n

然后,当您拥有您所描述的代码时,您可以对称地扩展xy

\n
x = [1, 2, 3, 4]\ny = [1, 2]\nextend!(x, y)\nextend!(y, x)\nx + y\n==> [2, 4, 3, 4]\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,这会发生突变y。在许多情况下,所需的长度将来自代码外部,并将应用于xy。我还可以想象这0通常是一个糟糕的默认值(即使它在您的加法上下文中完全合适)。

\n

下面的评论提出了一个有价值的观点,即您应该考虑使用append!而不是循环push!。事实上,如果您关心非常小的差异,最好像这样测量差异。我继续测试:

\n
julia> using BenchmarkTools\njulia> extend1(x, n) = begin\n       while length(x) < n\n           push!(x, 0)\n       end\n       x\nend\njulia> @btime begin\n       x = rand(10)\n       sum(x)\n       end\n  59.815 ns (1 allocation: 160 bytes)\n5.037723569560573\n\njulia> @btime begin\n       x = rand(10)\n       extend1(x, 1000)\n       sum(x)\n       end\n  7.281 \xce\xbcs (8 allocations: 20.33 KiB)\n6.079832879992913\njulia> x = rand(10)\njulia> @btime begin\n       x = rand(10)\n       append!(x, zeros(990))\n       sum(x)\n       end\n  1.290 \xce\xbcs (3 allocations: 15.91 KiB)\n3.688526541987817\njulia> \n
Run Code Online (Sandbox Code Playgroud)\n

将基元推入循环中非常快,分配一个零向量以便我们可以使用append!会稍微快一些。

\n

但这里真正的教训是,循环版本需要几微秒的时间来附加近 1000 个值(多次重新分配数组)。逐一追加 10 个值只需 150 纳秒多一点(append!稍微快一些)。这速度快得令人眼花缭乱。从字面上看,在 R 或 Python 中什么都不做可能需要比这更长的时间。

\n

这种差异在某些情况下很重要,而在许多其他情况下则无法察觉。如果重要的话,就衡量一下。如果没有,请做您想到的最简单的事情,因为 Julia 会为您提供支持(在性能方面)。

\n

进一步更新

\n

从科林的另一条评论中得到暗示,这里是我们使用append!但不分配列表的结果。相反,我们使用生成器……即一种数据结构,当需要数据时,它会通过类似于列表的接口来发明数据。结果比我上面展示的要好得多。

\n
julia> @btime begin\n       x = rand(10)\n       append!(x, (0 for i in 1:990))\n       sum(x)\n       end\n  565.814 ns (2 allocations: 8.03 KiB)\n
Run Code Online (Sandbox Code Playgroud)\n

请注意 周围的圆括号0 for i in 1:990

\n

最后,科林是对的。如果我们能够避免相关的开销,那么使用append!会快得多。令人惊讶的是,基本函数Iterators.repeated(0, 990)要慢得多

\n

但是,无论如何,所有这些选项都非常快,并且所有这些选项都可能如此之快,以至于这些细微的差异都不重要。

\n

朱莉娅很有趣!

\n