Python 既然在结构化数组的一维切片中添加了一个新字段,为什么不能将新字段的条目设置为列表?
标题可能有点混乱,所以我希望通过一个例子可以让它更清楚。Image我有一个小助手函数,它可以将新字段添加到现有的结构化数组中:Python 既然在结构化数组的一维切片中添加了一个新字段,为什么不能将新字段的条目设置为列表?,python,list,numpy,field,structured-array,Python,List,Numpy,Field,Structured Array,标题可能有点混乱,所以我希望通过一个例子可以让它更清楚。Image我有一个小助手函数,它可以将新字段添加到现有的结构化数组中: import numpy as np def add_field(a, *descr): b = np.empty(a.shape, dtype=a.dtype.descr + [*descr]) for name in a.dtype.names: b[name] = a[name] return b 给定一个结构化数组,
import numpy as np
def add_field(a, *descr):
b = np.empty(a.shape, dtype=a.dtype.descr + [*descr])
for name in a.dtype.names:
b[name] = a[name]
return b
给定一个结构化数组,我可以简单地使用它添加新字段:
a = np.array(
[(1, False), (2, False), (3, False), (4, True)],
dtype=[('id', 'i4'), ('used', '?')]
)
print(a)
b = add_field(a, ('new', 'O'))
print(b)
然后,我可以将新创建字段的条目设置为(空)列表,而不会出现问题:
b[0]['new'] = []
我还可以创建一个新数组,它只是原始数组的一部分,然后向这个新数组添加一个新字段:
c = a[0]
print(c)
d = add_field(c, ('newer', 'O'))
print(d)
但是,如果我现在尝试将新字段设置为(空)列表,它将不起作用:
d['newer'] = []
ValueError: assignment to 0-d array
为什么呢?根据add_field
,d
是一个全新的数组,恰好与b
一样共享相同的字段和条目。有趣的是,b[0]
的形状是()
,而d
的形状是(1,)
(而且类型(b)
是np.void
,而类型(d)
是np.array
)。也许这与此有关?同样有趣的是,所有这些工作:
d['newer'] = 1.34
d['newer'] = False
d['newer'] = None
d['newer'] = add_field
d['newer'] = set()
d['newer'] = {}
d['newer'] = {'test': []}
但是,使用键“test”
访问上一个命令中的vaues不会:
>>> d['newer'] = {'test': []}
>>> d['newer']
>>> array({'test': []}, dtype=object)
>>> d['newer']['test']
>>> IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
>>> d['newer'][0]
>>> IndexError: too many indices for array
这很令人困惑
编辑
好的,我刚刚尝试修改add_字段
函数,如下所示:
def add_field(a, *descr):
shape = a.shape if len(a.shape) else (1,)
b = np.empty(shape, dtype=a.dtype.descr + [*descr])
for name in a.dtype.names:
b[name] = a[name]
return b
但这没有帮助:
>>> d = add_field(a[0], ('newer', 'O'))
>>> d
>>> array([(1, False, None)], dtype=[('id', '<i4'), ('used', '?'), ('test', 'O')])
>>> d.shape
>>> (1,)
>>> d['newer'] = []
>>> ValueError: cannot copy sequence with size 0 to array axis with dimension 1
但我不喜欢这种变通方法。我希望它的工作原理与b[0]
相同
编辑2
如果我进一步修改add_字段
函数,我可以强制执行想要的行为,尽管我不是100%喜欢它:
def add_field(a, *descr):
shape = a.shape if len(a.shape) else (1,)
b = np.empty(shape, dtype=a.dtype.descr + [*descr])
for name in a.dtype.names:
b[name] = a[name]
return b if len(a.shape) else b[0]
d = add_field(a[0], ('newer', 'O'))
d['newer'] = []
总结评论意见:
原始问题中的问题似乎是返回对象的形状-当您这样做时,例如
c=a[0]
如果a
具有形状(n,)
,则不是从数组中获取一个切片,而是从单个元素中获取<代码>c.shape
然后是()
。当您将形状数组()
传递到添加字段中时,则
b=np.empty(a.shape,dtype=a.dtype.descr+[*descr])
还将具有形状()
。但是,结构化数组必须具有shape(n,)
(尽管在中没有列出)
与问题的第一次编辑一样,正确的修改是
def add_字段(a,*descr):
shape=a.shape如果len(a.shape)else(1,)
b=np.empty(shape,dtype=a.dtype.descr+[*descr])
b[列表(a.dtype.names)]=a
返回b
然后,返回的对象将共享形状(n,)
结构化数组的属性,其中:
如果在整数位置对数组进行索引,则会得到一个结构(例如d[0]
)
您可以通过使用字段名(例如d['newer']
)编制索引来访问和修改结构化数组的各个字段
通过上述修改,问题中d
的行为与b
相同,例如
d[0]['newer']=[]
是有效的
b[0]['new']=[]
这就引出了问题的真正症结:
为什么我们不能使用d['newer']=[]
语法为字段的每个元素分配一个空列表?
使用此语法指定iterable而不是标量时,numpy会尝试按元素分配(或广播,具体取决于iterable)。这与标量赋值不同,标量赋值给该字段的每个元素。关于这一点,我们还不清楚,但是我们可以通过使用
b['new']=np.array([]
回溯(最近一次呼叫最后一次):
文件“structuredArray.py”,第20行,在
b['new']=np.array([]
ValueError:无法将输入数组从形状(0)广播到形状(4)
因此,这里的问题不是如何添加字段,而是如何尝试将空列表分配给该字段的每个元素。正确的方法是
b['new']=[[]*b.shape[0]]
对于(1,)
和(4,)
形状的结构化数组,其工作原理与预期相同:
将numpy导入为np
def add_字段(a,*descr):
shape=a.shape如果len(a.shape)else(1,)
b=np.empty(shape,dtype=a.dtype.descr+[*descr])
对于a.dtype.names中的名称:
b[姓名]=a[姓名]
返回b
a=np.array(
[(1,假)、(2,假)、(3,假)、(4,真)],
数据类型=[('id','i4'),('used','?')]
)
b=添加_字段(a,('new','O'))
b['new']=[[]*b.shape[0]]
印刷品(b)
c=a[0]
d=添加_字段(c,('newer','O'))
d['newer']=[[]*d.shape[0]]
印刷品(d)
[(1,False,list([])(2,False,list([]))(3,False,list([])(4,True,list([]))]
[(1,False,列表([])]
如果在结构化数组中占据较大的一部分,是否会发生同样的情况?例如,a[:2]
你的意思是如果c=a[:2]
?在这种情况下,它是有效的。我觉得它与形状有关,因为add_field
使用了输入数组的形状,并且由于某种原因a[0]
的形状是()
,尽管我猜它应该是(1,)
?当我测试它时d[0]['newer']=[/code>没有错误-在我看来d[0]
与该编辑器的行为类似b[0]
如果d.shape
是()
,0d,则只能对其进行索引d[()]
。对对象数据类型数组的列表分配可能会有广播问题哇,我甚至不知道[()]
是一个有效的索引!疯了d['newer'][()]=[]
实际上与原始添加字段版本一起工作。不过我还是很困惑。我只是想提一个我想出的小调整,可能会快一点。而不是for-yo循环
def add_field(a, *descr):
shape = a.shape if len(a.shape) else (1,)
b = np.empty(shape, dtype=a.dtype.descr + [*descr])
for name in a.dtype.names:
b[name] = a[name]
return b if len(a.shape) else b[0]
d = add_field(a[0], ('newer', 'O'))
d['newer'] = []