为什么对布尔索引结构化数组的赋值取决于索引排序?
Why does an assignment to a boolean indexed structured array depend on index ordering?
我最近在使用结构化 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.]
我们在这里清楚地看到,第二次操作没有效果。如果有人能解释为什么会发生这种情况,将不胜感激。
这确实是copy v view的问题。但我会详细介绍。
视图与副本之间的主要区别在于 - 索引模式是否规则。一个正则可以用数组shape
、strides
和dtype
来表示。通常,布尔索引(以及相关的索引列表)不能用这些术语表示,因此 numpy
必须 return 一个副本。
我喜欢看arr.__array_interface__
属性。它显示形状、步幅和指向数据缓冲区的指针。如果指针与原来相同,则为view
.
用arr[idx] += 1
,索引实际上是一个setitem
方法,select修改哪些数据缓冲项用加法。查看和复制之间的区别不适用。
但是对于arr[idx1][idx2] += 1
,第一个索引是getitem
方法。为此,视图和副本之间的区别很重要。第二个索引修改第一个索引生成的数组。如果是视图,修改会影响原始数据;如果是副本,则不会发生任何永久性的事情。该副本可能会被修改,但它会在垃圾回收通道中消失。
对于二维数组,您可以组合这两个索引步骤,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)
使用布尔值选择元素会产生一个副本(差异指针)
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
字段修改 selected 项。
arr[idx]['f0'] += 1
修改副本的f0
字段,对arr
.
没有影响
arr[idx]['f0'] + 1
和 arr['f0'][idx] + 1
显示相同的内容,但它们并未尝试执行任何就地更改。
您可以 select 来自结构化数组的多个字段,arr[['f0','f2']]
。但这是一个副本。 (并且我收到一条警告,建议我进行显式复制)。
我最近在使用结构化 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.]
我们在这里清楚地看到,第二次操作没有效果。如果有人能解释为什么会发生这种情况,将不胜感激。
这确实是copy v view的问题。但我会详细介绍。
视图与副本之间的主要区别在于 - 索引模式是否规则。一个正则可以用数组shape
、strides
和dtype
来表示。通常,布尔索引(以及相关的索引列表)不能用这些术语表示,因此 numpy
必须 return 一个副本。
我喜欢看arr.__array_interface__
属性。它显示形状、步幅和指向数据缓冲区的指针。如果指针与原来相同,则为view
.
用arr[idx] += 1
,索引实际上是一个setitem
方法,select修改哪些数据缓冲项用加法。查看和复制之间的区别不适用。
但是对于arr[idx1][idx2] += 1
,第一个索引是getitem
方法。为此,视图和副本之间的区别很重要。第二个索引修改第一个索引生成的数组。如果是视图,修改会影响原始数据;如果是副本,则不会发生任何永久性的事情。该副本可能会被修改,但它会在垃圾回收通道中消失。
对于二维数组,您可以组合这两个索引步骤,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)
使用布尔值选择元素会产生一个副本(差异指针)
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
字段修改 selected 项。
arr[idx]['f0'] += 1
修改副本的f0
字段,对arr
.
arr[idx]['f0'] + 1
和 arr['f0'][idx] + 1
显示相同的内容,但它们并未尝试执行任何就地更改。
您可以 select 来自结构化数组的多个字段,arr[['f0','f2']]
。但这是一个副本。 (并且我收到一条警告,建议我进行显式复制)。