鉴于您将新字段添加到结构化数组的一维切片中,为什么不能将新字段的条目设置为列表?
Given that you added a new field to a 1-d slice of a structured array, why can you not set the entry of the new field to a list?
标题可能有点混乱,所以我希望我能借助一个例子来解释清楚。图片 我有一个小辅助函数,可以将新字段添加到现有的结构化数组中:
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
中的值不会:
>>> 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']=[]
语法为字段的每个元素分配一个空列表?
当您使用此语法分配可迭代对象而不是标量时,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([]))]
标题可能有点混乱,所以我希望我能借助一个例子来解释清楚。图片 我有一个小辅助函数,可以将新字段添加到现有的结构化数组中:
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
中的值不会:
>>> 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']=[]
语法为字段的每个元素分配一个空列表?
当您使用此语法分配可迭代对象而不是标量时,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([]))]