Julia 中的多维数组理解
Multi-dimensional array comprehension in Julia
我可以创建以下 (length(X),) 数组:
[F(x) for x in X]
但是,如果 F(x) returns 是一个数组,有没有办法使用推导式创建多维数组,其中每一行都是 F(x),数组的维度是 (length(X),length(F(x))
?
在 Julia 中有很多方法可以做到这一点!在您使用理解之后,将 F(x)
的数组输出转换为二维数组的一种简单方法是连接结果,或者对参数进行 vcat
, hcat
, or the more generic cat
, and using a reduce
操作:
F(x) = [x, x*2, x*3]
X = collect(1:5)
reduce(vcat, [F(x)' for x in X])
# 5×3 Array{Int64,2}:
# 1 2 3
# 2 4 6
# 3 6 9
# 4 8 12
# 5 10 15
因为您特别请求 length(X)
by length(F(x))
数组,所以必须在 F(x)
的输出上使用伴随运算符 '
以正确定位它 vcat
。您也可以对 hcat
执行相同操作并转置结果:
reduce(hcat, [F(x) for x in X])'
# 5×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
# 1 2 3
# 2 4 6
# 3 6 9
# 4 8 12
# 5 10 15
调用 reduce
是必需的,因为 vcat
和 hcat
将它们的所有参数连接在一起——如果你只给它们 [F(x) for x in X]
,它们会认为你是传递一个单一的数组数组并简单地 return 那是因为没有什么可以连接的。
您还应该看看 function broadcasting,这是一个让 Julia 有别于许多其他语言的特性。它可以帮助减少其他语言中依赖于列表理解的代码的冗长性。只需在函数名称之后和参数之前添加一个句点,即可自动将函数广播到参数的所有元素上!
Y = collect(2:2:10)
reduce(hcat, F.(Y))'
# 5×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
# 2 4 6
# 4 8 12
# 6 12 18
# 8 16 24
# 10 20 30
高级:函数组合
正如@CameronBiegnanek 指出的那样,Julia 支持使用 ∘
运算符的 function composition。将两个函数组合在一起,如 (G ∘ F)(x)
转换为 G(F(x))
,因此您可以将 adjoint
操作与函数 F
组合在一起,如下所示:
reduce(vcat, (adjoint ∘ F).(X))
但是这种操作(映射一个数组所有元素的函数,然后reducing所有这些输出使用另一个函数) 是一种常见的数据处理方式,称为“map-reduce”操作。 Julia 有一个 built-in mapreduce
函数可以做到这一点!
mapreduce(adjoint ∘ F, vcat, X)
如果 F(x)
returns 一个元组或 StaticArray,即将推出的 Julia 1.6 将允许您像这样执行您的要求:
reinterpret(reshape, T, [F(x) for x in X])
演示:
julia> reinterpret(reshape, Int, [(i, i+1) for i=1:4])
2×4 reinterpret(reshape, Int64, ::Vector{Tuple{Int64, Int64}}) with eltype Int64:
1 2 3 4
2 3 4 5
我可以创建以下 (length(X),) 数组:
[F(x) for x in X]
但是,如果 F(x) returns 是一个数组,有没有办法使用推导式创建多维数组,其中每一行都是 F(x),数组的维度是 (length(X),length(F(x))
?
在 Julia 中有很多方法可以做到这一点!在您使用理解之后,将 F(x)
的数组输出转换为二维数组的一种简单方法是连接结果,或者对参数进行 vcat
, hcat
, or the more generic cat
, and using a reduce
操作:
F(x) = [x, x*2, x*3]
X = collect(1:5)
reduce(vcat, [F(x)' for x in X])
# 5×3 Array{Int64,2}:
# 1 2 3
# 2 4 6
# 3 6 9
# 4 8 12
# 5 10 15
因为您特别请求 length(X)
by length(F(x))
数组,所以必须在 F(x)
的输出上使用伴随运算符 '
以正确定位它 vcat
。您也可以对 hcat
执行相同操作并转置结果:
reduce(hcat, [F(x) for x in X])'
# 5×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
# 1 2 3
# 2 4 6
# 3 6 9
# 4 8 12
# 5 10 15
调用 reduce
是必需的,因为 vcat
和 hcat
将它们的所有参数连接在一起——如果你只给它们 [F(x) for x in X]
,它们会认为你是传递一个单一的数组数组并简单地 return 那是因为没有什么可以连接的。
您还应该看看 function broadcasting,这是一个让 Julia 有别于许多其他语言的特性。它可以帮助减少其他语言中依赖于列表理解的代码的冗长性。只需在函数名称之后和参数之前添加一个句点,即可自动将函数广播到参数的所有元素上!
Y = collect(2:2:10)
reduce(hcat, F.(Y))'
# 5×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
# 2 4 6
# 4 8 12
# 6 12 18
# 8 16 24
# 10 20 30
高级:函数组合
正如@CameronBiegnanek 指出的那样,Julia 支持使用 ∘
运算符的 function composition。将两个函数组合在一起,如 (G ∘ F)(x)
转换为 G(F(x))
,因此您可以将 adjoint
操作与函数 F
组合在一起,如下所示:
reduce(vcat, (adjoint ∘ F).(X))
但是这种操作(映射一个数组所有元素的函数,然后reducing所有这些输出使用另一个函数) 是一种常见的数据处理方式,称为“map-reduce”操作。 Julia 有一个 built-in mapreduce
函数可以做到这一点!
mapreduce(adjoint ∘ F, vcat, X)
如果 F(x)
returns 一个元组或 StaticArray,即将推出的 Julia 1.6 将允许您像这样执行您的要求:
reinterpret(reshape, T, [F(x) for x in X])
演示:
julia> reinterpret(reshape, Int, [(i, i+1) for i=1:4])
2×4 reinterpret(reshape, Int64, ::Vector{Tuple{Int64, Int64}}) with eltype Int64:
1 2 3 4
2 3 4 5