为什么布尔索引结构化数组的赋值取决于索引排序



我最近在处理结构化numpy数组时看到了一种毫无意义的现象。我希望有人能帮助我理解发生了什么。我提供了一个简单的例子来说明这个问题。问题是:

当用布尔掩码对结构化numpy数组进行索引时,这是有效的:

arr['fieldName'][boolMask] += val

但以下情况并非如此:

arr[boolMask]['fieldName'] += val

下面是一个最小的工作示例:

import numpy as np
myDtype = np.dtype([('t','<f8'),('p','<f8',(3,)),('v','<f4',(3,))])
nominalArray = np.zeros((10,),dtype=myDtype)
nominalArray['t'] = np.arange(10.)
# In real life, the other fields would also be populated
print "original times: {0}".format(nominalArray['t'])
# Add 10 to all times greater than 5
timeGreaterThan5 = nominalArray['t'] > 5
nominalArray['t'][timeGreaterThan5] += 10.
print "times after first operation: {0}".format(nominalArray['t'])
# Return those times to their original values
nominalArray[timeGreaterThan5]['t'] -= 10.
print "times after second operation: {0}".format(nominalArray['t'])

运行此操作会产生以下输出:

original times: [ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]
times after first operation: [  0.   1.   2.   3.   4.   5.  16.  17.  18.  19.]
times after second operation: [  0.   1.   2.   3.   4.   5.  16.  17.  18.  19.]

我们在这里清楚地看到,第二次手术没有效果。如果有人能解释为什么会发生这种情况,我们将不胜感激。

这确实是复制v视图的问题。但我会详细介绍。

视图与副本之间的关键区别在于索引模式是否规则。正则表达式可以用数组shapestridesdtype来表示。通常,布尔索引(以及相关的索引列表(不能用这些术语表示,因此numpy必须返回一个副本。

我喜欢看arr.__array_interface__属性。它显示形状、步幅和指向数据缓冲区的指针。如果指针与原始指针相同,则它是view

使用arr[idx] += 1,索引实际上是一种setitem方法,选择要通过添加来修改的数据缓冲区项。视图和副本之间的区别不适用。

但对于arr[idx1][idx2] += 1,第一个索引是getitem方法。因此,视图和副本之间的区别很重要。第二个索引修改由第一个生成的数组。如果是视图,则修改会影响原始数据;如果是复制品,就不会发生永久性的事情。副本可能会被修改,但它会随着垃圾收集关闭而消失。

使用2d数组,可以将这两个索引步骤arr[idx1, idx2] += 1组合起来;事实上,这是首选语法。

对于结构化数组,字段索引与列索引类似,但不完全相同。首先,它不能和元素索引结合在一起。

一个简单的结构化阵列:

In [234]: arr=np.ones((5,),dtype='i,f,i,f')
In [235]: arr.__array_interface__
{'strides': None,
 'shape': (5,),
 'data': (152524816, False),
 'descr': [('f0', '<i4'), ('f1', '<f4'), ('f2', '<i4'), ('f3', '<f4')],
 'typestr': '|V16',
 'version': 3}

选择一个字段产生一个视图相同的数据指针

In [236]: arr['f0'].__array_interface__['data']
Out[236]: (152524816, False)

用布尔值选择元素会生成一个副本(diff指针(

In [242]: idx = np.array([1,0,0,1,1],bool)
In [243]: arr[idx].__array_interface__['data']
Out[243]: (152629520, False)

因此arr['f0'][idx] += 1修改f0字段中的选定项目。

arr[idx]['f0'] += 1修改副本的f0字段,对arr没有影响。

arr[idx]['f0'] + 1arr['f0'][idx] + 1显示的内容相同,但它们并不试图执行任何就地更改。

您可以从结构化数组arr[['f0','f2']]中选择多个字段。但这是一个副本。(我收到一个警告,建议我做一个明确的副本(。

最新更新