Подтвердить что ты не робот

Именованный массив dtype: Разница между [0] ['name'] и ['name'] [0]?

Я наткнулся на следующую странность в numpy, которая может быть или не быть ошибкой:

import numpy as np
dt = np.dtype([('tuple', (int, 2))])
a = np.zeros(3, dt)
type(a['tuple'][0])  # ndarray
type(a[0]['tuple'])  # ndarray

a['tuple'][0] = (1,2)  # ok
a[0]['tuple'] = (1,2)  # ValueError: shape-mismatch on array construction

Я бы ожидал, что оба варианта ниже работают. Мнения?

4b9b3361

Ответ 1

Это ошибка восходящего потока, фиксированная как NumPy PR # 5947 с исправлением в 1.9.3.

Ответ 2

Я спросил об этом в списке numpy-discussion. Трэвис Олифант ответил здесь.

Ссылаясь на его ответ:

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

Структурированные массивы могут быть запутанной областью NumPy по нескольким причинам. Вы создали пример, который затрагивает некоторые из них. У вас есть тип данных, который является массивом "структура" с одним элементом ( "кортеж" ). Этот член содержит 2-вектор целых чисел.

Прежде всего, важно помнить, что с Python, делая

a ['tuple'] [0] = (1,2)

эквивалентно

b = a ['tuple']; b [0] = (1,2)

Аналогично,

a [0] ['tuple'] = (1,2)

эквивалентно

b = a [0]; b ['tuple'] = (1,2)

Чтобы понять поведение, нам нужно проанализировать оба пути кода и что происходит. Вы построили (3,) массив этих элементов в 'a'. Когда вы пишете b = a ['tuple'], вы, вероятно, должны получать (3,) массив из (2,) - целых чисел, но поскольку в настоящее время формальная поддержка dtype для (n,) - целых чисел как общего типа dtype в NumPy вы возвращаете массив (3,2) целых чисел, который является самым близким, что может дать вам NumPy. Установка строки [0] этого объекта через

a ['tuple'] [0] = (1,2)

отлично работает и делает то, что вы ожидаете.

С другой стороны, при вводе:

b = a [0]

вы возвращаете массив-скаляр, который является особенно интересным видом массива, который может хранить записи. Этот новый объект формально имеет тип numpy.void и содержит "скалярное представление" всего, что соответствует базовому dtype "VOID".

По какой-то причине:

b ['tuple'] = [1,2]

не работает. В моей системе я получаю другую ошибку: TypeError: объект типа 'int' не имеет len()

Я думаю, что это должно быть зарегистрировано как ошибка в трекер-проблеме, которая на данный момент находится здесь: http://projects.scipy.org/numpy

В конечном итоге проблема заключается в том, что функция void- > copyswap вызывается в voidtype_setfields, если кто-то хочет исследовать. Я думаю, что это поведение должно работать.

Объяснение этому дано в отчет об ошибке numpy.

Ответ 3

Я получаю другую ошибку, чем вы (используя numpy 1.7.0.dev):

ValueError: setting an array element with a sequence.

поэтому приведенное ниже объяснение может быть неправильным для вашей системы (или это может быть неправильное объяснение того, что я вижу).

Во-первых, обратите внимание, что индексирование строки структурного массива дает вам объект numpy.void (см. документы типа данных)

import numpy as np
dt = np.dtype([('tuple', (int, 2))])
a = np.zeros(3, dt)
print type(a[0]) # = numpy.void

Из того, что я понимаю, void является своего рода списком Python, поскольку он может содержать объекты разных типов данных, что имеет смысл, поскольку столбцы в структурированном массиве могут быть разными типами данных.

Если вместо индексации вы срезаете первую строку, вы получаете ndarray:

print type(a[:1]) # = numpy.ndarray

Это аналогично тому, как работают списки Python:

b = [1, 2, 3]
print b[0] # 1
print b[:1] # [1]

Slicing возвращает сокращенную версию исходной последовательности, но индексирование возвращает элемент (здесь, int; выше, тип void).

Поэтому, когда вы нарезаете строки структурированного массива, вы должны ожидать, что он будет вести себя так же, как ваш исходный массив (только с меньшим количеством строк). Продолжая ваш пример, теперь вы можете назначить столбцам "кортеж" первой строки:

a[:1]['tuple'] = (1, 2)

Итак,... почему не работает a[0]['tuple'] = (1, 2)?

Напомним, что a[0] возвращает объект void. Поэтому, когда вы вызываете

a[0]['tuple'] = (1, 2) # this line fails

вы назначаете tuple элементу "tuple" этого объекта void. Примечание:, несмотря на то, что вы назвали этот индексный кортеж, он был сохранен как ndarray:

print type(a[0]['tuple']) # = numpy.ndarray

Итак, это означает, что кортеж должен быть добавлен в ndarray. Но объект void не может передавать задания (это всего лишь догадка), потому что он может содержать произвольные типы данных, поэтому он не знает, к какому типу относится. Чтобы обойти это, вы можете сами внести вклад:

a[0]['tuple'] = np.array((1, 2))

Тот факт, что мы получаем разные ошибки, говорит о том, что вышеприведенная строка может не работать для вас, поскольку вы выбрали ошибку, полученную мной, а не ту, которую вы получили.

Добавление:

Итак, почему работает следующее?

a[0]['tuple'][:] = (1, 2)

Здесь вы индексируете массив, когда добавляете [:], но без этого вы индексируете объект void. Другими словами, a[0]['tuple'][:] говорит "заменить элементы хранимого массива" (который обрабатывается массивом), a[0]['tuple'] говорит "заменить сохраненный массив" (который обрабатывается void).

Эпилог:

Как ни странно, доступ к строке (т.е. индексирование с помощью 0), похоже, отбрасывает базовый массив, но он все же позволяет назначать базовому массиву.

print a['tuple'].base is a # = True
print a[0].base is a # = False
a[0] = ((1, 2),) # `a` is changed

Может быть, void не является массивом, поэтому он не имеет базового массива... но тогда почему он имеет атрибут base?