Julia:扁平化数组/元组数组

Posted

技术标签:

【中文标题】Julia:扁平化数组/元组数组【英文标题】:Julia: Flattening array of array/tuples 【发布时间】:2018-04-11 19:41:35 【问题描述】:

在 Julia 中,vec 将多维数组重塑为一维数组。 但是它不适用于数组数组或元组数组。 使用数组理解的一部分,是否有另一种方法来展平数组/元组的数组?还是数组数组/数组元组/元组?或者...

【问题讨论】:

【参考方案1】:

如果您使用来自RecursiveArrayTools.jl 的VectorOfArray,它使用索引回退为VectorOfArray A 提供convert(Array,A)

julia> using RecursiveArrayTools

julia> A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
3-element ArrayArrayInt64,1,1:
 [1, 2, 3]
 [4, 5, 6]
 [7, 8, 9]

julia> VA = VectorOfArray(A)
3-element ArrayArrayInt64,1,1:
 [1, 2, 3]
 [4, 5, 6]
 [7, 8, 9]

首先它充当一个惰性包装器,用于在不进行转换的情况下进行索引:

julia> VA[1,3]
7

请注意,列是单独的数组,因此它仍然是“列主要的”(即有效地向下索引列)。但是它有一个直接的转换:

julia> convert(Array,VA)
3×3 ArrayInt64,2:
 1  4  7
 2  5  8
 3  6  9

处理这种转换的另一种方法是执行hcat(A...) 之类的操作,但如果您有很多数组要飞溅,那会很慢!

现在,您可能会想:编写一个预先分配矩阵然后循环并填充它的函数怎么样?这几乎就是VectorOfArray 上的convert 的工作原理,除了convert 在这里使用的后备使用Tim Holy 的笛卡尔机器。有一次,我写了这个函数:

function vecvec_to_mat(vecvec)
  mat = Matrixeltype(eltype(vecvec))(length(vecvec),length(vecvec[1]))
  for i in 1:length(vecvec)
    mat[i,:] .= vecvec[i]
  end
  mat
end

但我已经摆脱了它,因为回退要快得多。所以,YMMV,但这是解决您问题的几种方法。

【讨论】:

【参考方案2】:

Iterators.flatten(x) 创建一个生成器,它迭代x 的每个元素。它可以处理您描述的一些情况,例如

julia> collect(Iterators.flatten([(1,2,3),[4,5],6]))
6-element ArrayAny,1:
 1
 2
 3
 4
 5
 6

如果您有由数组和元组组成的数组,您可能应该重新考虑您的数据结构,因为它听起来类型不稳定。但是,您可以多次调用flatten,例如

julia> collect(Iterators.flatten([(1,2,[3,3,3,3]),[4,5],6]))
6-element ArrayAny,1:
 1            
 2            
  [3, 3, 3, 3]
 4            
 5            
 6            

julia> collect(Iterators.flatten(Iterators.flatten([(1,2,[3,3,3,3]),[4,5],6])))
9-element ArrayAny,1:
 1
 2
 3
 3
 3
 3
 4
 5
 6

请注意我的所有示例如何返回ArrayAny,1。这对性能来说是一个坏信号,因为这意味着编译器无法确定输出数组元素的单一具体类型。我选择这些示例是因为我阅读您的问题的方式听起来您可能已经输入了不稳定的容器。

【讨论】:

这正是我想要的!就我而言,它只有数组,没有元组,所以这不是我的问题。然而,在添加“迭代器”包并调用“使用迭代器”之后,当我尝试使用 flatten(在 REPL 中)时,它告诉我该函数不存在我在 Julia 0.6.1 Iterators 现在已内置到基础中,旧的 Iterators 包已弃用。我不知道如果你现在安装它会发生什么,但如果你有任何问题卸载它。您需要using Base.Iterators 获取导出的方法(包括flatten)或import Base.Iterators: flatten 获取一个。【参考方案3】:

为了展平一个数组,你可以像这样简单地使用 vcat():

julia> A = [[1,2,3],[4,5], [6,7]]
VectorInt64[3]
    Int64[3]
    Int64[2]
    Int64[2]
julia> flat = vcat(A...)
Int64[7]
    1
    2
    3
    4
    5
    6
    7

【讨论】:

似乎不适用于数组数组的数组。 如果您的数组不是任意嵌套的,这似乎是性能最高的选项。【参考方案4】:

对于 Julia 0.7x:

对于数组:

flat(arr::Array) = mapreduce(x -> isa(x, Array) ? flat(x) : x, 追加!, arr,init=[])

对于元组:

flat(arr::Tuple) = mapreduce(x -> isa(x, Tuple) ? flat(x) : x, 追加!, arr,init=[])

适用于任意深度。 见:https://rosettacode.org/wiki/Flatten_a_list#Julia 数组/元组代码:

function flatten(arr)
    rst = Any[]
    grep(v) =   for x in v
                if isa(x, Tuple) ||  isa(x, Array)
                grep(x) 
                else push!(rst, x) end
                end
    grep(arr)
    rst
end

【讨论】:

【参考方案5】:

最简单的方法是应用省略号... 两次。

A = [[1,2,3],[4,5], [6,7]]
flat = [(A...)...]
println(flat)

输出将是 [1, 2, 3, 4, 5, 6, 7].

【讨论】:

这是非常棒且非常紧凑的语法,但我仍然无法理解 ... splat 运算符的工作原理 - 请您详细介绍如何这个表达式被评估了吗? 在函数调用中使用 splatting 运算符将单个参数拆分为多个参数 [julialang 的常见问题解答]docs.julialang.org/en/v1/manual/faq/…。在这里,它在数组文字 [ ] 中使用,它也可以工作。如果操作符被使用一次,它会将 splatted 数组的元素组合回一个数组,基本上什么都不做。使用括号,splat 操作被评估两次:它将 splatted 数组拆分回一个数组。开箱、开箱、打包。 这非常简洁,但我会警告你,splat 运算符可能会引入一些非常意外的减速。试试x = [rand(n) for n in rand(1:20, 100000)]。比较@btime [(x...)...](21.215 毫秒和 1049049 次分配)与@btime vcat(x...)(2.836 毫秒和 3 次分配)之间的差异。

以上是关于Julia:扁平化数组/元组数组的主要内容,如果未能解决你的问题,请参考以下文章

关于ES6的flat(扁平化数组)

数组扁平化专栏

实现数组扁平化的几种方式

数组扁平化

数组扁平化

扁平化数组,多维数组变一维数组