Учитывая, что вы добавили новое поле в 1-й фрагмент структурированного массива, почему вы не можете установить запись нового поля в список? - PullRequest
1 голос
/ 16 января 2020

Название может быть немного запутанным, поэтому я надеюсь, что смогу прояснить его с помощью примера. У меня есть небольшая вспомогательная функция, которая добавляет новые поля к уже существующим структурированным массивам:

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': []}

Однако доступ к файлам за последние dict с помощью клавиши 'test' не дает:

>>> 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'] = []

1 Ответ

1 голос
/ 17 января 2020

Чтобы суммировать комментарии:

Проблема в исходном вопросе выглядит как форма возвращаемого объекта - когда вы делаете, например,

c = a[0]

с a, имеющим форму (n,) вы берете не фрагмент из массива, а отдельный элемент. c.shape тогда равно (). Когда вы передаете массив формы () в add_field, новый массив, созданный

b = np.empty(a.shape, dtype=a.dtype.descr + [*descr])

, также будет иметь форму (). Однако структурированному массиву необходимо иметь форму (n,) (хотя это не указано в документации ).

Как и при первом редактировании вопроса, правильная модификация будет

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'] = []

Это подводит нас к сути вопроса:


Почему мы не можем назначить пустой список каждому элементу поля, используя * 1047? * синтаксис?

Когда вы назначаете итерируемое вместо скаляра с использованием этого синтаксиса, numpy пытается поэлементное назначение (или широковещательную передачу в зависимости от итерируемого). Это отличается от назначения скаляра, в котором скаляр назначается каждому элементу этого поля. документация не ясна по этому вопросу, но мы можем получить гораздо более полезную ошибку, используя

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([]))]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...