Ответ 1
Это ошибка восходящего потока, фиксированная как NumPy PR # 5947 с исправлением в 1.9.3.
Я наткнулся на следующую странность в 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
Я бы ожидал, что оба варианта ниже работают. Мнения?
Это ошибка восходящего потока, фиксированная как NumPy PR # 5947 с исправлением в 1.9.3.
Я спросил об этом в списке 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.
Я получаю другую ошибку, чем вы (используя 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
?