既然您向结构化数组的一维切片中添加了一个新字段,为什么不能将新字段的条目设置为列表呢? [英] 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?

查看:24
本文介绍了既然您向结构化数组的一维切片中添加了一个新字段,为什么不能将新字段的条目设置为列表呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

标题可能有点令人困惑,所以我希望我能通过一个例子让它更清楚。图像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,)结构化数组的属性,即:

  1. 如果在整数位置为数组编制索引,则会得到一个结构(例如d[0])
  2. 您可以通过使用字段名(例如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([]))]

这篇关于既然您向结构化数组的一维切片中添加了一个新字段,为什么不能将新字段的条目设置为列表呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