'N' 维 numpy 数组的操作

Operations on 'N' dimensional numpy arrays

我正在尝试概括一些 Python 代码以对任意维度的数组进行操作。这些操作应用于数组中的每个向量。因此,对于一维数组,只有一个操作,对于二维数组,它将是行和列的(线性的,所以顺序无关紧要)。例如,一维数组 (a) 很简单:

b = operation(a)

其中 'operation' 需要一维数组。对于二维数组,操作可能会按

进行
for ii in range(0,a.shape[0]):
    b[ii,:] = operation(a[ii,:])
for jj in range(0,b.shape[1]):
    c[:,ii] = operation(b[:,ii])

我想在不需要事先知道数组维度的情况下进行一般化,并且不需要为每个可能的维度设置大量 if/elif 语句。 对于一维或二维通用的解决方案是可以的,但最好是完全通用的解决方案。实际上,我不认为任何大于 2 的维度都需要这个,但如果我能看到一个一般的例子,我会学到一些东西!

额外信息: 我有一个使用单元格做类似事情的 matlab 代码,但我不完全理解它是如何工作的。在这个例子中,每个向量都被重新排列(与numpy.fft中的fftshift基本相同的功能)。不确定这是否有帮助,但它对任意维度的数组进行操作。

function aout=foldfft(ain)
nd = ndims(ain);
for k = 1:nd
    nx = size(ain,k);
    kx = floor(nx/2);
    idx{k} = [kx:nx 1:kx-1];
end
aout = ain(idx{:});

如果您正在寻找一种编程方式来为 k-th 维索引一个 n 维数组,那么 numpy.take 可能会对您有所帮助。

下面给出了foldfft的实现作为例子:

In[1]:
import numpy as np

def foldfft(ain):
    result = ain
    nd = len(ain.shape)
    for k in range(nd):
        nx = ain.shape[k]
        kx = (nx+1)//2
        shifted_index = list(range(kx,nx)) + list(range(kx))
        result = np.take(result, shifted_index, k)
    return result

a = np.indices([3,3])
print("Shape of a = ", a.shape)
print("\nStarting array:\n\n", a)
print("\nFolded array:\n\n", foldfft(a))


Out[1]:
Shape of a =  (2, 3, 3)

Starting array:

 [[[0 0 0]
  [1 1 1]
  [2 2 2]]

 [[0 1 2]
  [0 1 2]
  [0 1 2]]]

Folded array:

 [[[2 0 1]
  [2 0 1]
  [2 0 1]]

 [[2 2 2]
  [0 0 0]
  [1 1 1]]]

您可以使用 numpy.ndarray.flat,它允许您对 n 维 numpy 数组进行线性迭代。您的代码应如下所示:

b = np.asarray(x)
for i in range(len(x.flat)):
    b.flat[i] = operation(x.flat[i])

在 Octave 中,您的 MATLAB 代码可以:

octave:19> size(ain)
ans =
   2   3   4
octave:20> idx
idx = 
{
  [1,1] =
     1   2
  [1,2] =
     1   2   3
  [1,3] =
     2   3   4   1
}

然后它使用 idx 元胞数组来索引 ain。有了这些尺寸,它 'rolls' 尺寸 4 尺寸。

对于 5 和 6,索引列表将是:

 2   3   4   5   1
 3   4   5   6   1   2

numpy中的等价物是:

In [161]: ain=np.arange(2*3*4).reshape(2,3,4)
In [162]: idx=np.ix_([0,1],[0,1,2],[1,2,3,0])
In [163]: idx
Out[163]: 
(array([[[0]],

        [[1]]]), array([[[0],
         [1],
         [2]]]), array([[[1, 2, 3, 0]]]))
In [164]: ain[idx]
Out[164]: 
array([[[ 1,  2,  3,  0],
        [ 5,  6,  7,  4],
        [ 9, 10, 11,  8]],

       [[13, 14, 15, 12],
        [17, 18, 19, 16],
        [21, 22, 23, 20]]])

除了基于 0 的索引之外,我还使用 np.ix_ 来重塑索引。 MATLAB 和 numpy 使用不同的语法来索引值块。

下一步是用代码构造 [0,1],[0,1,2],[1,2,3,0],直接翻译。

我可以使用 np.r_ 作为将 2 个切片转换为索引数组的快捷方式:

In [201]: idx=[]
In [202]: for nx in ain.shape:
    kx = int(np.floor(nx/2.))
    kx = kx-1;
    idx.append(np.r_[kx:nx, 0:kx])
   .....:     
In [203]: idx
Out[203]: [array([0, 1]), array([0, 1, 2]), array([1, 2, 3, 0])]

并通过 np.ix_ 传递它以生成适当的索引元组:

In [204]: ain[np.ix_(*idx)]
Out[204]: 
array([[[ 1,  2,  3,  0],
        [ 5,  6,  7,  4],
        [ 9, 10, 11,  8]],

       [[13, 14, 15, 12],
        [17, 18, 19, 16],
        [21, 22, 23, 20]]])

在这种情况下,2 个维度不滚动任何东西,slice(None) 可以替换那些:

In [210]: idx=(slice(None),slice(None),[1,2,3,0])
In [211]: ain[idx]

======================

np.roll 是:

indexes = concatenate((arange(n - shift, n), arange(n - shift)))
res = a.take(indexes, axis)

np.apply_along_axis是另一个构造索引数组的函数(并将其变成元组进行索引)。

上面的人提供了多种合适的解决方案。为了完整起见,这是我的最终解决方案。在这个 3 维情况下的玩具示例中,函数 'ops' 将向量的第一个和最后一个元素替换为 1.

import numpy as np

def ops(s):
    s[0]=1
    s[-1]=1
    return s

a = np.random.rand(4,4,3)
print '------'
print 'Array a'
print a
print '------'
for ii in np.arange(a.ndim):
    a = np.apply_along_axis(ops,ii,a)
    print '------'
    print ' Axis',str(ii)
    print a
    print '------'
    print ' '

生成的 3D 数组在 'border' 上的每个元素中都有一个 1,数组中间的数字不变。这当然是一个玩具示例;但是 ops 可以是对一维向量进行运算的任意函数。

扁平化矢量也可以;我之所以选择不这样做,仅仅是因为记账比较困难,而且 apply_along_axis 是最简单的方法。

apply_along_axis reference page