Numpy:转置高级索引的结果

时间:2014-12-28 18:17:12

标签: python performance numpy memory-layout

>>> import numpy as np
>>> X = np.arange(27).reshape(3, 3, 3)
>>> x = [0, 1]
>>> X[x, x, :]
array([[ 0,  1,  2],
       [12, 13, 14]])

我需要将其与0维度相加,但在现实世界中,矩阵是巨大的,我宁愿将其与-1维度相加,这由于内存布局而更快。因此,我希望将结果转换为:

array([[ 0, 12],
       [ 1, 13],
       [ 2, 14]])

我该怎么做?我希望numpy的“高级索引”的结果是隐式转置。

Update1 :在现实世界中,高级索引是不可避免的,并且不保证下标是相同的。

.T

Update2 :为了澄清这不是XY problem,,这是实际问题:

我有一个大矩阵>>> x = [0, 0, 1] >>> y = [0, 1, 1] >>> X[x, y, :] array([[ 0, 1, 2], [ 3, 4, 5], [12, 13, 14]]) ,它包含来自某个概率分布的元素X。元素的概率分布取决于元素的邻域。这个分布是未知的,所以我遵循Gibbs sampling过程来构建一个矩阵,该矩阵包含来自此分布的元素。简而言之,这意味着我对矩阵x做了一些初步猜测,然后我继续迭代矩阵X的元素,用一个取决于相邻值的公式更新每个元素X x。因此,对于矩阵的任何元素,我需要获取它的邻居(高级索引)并对它们执行一些操作(在我的示例中求和)。我使用x来查看代码中占用大部分时间的行是使用与line_profiler而不是0相关的数组之和。因此,我想知道是否有一种方法可以通过高级索引生成已经转置的矩阵。

1 个答案:

答案 0 :(得分:3)

  

我想在0维度上总结它,但在现实世界中,矩阵是巨大的,我更倾向于将它与-1维相加,这由于内存布局而更快。

我不完全确定你的意思。如果底层数组是行主要(默认值,即X.flags.c_contiguous == True),那么它可能稍微更快,以便沿 0th 维度求和。简单地使用.Tnp.transpose()转换数组本身并不会改变数组在内存中的布局方式。

例如:

# X is row-major
print(X.flags.c_contiguous)
# True

# Y is just a transposed view of X
Y = X.T

# the indices of the elements in Y are transposed, but their layout in memory
# is the same as in X, therefore Y is column-major rather than row-major
print(Y.flags.c_contiguous)
# False

您可以从row-major转换为column-major,例如使用np.asfortranarray(X),但如果没有在内存中创建X的完整副本,则无法执行此转换。除非您要在X列上执行大量操作,否则几乎肯定不值得进行转换。

如果要将总和的结果存储在列主数组中,可以使用out= kwarg到X.sum(),例如:

result = np.empty((3, 3), order='F') # Fortran-order, i.e. column-major
X.sum(0, out=result)

在你的情况下,行与列之间的求和之间的差异可能是非常最小 - 但是 - 因为你已经要在X索引非相邻元素,你将会已经失去了spatial locality of reference的好处,通常会在行上略微加快。

例如:

X = np.random.randn(100, 100, 100)

# summing over whole rows is slightly faster than summing over whole columns
%timeit X.sum(0)
# 1000 loops, best of 3: 438 µs per loop
%timeit X.T.sum(0)
# 1000 loops, best of 3: 486 µs per loop

# however, the locality advantage disappears when you are addressing
# non-adjacent elements using fancy indexing
%timeit X[[0, 0, 1], [0, 1, 1], :].sum()
# 100000 loops, best of 3: 4.72 µs per loop
%timeit X.T[[0, 0, 1], [0, 1, 1], :].sum()
# 100000 loops, best of 3: 4.63 µs per loop

更新

@senderle 在评论中提到使用numpy v1.6.2他看到了相反的时间顺序,即X.sum(-1)X.sum(0)更快一行 - 主阵列。这似乎与他正在使用的numpy版本有关 - 使用v1.6.2我可以重现他观察到的顺序,但是使用两个较新的版本(v1.8.2和1.10.0.dev-8bcb756)我观察到相反的情况(即X.sum(0)X.sum(-1)快一点。无论哪种方式,我都不认为更改阵列的内存顺序可能对OP的情况有很大帮助。