Julia中的多维数组推导式

14

我正在尝试使用Julia进行多维数组推导,但似乎无法正常工作。我正在使用OSX的0.20-pre夜间版本构建; 这可能是构建中的错误。然而,我怀疑这是用户的错误。

假设我想得到如下结果:

5x2 Array
1 6
2 7
3 8
4 9
5 10

我不想仅仅使用reshape函数。从我的理解来看,应该像这样生成一个多维数组:[(x, y) for x in 1:5, y in 6:10]。但是这将生成一个5x5的元组数组:

julia> [(x, y) for x in 1:5, y in 6:10]
5x5 Array{(Int64,Int64),2}:
 (1,6)  (1,7)  (1,8)  (1,9)  (1,10)
 (2,6)  (2,7)  (2,8)  (2,9)  (2,10)
 (3,6)  (3,7)  (3,8)  (3,9)  (3,10)
 (4,6)  (4,7)  (4,8)  (4,9)  (4,10)
 (5,6)  (5,7)  (5,8)  (5,9)  (5,10)

或者,也许我想为每个值生成一组值和一个布尔代码:

5x2 Array
1 false
2 false
3 false
4 false
5 false

我似乎只能用{(x, y) for x in 1:5, y=false}创建一个元组数组。如果我去掉x,y周围的圆括号,我会得到ERROR:syntax:missing separator in array expression的错误提示。无论我用什么包装x,y,我总是得到这种输出-- Array,Array {Any} Tuple

我猜想:这里有些东西我不太明白。是否有人愿意帮助我理解一下?

7个回答

12

我认为你只是把列表推导式读错了。

julia> [x+5y for  x in 1:5, y in 0:1]
5x2 Array{Int64,2}:
 1   6
 2   7
 3   8
 4   9
 5  10

当您在多个维度中使用它们时,会得到两个变量,并需要基于坐标的单元格值函数。

对于您的第二个问题,我认为您应重新考虑您的要求。Julia使用类型化数组以提高性能,并且在不同列中存储不同类型是可能的。要获取未类型化的数组,您可以使用{}而不是[],但我认为更好的解决方案是拥有一个元组数组(Int,Bool)或者更好的是只使用两个数组(一个用于int,另一个用于bool)。

julia> [(i,false) for i in 1:5]
5-element Array{(Int64,Bool),1}:
 (1,false)
 (2,false)
 (3,false)
 (4,false)
 (5,false)

是的,我确实了解我所做的不符合Julia的优化方式 - 但是,目前我需要的是可变数组,以便稍后可以将false翻转为true;使用元组无法实现这一点,遗憾的是。不过,使用两个单独的数组可能会非常有效!我会看看这个建议,谢谢你的建议和时间! - Gastove

12

我认为理解并不适合你想要做的事情。原因可以在Julia手册的数组理解章节中找到:

A = [ F(x,y,...) for x=rx, y=ry, ... ]
这个表达式的含义是,使用给定数值列表中的变量x、y等计算F(x,y,...)。数值可以被指定为任何可迭代对象,但通常是像1:n或2:(n-1)这样的范围,或者是像[1.2, 3.4, 5.7]这样的显式值数组。结果是一个N维密集数组,其维度是变量范围rx、ry等维度的串联,并且每个F(x,y,...)的计算结果返回一个标量。
这里有一个警告,如果你将其中一个变量设置为多于1维的数组,则似乎会首先对其进行展平;因此,“结果是...维度为变量范围rx、ry等维度的串联的数组”这一说法并不是真正准确的,因为如果rx是2x2,ry是3,那么你不会得到一个2x2x3的结果,而是一个4x3的结果。但是根据上述内容,你得到的结果应该是有意义的:你正在返回一个元组,所以这就是放在数组单元格中的内容。不会自动将返回的元组扩展为数组行。
如果你想从表达式中获得一个5x2的数组,你需要确保x的长度为5,y的长度为2。然后,每个单元格都包含函数在从x和y的每个可能组合中作为参数计算的结果。事实上,示例数组中单元格的值并不需要使用两个参数计算函数。相反,你只是要将两个预定列粘在一起成为一个二维数组。为此,请使用hcat或文字:
- hcat(1:5, 6:10) - [1:5 5:10] - hcat(1:5, falses(5)) - [1:5 falses(5)]
如果你想创建一个二维数组,其中第2列包含在第1列上评估函数的结果,则可以使用如下的表达式:
f(x) = x + 5
[ y ? f(x) : x for x=1:5, y=(false,true) ]

