【问题标题】:Julia: Converting Vector of Arrays to Array for Arbitrary DimensionsJulia:将数组的向量转换为任意维度的数组
【发布时间】:2016-09-25 09:36:04
【问题描述】:

使用时序测试,我发现使用push! 增长Vector{Array{Float64}} 对象比简单地使用Array{Float64} 对象和hcatvcat 的性能要高得多。但是,计算完成后,我需要将生成的对象更改为Array{Float64} 以进行进一步分析。有没有一种不管尺寸如何都有效的方法?例如,如果我通过

生成Arrays 的Vector
u =  [1 2 3 4
      1 3 3 4
      1 5 6 3
      5 2 3 1]
uFull = Vector{Array{Int}}(0)
push!(uFull,u)
for i = 1:10000
  push!(uFull,u)
end

我可以这样转换:

fill = Array{Int}(size(uFull)...,size(u)...)
for i in eachindex(uFull)
  fill[i,:,:] = uFull[i]
end

但请注意,这要求我知道数组是矩阵(二维)。如果它是 3 维的,我需要另一个 :,因此这不适用于任意维度。

请注意,我还需要一种任意维度的“逆变换”形式(除了第一个由完整数组的最后一个索引索引),我目前有

filla = Vector{Array{Int}}(size(fill)[end])
  for i in 1:size(fill)[end]
filla[i] = fill[:,:,i]' 
end

我认为第一次转换的方法也可能解决第二次。

【问题讨论】:

  • 我认为您首先需要sizehint!,而不是来回切换Vector{Array{Float64}}Array{Float64}docs.julialang.org/en/release-0.4/stdlib/collections/…!
  • 这实际上没有意义,因为使用数组向量会跳过连接到数组所需的大量复制。我认为不可能让它更快。以及如何将sizehint! 添加到数组中?例如,我试过uFull = usizehint!(uFull,100000)for i = 1:100000uFull = hcat(uFull,u)end
  • 你确实复制了上面的例子两次。我建议你使用 sizehint! 给 Vector{Float64} 预分配并使用 push!append! 来增长它(意思是复制一次)。稍后您可以将其重塑为多维数组。
  • 我必须测试一下哪个更快,因为我真的不能给出一个好的sizehint!
  • 你为什么使用Array{Int}而不给出尺寸?我认为这没有Array{Int,2}(对于二维数组)那么快(因为它没有完全参数化)

标签: arrays type-conversion julia


【解决方案1】:

这是 Julia 的 custom array infrastructure 擅长的事情。我认为这里最简单的解决方案是实际制作一个特殊的数组类型来为您进行这种转换:

immutable StackedArray{T,N,A} <: AbstractArray{T,N}
    data::A # A <: AbstractVector{<:AbstractArray{T,N-1}}
    dims::NTuple{N,Int}
end
function StackedArray(vec::AbstractVector)
    @assert all(size(vec[1]) == size(v) for v in vec)
    StackedArray(vec, (length(vec), size(vec[1])...))
end
StackedArray{T, N}(vec::AbstractVector{T}, dims::NTuple{N}) = StackedArray{eltype(T),N,typeof(vec)}(vec, dims)
Base.size(S::StackedArray) = S.dims
@inline function Base.getindex{T,N}(S::StackedArray{T,N}, I::Vararg{Int,N})
    @boundscheck checkbounds(S, I...)
    S.data[I[1]][Base.tail(I)...]
end

现在只需将您的向量包装在 StackedArray 中,它的行为就像一个 N+1 维数组。这可以扩展并使其更具功能(它可以类似地支持setindex! 甚至push!ing 数组以本地连接),但我认为这足以解决您的问题。通过简单地将uFull 包装在StackedArray 中,您将获得一个行为类似于Array{T, N+1} 的对象。创建一个copy,您就可以得到一个完全密集的Array{T, N+1},而无需自己编写一个for循环。

julia> S = StackedArray(uFull)
10001x4x4 StackedArray{Int64,3,Array{Array{Int64,2},1}}:
[:, :, 1] =
 1  1  1  5
 1  1  1  5
 1  1  1  5
…

julia> squeeze(S[1:1, :, :], 1) == u
true

julia> copy(S) # returns a dense Array{T,N}
10001x4x4 Array{Int64,3}:
[:, :, 1] =
 1  1  1  5
 1  1  1  5
…

最后,我要注意这里还有另一种解决方案:您可以引入自定义数组类型很快,并创建一个GrowableArray,在内部将其元素存储为线性Vector{T},但允许直接推送整个列或数组。

【讨论】:

【解决方案2】:

Matt B. 的答案很棒,因为它“模拟”了一个数组,而无需实际创建或存储它。当您可以使用此解决方案时,它可能是您的最佳选择。

但是,在某些情况下,您可能需要创建一个串联数组(例如,如果您将其传递给一些需要连续内存的 C 代码)。在这种情况下,您只需调用cat,它是通用的(它可以处理任意尺寸)。

例如:

u =  [1 2 3 4
      1 3 3 4
      1 5 6 3
      5 2 3 1]
uFull = Vector{typeof(u)}(0)
push!(uFull,u)
for i = 1:10000
  push!(uFull,u)
end
ucat = cat(ndims(eltype(uFull))+1, uFull)

我冒昧地对您的代码进行了一项重要更改:uFull = Vector{typeof(u)}(0),因为它确保存储在 Vector 容器中的对象具有具体类型。 Array{Int} 实际上是一种抽象类型,因为您还需要指定维度 (Array{Int,2})。

【讨论】:

  • 今天早上我做了一些类似的改变,并以“成长”的方式实现了它。如果你想看一下,我为它制作了一个名为 GrowableArrays.jl 的包(它也有测试中的基准)
猜你喜欢
  • 2022-01-16
  • 1970-01-01
  • 2021-01-31
  • 2017-12-24
  • 2015-11-23
  • 1970-01-01
  • 2018-03-31
  • 1970-01-01
相关资源
最近更新 更多