问题描述
标题可能有点令人困惑,所以我希望我能通过一个例子让它更清楚。图像I有一个小帮助器函数,用于向已有的结构化数组添加新字段:
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,)
(并且type(b)
是np.void
,而type(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'
访问最后dict
中的vaue不会:
>>> 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_field
函数,如下所示:
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
所以我想事情不是这样的。不过,现在可以使用了:
>>> d['newer'][0] = []
但我不喜欢这种变通方法。我希望它的工作方式与b[0]
相同。
编辑%2
如果我进一步修改add_field
函数,我可以强制执行所需的行为,尽管我不是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
则为()
。当您将()
形状的数组传递给add_field
时,由创建的新数组
b = np.empty(a.shape, dtype=a.dtype.descr + [*descr])
也将具有形状()
。然而,结构化数组必须具有(n,)
形状(尽管documentation中没有概述)。
与问题的第一次编辑一样,正确的修改应为
def add_field(a, *descr):
shape = a.shape if len(a.shape) else (1,)
b = np.empty(shape, dtype=a.dtype.descr + [*descr])
b[list(a.dtype.names)] = a
return b
然后返回的对象将共享形状(n,)
结构化数组的属性,即:
- 如果在整数位置为数组编制索引,则会得到一个结构(例如
d[0]
) - 您可以通过使用字段名(例如
d['newer']
)进行索引来访问和修改结构化数组的各个字段
通过上述修改,问题中d
的行为与b
相同,例如
d[0]['newer'] = []
有效,
有效b[0]['new'] = []
这就引出了问题的真正症结:
为什么不能使用d['newer']=[]
语法为字段的每个元素分配一个空列表?
&emsp;&emsp;当您使用此语法指定一个迭代数而不是标量时,NumPy会尝试按元素进行赋值(或根据迭代数进行广播)。这与标量的赋值不同,在标量的赋值中,标量被赋值给该字段的每个元素。documentation在这一点上不清楚,但我们可以通过使用
得到一个更有用的错误b['new'] = np.array([])
Traceback (most recent call last):
File "structuredArray.py", line 20, in <module>
b['new'] = np.array([])
ValueError: could not broadcast input array from shape (0) into shape (4)
因此,这里的问题不是如何添加字段,而是如何尝试为该字段的每个元素分配一个空列表。执行此操作的正确方法如下
b['new'] = [[]*b.shape[0]]
对于(1,)
和(4,)
形状的结构化数组,其工作方式与预期相同:
import numpy as np
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
a = np.array(
[(1, False), (2, False), (3, False), (4, True)],
dtype=[('id', 'i4'), ('used', '?')]
)
b = add_field(a, ('new', 'O'))
b['new'] = [[]*b.shape[0]]
print(b)
c = a[0]
d = add_field(c, ('newer', 'O'))
d['newer'] = [[]*d.shape[0]]
print(d)
[(1, False, list([])) (2, False, list([])) (3, False, list([])) (4, True, list([]))]
[(1, False, list([]))]
这篇关于既然您向结构化数组的一维切片中添加了一个新字段,为什么不能将新字段的条目设置为列表呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!