索引一个numpy阵列行



说我有一个numpy数组:

>>> X = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
>>> X
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

和我想为每一行选择的一系列索引:

>>> ixs = np.array([[1, 3], [0, 1], [1, 2]])
>>> ixs
array([[1, 3],
       [0, 1],
       [1, 2]])

如何索引数组x,以便在X中的每一行我选择ixs中指定的两个索引?

因此,对于这种情况,我想为第一行选择元素1和3,第二行的元素0和1,依此类推。输出应为:

array([[2, 4],
       [5, 6],
       [10, 11]])

缓慢的解决方案将是这样的:

output = np.array([row[ix] for row, ix in zip(X, ixs)])

但是,这可能会在非常长的阵列中变得有点慢。有没有使用numpy的循环的速度更快的方法?

编辑:在2.5k * 1M阵列上具有2k宽IX(10GB(的一些非常近似的速度测试:

np.array([row[ix] for row, ix in zip(X, ixs)]) 0.16S

X[np.arange(len(ixs)), ixs.T].T 0.175S

X.take(idx+np.arange(0, X.shape[0]*X.shape[1], X.shape[1])[:,None]) 33s

np.fromiter((X[i, j] for i, row in enumerate(ixs) for j in row), dtype=X.dtype).reshape(ixs.shape) 2.4s

您可以使用以下方式:

X[np.arange(len(ixs)), ixs.T].T

这是复杂索引的参考。

我相信您可以这样使用.take

In [185]: X
Out[185]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
In [186]: idx
Out[186]:
array([[1, 3],
       [0, 1],
       [1, 2]])
In [187]: X.take(idx + (np.arange(X.shape[0]) * X.shape[1]).reshape(-1, 1))
Out[187]:
array([[ 2,  4],
       [ 5,  6],
       [10, 11]])

如果您的数组尺寸庞大,它可能会更快,尽管丑陋,但要做:

idx+np.arange(0, X.shape[0]*X.shape[1], X.shape[1])[:,None]

只是为了娱乐,请查看以下表现:

np.fromiter((X[i, j] for i, row in enumerate(ixs) for j in row), dtype=X.dtype, count=ixs.size).reshape(ixs.shape)

编辑以添加时间

In [15]: X = np.arange(1000*10000, dtype=np.int32).reshape(1000,-1)
In [16]: ixs = np.random.randint(0, 10000, (1000, 2))
In [17]: ixs.sort(axis=1)
In [18]: ixs
Out[18]:
array([[2738, 3511],
       [3600, 7414],
       [7426, 9851],
       ...,
       [1654, 8252],
       [2194, 8200],
       [5497, 8900]])
In [19]: %timeit  np.array([row[ix] for row, ix in zip(X, ixs)])
928 µs ± 23.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [20]: %timeit X[np.arange(len(ixs)), ixs.T].T
23.6 µs ± 491 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [21]: %timeit X.take(idx+np.arange(0, X.shape[0]*X.shape[1], X.shape[1])[:,None])
20.6 µs ± 530 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [22]: %timeit np.fromiter((X[i, j] for i, row in enumerate(ixs) for j in row), dtype=X.dtype, count=ixs.size).reshape(ixs.shape)
1.42 ms ± 9.94 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

@mxbi我添加了一些时间,我的结果与您的结果并不一致,您应该检查

这是一个较大的数组:

In [33]: X = np.arange(10000*100000, dtype=np.int32).reshape(10000,-1)
In [34]: ixs = np.random.randint(0, 100000, (10000, 2))
In [35]: ixs.sort(axis=1)
In [36]: X.shape
Out[36]: (10000, 100000)
In [37]: ixs.shape
Out[37]: (10000, 2)

有一些结果:

In [42]: %timeit  np.array([row[ix] for row, ix in zip(X, ixs)])
11.4 ms ± 177 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [43]: %timeit X[np.arange(len(ixs)), ixs.T].T
596 µs ± 17.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [44]: %timeit X.take(ixs+np.arange(0, X.shape[0]*X.shape[1], X.shape[1])[:,None])
540 µs ± 16.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

现在,我们使用的是第500列索引而不是两个索引,我们看到列表的理解开始获胜:

In [45]: ixs = np.random.randint(0, 100000, (10000, 500))
In [46]: ixs.sort(axis=1)
In [47]: %timeit  np.array([row[ix] for row, ix in zip(X, ixs)])
93 ms ± 1.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [48]: %timeit X[np.arange(len(ixs)), ixs.T].T
133 ms ± 638 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [49]: %timeit X.take(ixs+np.arange(0, X.shape[0]*X.shape[1], X.shape[1])[:,None])
87.5 ms ± 1.13 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

从行索引项目的通常建议是:

X[np.arange(X.shape[0])[:,None], ixs]

也就是说,制作形状的行索引(n,1((列向量(,它将用ixs的(n,m(形状广播以给出(n,m(形状以给出(n,m(解决方案。

基本上与:

X[np.arange(len(ixs)), ixs.T].T

对A(M,N(和转置广播A(N,(索引。

时间基本相同:

In [299]: X = np.ones((1000,2000))
In [300]: ixs = np.random.randint(0,2000,(1000,200))
In [301]: timeit X[np.arange(len(ixs)), ixs.T].T
6.58 ms ± 71.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [302]: timeit X[np.arange(X.shape[0])[:,None], ixs]
6.57 ms ± 129 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

进行比较:

In [307]: timeit np.array([row[ix] for row, ix in zip(X, ixs)])
6.63 ms ± 229 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

我有点惊讶此列表理解表现很好。我想知道相对优势如何比较何时变化,尤其是在Xixs的相对形状(长,宽等(的相对形状中。


第一个解决方案是ix_产生的索引样式:

In [303]: np.ix_(np.arange(3), np.arange(2))
Out[303]: 
(array([[0],
        [1],
        [2]]), array([[0, 1]]))

这应该有效

[X[i][[y]] for i, y in enumerate(ixs)] 

编辑:我只是注意到您不想循环解决方案。

最新更新