将numpy数组转换为C连续顺序的最便宜方法是什么?

26
以下代码产生一个 C-contiguous 的 numpy 数组:
import numpy

a = numpy.ones((1024,1024,5))

现在如果我对其进行切片,结果可能不再相同。例如:
bn = a[:, :, n]

从0到4循环n。 我的问题是我需要bn是C连续的,并且我需要对许多a实例执行此操作。我只需要每个bn一次,并希望避免这样做。
bn  = bn.copy(order='C')

我不想重写我的代码,以至于:
a = numpy.ones((5,1024,1024))

有没有比复制更快、更便宜的方法来获取bn

背景:

我想对每个a的每个片段进行哈希处理,使用:

import hashlib

hashlib.sha1(a[:, :, n]).hexdigest()

不幸的是,这会引发一个ValueError,抱怨顺序错误。所以如果有另一种快速获取我想要的哈希的方法,我也会使用它。


1
顺便提一下,我只是通过大量的调试才了解了hashlib的行为,直到我找到那个异常真正来自哪里。Python文档不应该提到这一点吗? - Daniel Sk
3个回答

22

当使用numpy与C进行接口时,这是一种标准操作。请查看numpy.ascontiguousarray

x=numpy.ascontiguousarray(x)

是正确的处理方式。

如果需要Fortran顺序,请使用numpy.asfortranarray

如前所述,必要时该函数会执行复制。因此没有其他方法。您可以在操作之前尝试rollaxis,使较短的轴成为第一个轴。这将为您提供有关数组的视图。

In [2]: A=np.random.rand(1024,1024,5)
In [3]: B=np.rollaxis(A,2)
In [4]: B.shape
Out[4]: (5, 1024, 1024)
In [5]: B.flags
Out[5]:
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

In [6]: A.flags
Out[6]:
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

因此,rollaxis也无法解决这个问题。


12
目前情况下,任何试图强制将切片“bn”转换为C连续顺序的尝试都会创建一个副本。如果您不想改变您开始使用的形状(并且不需要a本身在C顺序中),一个可能的解决方案是以Fortran顺序开始使用数组“a”。
>>> a = numpy.ones((1024, 1024, 5), order='f')

切片也是F连续的:

>>> bn = a[:, :, 0]
>>> bn.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : False
  ...

这意味着切片 bn 的转置将是按C顺序排列的,并且转置不会创建副本:

这意味着切片bn的转置将按照C顺序进行排序,而转置不会创建任何副本:

>>> bn.T.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : False
  ...

然后您可以对该切片进行哈希处理:

>>> hashlib.sha1(bn.T).hexdigest()
'01dfa447dafe16b9a2972ce05c79410e6a96840e'

6
在我看来,这似乎是解决问题的正确路径,但是当转置视图时,您改变了另外两个轴的顺序,这并不好。例如,类似于a = numpy.ones((5, 1024, 1024)).transpose(1, 2, 0)的代码会产生一个数组,它既不是C连续的也不是Fortran连续的,但是当沿着最后一个维度索引时,可以产生C连续的片段。 - Jaime

10
为了将一个numpy数组x强制转换为C连续的,而不会在已经是C连续时做出不必要的复制,您应该使用:
```python x = np.ascontiguousarray(x) ```
 x = numpy.asarray(x, order='C')
请注意,如果该数组不是按照 C 风格连续的,那么它在效率上可能与 x.copy(order='C') 相似。我认为没有绕过它的方法。除非将数据复制到新位置以重新组织内存中数组的对齐方式。
重写您的代码,使其首先使用切片索引,例如 numpy.ones((5,1024,1024)),似乎是优化的唯一合理方法。

我怎样才能知道“一开始就是那样的”?如果是这样,我不会收到错误提示吧?我的情况是使用.values从pandas中取出一列。为什么它可能或者不可能是C-contiguous的呢? - Gulzar
例如,您创建了一个2D numpy数组(默认为C顺序),您对其进行转置,它将变为F顺序。因此,它取决于对数组执行的操作。使用pandas,也许块管理器可能与此有关https://uwekorn.com/2020/05/24/the-one-pandas-internal.html无论如何,您可以通过x.flags进行检查。 - rth

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