在Julia中切片和广播多维数组:meshgrid示例

Nat*_*han 6 slice multidimensional-array julia

我最近开始通过编写自组织映射的简单实现来学习Julia.我希望用户指定地图的大小和尺寸,这意味着我不能真正使用for循环来处理地图数组,因为我事先并不知道我需要多少层循环.所以我绝对需要广播和切片功能,这些功能适用于任意维度的数组.

现在,我需要构建一个地图索引数组.说我的地图是由大小的数组定义mapsize = (5, 10, 15),我需要构建一个数组indices大小的(3, 5, 10, 15)地方indices[:, a, b, c]应返回[a, b, c].

我来自Python/NumPy背景,其中解决方案已由特定的"函数"给出,mgrid:

indices = numpy.mgrid[:5, :10, :15]
print indices.shape # gives (3, 5, 10, 15)
print indices[:, 1, 2, 3] gives [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

我没想到朱莉娅在一开始就有这样的功能,所以我转向广播.在NumPy中,广播基于一系列规则,我发现这些规则非常清晰和合乎逻辑.只要每个维度中的大小匹配或其中一个为1,您就可以在同一个表达式中使用不同维度的数组:

(5, 10, 15)   broadcasts to  (5, 10, 15) 
   (10,  1)

(5,  1, 15)   also broadcasts to (5, 10, 15) 
(1, 10,  1)
Run Code Online (Sandbox Code Playgroud)

为此,您还可以使用numpy.newaxis或None轻松地向阵列添加新维度:

array = numpy.zeros((5, 15))
array[:,None,:] has shape (5, 1, 15)
Run Code Online (Sandbox Code Playgroud)

这有助于轻松播放阵列:

A = numpy.arange(5)
B = numpy.arange(10)
C = numpy.arange(15)
bA, bB, bC = numpy.broadcast_arrays(A[:,None,None], B[None,:,None], C[None,None,:])
bA.shape == bB.shape == bC.shape = (5, 10, 15)
Run Code Online (Sandbox Code Playgroud)

使用它,创建indices数组非常简单:

indices = numpy.array(numpy.broadcast_arrays(A[:,None,None], B[None,:,None], C[None,None,:]))
(indices == numpy.mgrid[:5,:10,:15]).all() returns True
Run Code Online (Sandbox Code Playgroud)

一般情况当然有点复杂,但可以使用列表理解和切片来解决:

arrays = [ numpy.arange(i)[tuple([None if m!=n else slice(None) for m in range(len(mapsize))])] for n, i in enumerate(mapsize) ]
indices = numpy.array(numpy.broadcast_arrays(*arrays))
Run Code Online (Sandbox Code Playgroud)

所以回到朱莉娅.我尝试应用相同的理由,最终实现了arrays上面代码列表的等效.由于复合表达式语法,这最终比NumPy对应物更简单:

arrays = [ (idx = ones(Int, length(mapsize)); idx[n] = i;reshape([1:i], tuple(idx...))) for (n,i)=enumerate(mapsize) ]
Run Code Online (Sandbox Code Playgroud)

现在我被困在这里,因为我真的不知道如何将广播应用到我的生成数组列表中...这些broadcast[!]函数要求应用函数f,而我没有.我尝试使用for循环来尝试强制广播:

indices = Array(Int, tuple(unshift!([i for i=mapsize], length(mapsize))...))
for i=1:length(mapsize)
    A[i] = arrays[i]
end
Run Code Online (Sandbox Code Playgroud)

但这给了我一个错误: ERROR: convert has no method matching convert(::Type{Int64}, ::Array{Int64,3})

我这样做是对的吗?我忽略了重要的事情吗?任何帮助表示赞赏.

tho*_*oly 5

如果你正在运行julia 0.4,你可以这样做:

julia> function mgrid(mapsize)
           T = typeof(CartesianIndex(mapsize))
           indices = Array(T, mapsize)
           for I in eachindex(indices)
               indices[I] = I
           end
           indices
       end
Run Code Online (Sandbox Code Playgroud)

如果人们可以说,那就更好了

indices = [I for I in CartesianRange(CartesianIndex(mapsize))]
Run Code Online (Sandbox Code Playgroud)

我会调查一下:-).


Toi*_*son 5

Julia中的广播几乎都是在NumPy中广播的,所以你应该发现它遵循或多或少相同的简单规则(不确定当不是所有输入具有相同数量的维度时,填充尺寸的方式是否相同但是,因为Julia数组是列专业的.

然而,许多有用的东西,比如newaxis索引,broadcast_arrays还没有实现.(我希望他们愿意.)另请注意,与NumPy相比,Julia中的索引工作方式略有不同:当您在NumPy中留下尾随维度的索引时,其余索引默认为冒号.在朱莉娅,他们可以说是默认为一些.

我不确定你是否真的需要一个meshgrid函数,大多数你想要使用它的东西可以通过使用arrays数组的原始条目和广播操作来完成.meshgrid在matlab中有用的主要原因是因为它在广播中很糟糕.

但是使用该broadcast!函数完成您想要的操作非常简单:

# assume mapsize is a vector with the desired shape, e.g. mapsize = [2,3,4]

N = length(mapsize)
# Your line to create arrays below, with an extra initial dimension on each array
arrays = [ (idx = ones(Int, N+1); idx[n+1] = i;reshape([1:i], tuple(idx...))) for (n,i) in enumerate(mapsize) ]

# Create indices and fill it one coordinate at a time
indices = zeros(Int, tuple(N, mapsize...))
for (i,arr) in enumerate(arrays)
    dest = sub(indices, i, [Colon() for j=1:N]...)
    broadcast!(identity, dest, arr)
end
Run Code Online (Sandbox Code Playgroud)

我必须在条目上添加一个初始单例维度arrays以与轴对齐indices(newaxis在这里很有用......).然后我浏览每个坐标,在相关部分创建一个子阵列(一个视图)indices并填充它.(索引将默认返回Julia 0.4中的子数组,但是现在我们必须sub明确使用).

调用broadcast!仅评估identity(x)=x输入上的标识函数arr=arrays[i],广播到输出的形状.使用该identity功能没有效率损失; broadcast!基于给定函数,参数数量和结果维度数生成专用函数.