如何制作numpy数组的精确副本(包括步幅)

时间:2015-12-08 06:24:42

标签: python numpy

如何复制numpy数组,使其与原始数组具有相同的内存布局,包括步幅和任何相关的不连续性?基本上,新的__array_interface__应该是相同的,除了指针。

理由:我需要针对不连续的数组测试我的代码,我需要运行多个破坏性测试,因此需要一个保留不连续性的副本。

示例(请注意,复制会更改步幅):

>>> import numpy as np
>>> a = np.empty((10, 6))
>>> b = a[::2]
>>> b.strides
(96, 8)
>>> b.copy(order='K').strides
(48, 8)

修改

以下是我根据senderle的答案使用的完整功能:

def exact_copy(data):
    if data.base is None:
        return data.copy()        
    base = data.base.copy()
    offset = (data.__array_interface__['data'][0] -
              data.base.__array_interface__['data'][0])
    return np.ndarray(buffer=base.data, shape=data.shape, strides=data.strides,
                      offset=offset, dtype=data.dtype)

1 个答案:

答案 0 :(得分:0)

基本上,你可以这样做,但在大多数情况下你可能不应该 - 效益不是那么大,numpy中的很多东西对于非连续数据来说更难。

但是,做你想做的事并不难 - 你可以使用strides公开的ndarray参数。假设我们从一个里面有一些美味苹果的阵列开始,但还有很多其他的东西。我们如何把苹果拿出来?

>>> xapplesx = numpy.array(list('xxxaxpxpxlxexsxxx'))
>>> xapplesx
array(['x', 'x', 'x', 'a', 'x', 'p', 'x', 'p', 'x', 'l', 'x', 'e', 'x',
       's', 'x', 'x', 'x'], 
      dtype='|S1')

我们希望从索引3开始,我们希望获取所有其他值并将其放在一维视图中,这意味着我们希望获取每个2 nd值。

所以我们通过了offset=3strides=(2,)

>>> apples = numpy.ndarray(shape=(6,), dtype='|S1', 
                           buffer=xapplesx, offset=3, strides=(2,))
>>> apples
array(['a', 'p', 'p', 'l', 'e', 's'], 
      dtype='|S1')

习惯于以这种方式直接使用strides需要仔细考虑。假设我们想要获得我们的苹果并将它们放在x以上。现在我们想要一个二维数组,我们必须为两个维度指定步幅。但我们希望第二行数据包含苹果之后的值。因此,我们为该维度指定了1的步幅:

>>> applesoverx = numpy.ndarray(shape=(2, 6), dtype='|S1', 
                                buffer=xapplesx, offset=3, strides=(1, 2))
>>> applesoverx
array([['a', 'p', 'p', 'l', 'e', 's'],
       ['x', 'x', 'x', 'x', 'x', 'x']], 
      dtype='|S1')

如果您在获取正确的buffer数据时遇到问题,可以执行以下操作:

>>> a = np.arange(12).reshape(4,3)[::-1,:]
>>> x = np.ndarray(shape=a.shape, buffer=a.base.data, 
                   strides=a.strides, offset=72, dtype=a.dtype)

唯一的技巧是你必须计算出正确的偏移量。结果是(我很确定)一个完美的克隆:

>>> x
array([[ 9, 10, 11],
       [ 6,  7,  8],
       [ 3,  4,  5],
       [ 0,  1,  2]])
>>> a
array([[ 9, 10, 11],
       [ 6,  7,  8],
       [ 3,  4,  5],
       [ 0,  1,  2]])
>>> x.shape
(4, 3)
>>> a.shape
(4, 3)
>>> x.strides
(-24, 8)
>>> a.strides
(-24, 8)
>>> id(x.base)
140602344736320
>>> id(a.base)
140602344736320

如果要复制原始数据,可以使用a.base.copy().data作为buffer参数。并且(正如Luke指出的那样),你可以用这个确定偏移量(见上文):

a.__array_interface__['data'][0] - a.base.__array_interface__['data'][0]

这为您提供了对给定数组缓冲区开始的指针的引用,如文档here所述。