使用布尔值或序列对象获取numpy数组的视图(高级索引)

时间:2019-02-08 13:42:54

标签: python arrays numpy

如何通过布尔值或整数元组作为索引返回numpy数组的视图(而不是副本)?

The trouble is this typically returns a copy:

  

当选择对象obj是一个   非元组序列对象,ndarray(数据类型为整数或布尔值),   或具有至少一个序列对象或ndarray(数据类型)的元组   整数或布尔值)。有两种类型的高级索引:整数   和布尔值。

     

高级索引总是返回数据的副本(与   返回视图的基本切片)。

我这样做的动机是节省内存。这是问题的快速示例:

import numpy as np

big_number = 10
x = np.ones((big_number, big_number, big_number))

#
sub_array = np.s_[(1, 2, 3, 5, 7), :, :]
y = x[sub_array]
print(y.flags['OWNDATA'])
  

通常,索引的元组(1、2、3、5、7)没有任何结构,因此我很困惑如何将其调整为基本numpy索引所需的常规步幅< / p>

2 个答案:

答案 0 :(得分:0)

NumPy中的视图基于查看内存中的相同数据,只是不同起点和“步幅”的某种混合,每个维度都必须遍历。 (步长告诉我们在每个维度上将索引增加1时需要在数组中移动的字节数。)

如果给定的原始数组可以用这种方式表示,那么将其构造为原始数组的视图应该足够容易。例如,在您的评论中,您提到了改组轴的顺序;那只是对np.transpose的调用,应该可以给您一个视图。通常来说,花式索引不会为您提供正确形式的子数组,这就是NumPy不从中返回视图的原因。 (“智能”不足以识别那些可能出现视图的特殊情况-您必须手动执行。)

一些例子:

In [1]: import numpy as np
   ...: x = np.empty((20,30,5))
   ...: x.strides

Out[1]: (1200, 40, 8)

In [2]: y = x.transpose((1,2,0))
   ...: y.strides

Out[2]: (40, 8, 1200)

In [3]: y.flags['OWNDATA']
Out[3]: False

In [4]: z = x[12:1:-2, 1:25:4, :]
   ...: z.strides

Out[4]: (-2400, 160, 8)

In [5]: z.flags['OWNDATA']
Out[5]: False

使用xtranspose的轴进行置换以获得y只是对步幅进行置换。获得z的相当复杂的标准索引也改变了跨度(第一个乘以-2,第二个乘以4,因为这些是步骤)。

我们可以看到yz都是视图,因为OWNDATA标志为False。

答案 1 :(得分:0)

一个可视化两个数组是否可以共享内存的方法是查看它们的“行”

In [422]: x = np.arange(24).reshape((4,3,2))
In [423]: x
Out[423]: 
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]],

       [[12, 13],
        [14, 15],
        [16, 17]],

       [[18, 19],
        [20, 21],
        [22, 23]]])
In [424]: y = x[[1,3,0,2],:,:]  # rearrange the 1st axis
In [425]: y
Out[425]: 
array([[[ 6,  7],
        [ 8,  9],
        [10, 11]],

       [[18, 19],
        [20, 21],
        [22, 23]],

       [[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[12, 13],
        [14, 15],
        [16, 17]]])

In [428]: x.ravel(order='K')
Out[428]: 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
In [429]: y.ravel(order='K')
Out[429]: 
array([ 6,  7,  8,  9, 10, 11, 18, 19, 20, 21, 22, 23,  0,  1,  2,  3,  4,
        5, 12, 13, 14, 15, 16, 17])

请注意y中的元素如何以不同的顺序出现。我们根本无法跨越x来获得y

在没有order参数的情况下,ravel使用'C',当新数组进行某种轴转置时,它会使我们感到困惑。如另一个答案所述,x.T是通过重新排列轴并因此改变步幅来实现的视图。

在[430]中:x.T.ravel()#逐行查看转置的数组 出[430]: array([0,6,12,18,2,8,14,20,4,10,16,22,1,7,13,19,3,         9、15、21、5、11、17、23]) 在[431]中:x.T.ravel(order ='K')#逐列查看转置的数组 出[431]: array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,        17、18、19、20、21、22、23])

__array_interface__是查看数组底层结构的便捷工具:

In [432]: x.__array_interface__
Out[432]: 
{'data': (45848336, False),
 'strides': None,
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (4, 3, 2),
 'version': 3}
In [433]: y.__array_interface__
Out[433]: 
{'data': (45892944, False),
 'strides': None,
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (4, 3, 2),
 'version': 3}
In [434]: x.T.__array_interface__
Out[434]: 
{'data': (45848336, False),     # same as for x
 'strides': (8, 16, 48),        # reordered strides
 'descr': [('', '<i8')],
 'typestr': '<i8',
 'shape': (2, 3, 4),
 'version': 3}