但这有点令人困惑,我认为更直观的做法是

x = 1:5
hcat( x, map(f,x) )

好的,这个加上@ivarne下面的评论就有意义了。我来自Python,但显然第一遍阅读手册时我没有理解透彻。感谢帮助澄清!我剩下的问题是:我找到了 [ 1:5 falses(5) ] ,它是完美的,除了它将false编码为0。Julia文档指定Julia不使用0/1进行布尔编码。 0 == false 返回true,但 if(0) 抛出了一个类型错误。有没有一种方法可以将数字与布尔值 hcat 在一起,返回布尔值而不是Int编码? - Gastove
@Gastove,这里发生的情况是false提升Int64。我一直在尝试,但无法找到一种优雅的方法来获取带有falses的5x2数组。您可以尝试键入该数组,但我无法使其正常工作:Any[ 1:5 falses(5) ]将返回一个1x2(范围和falses数组都没有扩展),而Union(Number,Bool)[ 1:5 falses(5) ]会抛出错误。以下是可行的代码:a = Array(Union(Number,Bool),5,2); a[:,1] = 1:5; a[:,2] = falses(5) - Sean Mackesey
非常感谢您的帮助。我有一种预感,制作一个适当尺寸的单元数组,然后填充它,会起作用,但我还没有足够的时间去尝试。我仍在适应Julian思维。 - Gastove

1
你的直觉是写[(x, y) for x in 1:5, y in 6:10],但实际上你需要用zip将范围包装起来,像这样:[(x, y) for x, y in zip(range(1, 6), range(6, 11))]
[i for i in zip(1:5, 6:10)]

这将为您提供非常接近您所需的内容,即:

5-element Array{(Int64,Int64),1}:
(1,6) 
(2,7) 
(3,8) 
(4,9) 
(5,10)

为了准确获取您需要的内容,您需要:
hcat([[i...] for i in zip(1:5, 6:10)]...)'

这将给你:
5x2 Array{Int64,2}:
1   6
2   7
3   8
4   9
5  10

1

我有点喜欢 @fawr 给出的关于在保留可变性的情况下提高数据类型效率的答案,但这很快就会得到你想要的结果(参考 Shawn 的回答):

hcat(1:5,6:10)
hcat({i for i=1:5},falses(5))

第二部分的单元数组推导式强制数据类型为Any而不是IntXX。
这也可以工作:
hcat(1:5,{i for i in falses(5)})

我还没有找到另一种将数组显式转换为任意类型的方法,除了使用推导式。

0

我找到了一种通过vcatsplat运算符生成数值多维数组的方法:

R = [ [x y] for x in 1:3, y in 4:6 ] # make the list of rows
A = vcat(R...) # make n-dim. array from the row list

那么R将是一个3x3 Array{Array{Int64,2},2},而A是一个9x2 Array{Int64,2},正如您所希望的那样。

对于第二种情况(一组值和每个值对应的布尔代码),可以这样做:

R = [[x y > 5] for x in 1:3, y in 4:6] # condition is y > 5
A = vcat(R...)

A将是一个9x2的Array{Int64,2},其中true/false分别用1/0表示。

我已在Julia 0.4.7中测试过这些内容。


0

正如 @ivarne 所指出的

[{x,false} for  x in 1:5]

这将起作用并给你可变的东西。


0

这是另一种(虽然有些复杂)的方法:

x1 = 1
x2 = 5

y1 = 6
y2 = 10

x = [x for x in x1:x2, y in y1:y2]
y = [y for x in x1:x2, y in y1:y2]

xy = cat(2,x[:],y[:])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接