【问题标题】:Cheapest way to get a numpy array into C-contiguous order?将 numpy 数组转换为 C 连续顺序的最便宜方法?
【发布时间】:2015-07-08 23:16:07
【问题描述】:

以下生成一个 C 连续的 numpy 数组:

import numpy

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

现在如果我切片它,结果可能不再相同。例如:

bn = a[:, :, n]

n 从 0 到 4。 我的问题是我需要 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,抱怨订单。因此,如果有另一种快速获取我想要的哈希的方法,我也会使用它。

【问题讨论】:

  • 在相关说明中,我只是通过大量调试了解了 hashlib 的行为,直到我发现异常真正来自何处。 python 文档不应该提到这一点吗?

标签: python arrays numpy


【解决方案1】:

要强制一个 numpy 数组 x 是 C 连续的,而不是在开始时进行不必要的复制,您应该使用,

 x = numpy.asarray(x, order='C')

注意,如果这个数组不是 C 连续的,它在效率方面可能与x.copy(order='C') 相似。我认为没有办法解决它。除了将数据复制到新位置之外,您无法重新组织内存中数组的对齐方式。

重写您的代码,使其首先使用切片索引,如numpy.ones((5,1024,1024)) 似乎是优化这一点的唯一合理方法。

【讨论】:

  • 如何知道“一开始就这样”?如果是这样,我不会只是没有得到错误吗?我的案例是使用 .values 从 pandas 中获取一个专栏。为什么会或不会是 C 连续的?
  • 例如,您创建一个 2D numpy 数组(默认情况下 C 排序),您转置它,它将是 F 排序。所以这取决于对数组所做的操作。有了 pandas,块管理器也许可以处理它uwekorn.com/2020/05/24/the-one-pandas-internal.html 在任何情况下,您都可以通过x.flags 进行检查。
【解决方案2】:

这是 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也没有解决这个问题。

【讨论】:

    【解决方案3】:

    就目前情况而言,任何将切片bn 强制为 C 连续顺序的尝试都会创建一个副本。

    如果您不想更改开始使用的形状(并且不需要按 C 顺序排列 a 本身),一种可能的解决方案是按 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.T.flags
      C_CONTIGUOUS : True
      F_CONTIGUOUS : False
      OWNDATA : False
      ...
    

    然后你可以散列切片:

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

    【讨论】:

    • 在我看来,这似乎是解决方案的正确路径,但是在转置视图时您正在更改其他两个轴的顺序,这并不好。像 a = numpy.ones((5, 1024, 1024)).transpose(1, 2, 0) 这样的东西给你一个既不是 C 也不是 Fortran 连续的数组,但是当沿着最后一个维度索引时会产生 C 连续的切片。
    猜你喜欢
    • 2020-12-21
    • 2022-12-07
    • 1970-01-01
    • 2015-06-20
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多