Пожалуйста, помогите понять причину проблемы в приведенном ниже коде и предложите соответствующие статьи для изучения.
Насколько я понимаю, структурированный тип numpy с несколькими полями, который включает в себя подмассив, определяется как:
the_type = np.dtype(
[ # ndarray
(<name>, <numpy dtype>, <numpy shape>) # (name, dtype, shape)
]
)
np.shape([[1, 2]]) # 2D matrix shape (1, 2) with 1 row x 2 columns
np.shape([1]) # 1D array shape (1, )
np.shape(1) # 0D array shape () which is not a scalar
Тип данных subarray Структурированный тип данных может содержать ndarray со своим собственным dtype и формой:
dt = np.dtype([('a', np.int32), ('b', np.float32, (3,))])
np.zeros(3, dtype=dt)
---
array([(0, [0., 0., 0.]), (0, [0., 0., 0.]), (0, [0., 0., 0.])],
dtype=[('a', '<i4'), ('b', '<f4', (3,))])
Первый код работает с предупреждением, которое, как я считаю, жалоба 1
в ("b", np.ubyte, 1)
не является правильной формой numpy, и она должна быть в форме 1D-массива (1,). Это не проблема.
color_type = np.dtype([
("r", np.ubyte, (1,)),
("g", np.ubyte, (1)), # <--- warning
("b", np.ubyte, 1) # <--- warning
])
---
FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.
Однако второй код не работает и хотелось бы понять почему.
16
и (16)
оба являются (16,)
. Это правильно или зависит от dtype?"hoge"[3] -> 'e'
, тогда почему (16,) является ошибкой?dt = np.dtype(
[
('first', np.unicode_, 16), # OK and no warning
('middle', np.unicode_, (16)), # OK and no warning
('last', np.unicode_, (16,)), # <----- Error
('grades', np.float64, (2,)) # OK and no warning
]
)
x = np.array(
[
('Sarah', 'Jeanette', 'Conner', (8.0, 7.0)),
('John', '', 'Conner', (6.0, 7.0))
],
dtype=dt
)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-382-3e8049d5246c> in <module>
----> 1 dt = np.dtype(
2 [
3 ('first', np.unicode_, 16),
4 ('middle', np.unicode_, (16)),
5 ('last', np.unicode_, (16,)),
ValueError: invalid itemsize in generic type tuple
Понял, что неправильно понял dtype. В этом случае требуется не форма, а длина.
Как указывает ошибка, третья позиция в ('first', np.unicode_, 16)
интерпретируется как размер типа элемента кортежа. Итак, first
определяется как поле юникода размера 16.
('middle', np.unicode_, (16))
также работает, так как (16)
просто оценивается как 16
, круглые скобки излишни. Итак, middle
будет таким же, как first
.
Однако ('last', np.unicode_, (16,))
вызывает ошибку, потому что вы передаете tuple
как itemsize
для типа элемента кортежа, который имеет только одно измерение. (16,)
может пониматься только как кортеж и не преобразуется автоматически в скаляр, в то время как np.dtype
ожидает скаляр как itemsize
для поля np.unicode_
.
Если вашей целью было определить поле, которое принимает массив из шестнадцати значений Юникода некоторой длины (скажем, 10), вы должны использовать:
dt = np.dtype(
[
('first', np.unicode_, 16),
('middle', np.unicode_, (16)),
('last', 'U10', (16,)),
('grades', np.float64, (2,))
]
)
И тогда вы можете определить массив, например:
a = np.array([('x','y',
['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'],
[1.0, 2.0])], dt)
a
тогда будет определяться как:
array([('x', 'y', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'], [1., 2.])],
dtype=[('first', '<U16'), ('middle', '<U16'), ('last', '<U10', (16,)), ('grades', '<f8', (2,))])
Более простое определение dt
с тем же результатом, что и выше:
dt = np.dtype(
[
('first', 'U16'),
('middle', 'U16'),
('last', 'U10', (16,)),
('grades', np.float64, (2,))
]
)
Правильно ли я думаю, что вы хотите, чтобы 'last'
был массивом из 16 текстовых полей Unicode? Какого размера строки юникода?
В «последних» и «классах» кортеж представляет собой форму. Остальные - просто альтернативные способы указания «U16».
В «последних» и «классах» кортеж представляет собой форму. Остальные - просто альтернативные способы указания «U16».
Согласен @hpaulj - тогда я оставил без изменений исходный код, предоставленный OP, но объяснил, что именно вы говорите в ответе. Но поскольку вы не убрали это из предоставленного решения, я добавлю менее запутанное.
Мой комментарий предназначался больше для ОП, который все еще кажется немного сбитым с толку различными вариантами использования.
Вы написали,
«Я считаю, что 16 и (16) оба (16)»
Это не правильно.
В Python есть встроенный тип контейнера с именем tuple
.
Следующий код представляет собой пример tuple
x = (1, 56, 7, 9, 13)
Следующий код НЕ создает кортеж, содержащий число 16.
y = (16)
print(type(y))
# <class 'int'>
Причина этого очень проста: если скобки заключают только один объект, то скобки обозначают математический порядок операций, а не tuple
x = (1 + 5) * 9
x = ( 6 ) * 9
x = 6 * 9
x = 54
Итак... (16)
это не кортеж.
16
— целое число. 16
НЕ является кортежем(16)
также является целым числом. (16)
НЕ кортеж(16,)
— это кортеж.print((16) == 16)
# prints `True`
Игнорируйте все, кроме container
и цикла for в следующем коде.
Попробуйте зациклиться на (5)
. Результат сильно отличается от цикла for (1, 2, 3)
.
import io
string_stream = io.StringIO()
try:
container = (1, 65, 8, 3, 3, 9)
container = (5) # try commenting-out this line
####################################
# WILL THE FOLLOWING FOR-LOOP WORK!??
####################################
for elem in iter(container):
print(
elem,
file = string_stream
)
print(
"THE FOR-LOOP SUCCESSFULLY EXECUTED!",
file = string_stream
)
except TypeError as tipe_ehror:
print(
container,
type(tipe_ehror),
tipe_ehror,
sep = "\n",
file = string_stream
)
finally:
print(string_stream.getvalue())
(5)
НЕ является контейнером.(5)
имеет круглые скобки, обозначающие порядок операций для некоторых математических операций.
Обратите внимание, что (1 + 2)
может выглядеть как несколько элементов в скобках.
(1 + 2)
на самом деле это только ОДИН объект внутри круглых скобок.
Знак плюс (+
) — это результат, возвращаемый функцией int.__add__
.
Даже если это выглядит как много всего, все, что находится внутри математических скобок, в конечном итоге сводится к одному значению внутри скобок.
x = ((1 + 2) * 7)
x = ((3) * 7)
x = (3 * 7)
x = (21)
x = 21
x = int.__mul__(int.__add__(1, 2), 7)
- Согласно предупреждению в приведенном выше коде, я считаю, что 16 и (16) оба (16). Это правильно или зависит от dtype?
В Python 16
— это целочисленный литерал, а (16)
— выражение в скобках Python, результатом которого является значение 16
. (Помните, что когда вы заключаете выражение в круглые скобки, вы делаете это для управления порядком вычисления операторов, а не для преобразования выражения в кортеж. Например, в выражении (2 + 3)/2
круглые скобки, окружающие 2 + 3
, не приводят к кортеж; скорее, они служат только для того, чтобы оператор +
оценивался перед оператором /
).
В Python (16,)
определенно является кортежем. Следовательно, это не эквивалентно 16
или (16)
.
- Я думаю, что строка Unicode представляет собой массив в Python как «hoge» [3] -> «e»
Нет, в Python строка Unicode не является массивом. Тот факт, что вы можете выполнять операцию индексации []
для строки Unicode, не обязательно делает ее массивом. Если уж на то пошло, вы можете выполнить операцию []
и над dict
, а dict
также не являются массивами.
тогда почему (16,) ошибка?
В numpy
, когда вы указываете поле как строку юникода, numpy
необходимо знать, сколько символов юникода будет содержаться в этой строке. (numpy
поддерживает только строки фиксированной длины в качестве полей пользовательского dtype
). Другими словами, вам нужно указать numpy
длину строки юникода. И это, конечно же, должно быть простое целое число 16
, а не кортеж (16,)
.
Кстати, если вы не укажете длину поля строки юникода, ошибки не будет, так как numpy
будет предполагать, что поле представляет собой строку юникода нулевой длины; вы получите ошибку во время присвоения значений строковому полю.
Вот массив с двумя полями, одно со строковым dtype, другое числовое:
In [148]: np.array([('abc',2),('defg',5)], dtype=[('x','U10'),('y',int)] )
Out[148]: array([('abc', 2), ('defg', 5)], dtype=[('x', '<U10'), ('y', '<i8')])
In [149]: _.shape
Out[149]: (2,)
In [150]: __['x']
Out[150]: array(['abc', 'defg'], dtype='<U10')
Обратите внимание, что я указываю длину строки Unicode, «U10» (10 символов).
Я также могу указать длину строки отдельным номером. Это то, что вы делаете с np.unicode_, 16
. Результирующий dtype тот же.
In [151]: np.array([('abc',2),('defg',5)], dtype=[('x','U',10),('y',int)] )
Out[151]: array([('abc', 2), ('defg', 5)], dtype=[('x', '<U10'), ('y', '<i8')])
Но если я укажу число после числового dtype, я получу новое измерение. Это спецификация (<name>, <numpy dtype>, <numpy shape>)
:
In [152]: np.array([('abc',[2,3]),('defg',[5,4])], dtype=[('x','U',10),('y',int,2)] )
Out[152]:
array([('abc', [2, 3]), ('defg', [5, 4])],
dtype=[('x', '<U10'), ('y', '<i8', (2,))])
In [153]: _['y'] # shape (2,2)
Out[153]:
array([[2, 3],
[5, 4]])
Я мог бы определить строковое поле, чтобы оно имело измерение:
In [155]: np.array([(['abc','xuz'],),(['defg','foo'],)], dtype=[('x','U10',2)] )
Out[155]: array([(['abc', 'xuz'],), (['defg', 'foo'],)], dtype=[('x', '<U10', (2,))])
In [156]: _['x']
Out[156]:
array([['abc', 'xuz'],
['defg', 'foo']], dtype='<U10')
Здесь снова форма (2,2).
Третий элемент кортежа имеет другую функцию в этих двух выражениях: ('x','U',10)
и ('x','U10',2)
.
Обычно я использую «U10», поэтому раньше не сталкивался с случаем 'U',10
. Я мог бы объединить два с:
In [158]: np.array([(['abc','xuz'],),(['defg','foo'],)], dtype=[('x',('U',10),2)] )
Out[158]: array([(['abc', 'xuz'],), (['defg', 'foo'],)], dtype=[('x', '<U10', (2,))])
То же, что и [155].
Так что это должно объяснить, почему ('x','U',(10,))
не работает; 10 здесь — это длина строки, как в «U10», а не форма.
Одна строка «U10» на поле:
In [166]: np.zeros((1,), dtype=[('x','U',10)])
Out[166]: array([('',)], dtype=[('x', '<U10')])
такой же:
In [167]: np.zeros((1,), dtype=[('x','U10')])
Out[167]: array([('',)], dtype=[('x', '<U10')])
10 строк 'U1' на поле:
In [168]: np.zeros((1,), dtype=[('x','U1',10)])
Out[168]:
array([(['', '', '', '', '', '', '', '', '', ''],)],
dtype=[('x', '<U1', (10,))])
Форма поля может быть многомерной:
In [169]: np.zeros((1,), dtype=[('x','U1',(2,3))])
Out[169]: array([([['', '', ''], ['', '', '']],)], dtype=[('x', '<U1', (2, 3))])
In [170]: _['x']
Out[170]:
array([[['', '', ''],
['', '', '']]], dtype='<U1')
In [171]: _.shape
Out[171]: (1, 2, 3)
Кортеж подходит при указании формы поля, но не при указании длины строки. Если вы хотите, чтобы третий элемент кортежа был формой поля, укажите «U10», а не «U» или «Unicode».
Предупреждение — это другое дело:
In [175]: np.zeros((1,), dtype=[('x','U10')])
Out[175]: array([('',)], dtype=[('x', '<U10')])
In [176]: _['x'].shape
Out[176]: (1,)
До сих пор это одно и то же, с «1» без разницы:
In [177]: np.zeros((1,), dtype=[('x','U10',1)])
<ipython-input-177-932c79fbeaf4>:1: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.
np.zeros((1,), dtype=[('x','U10',1)])
Out[177]: array([('',)], dtype=[('x', '<U10')])
In [178]: _['x'].shape
Out[178]: (1,)
Но они сглаживают острые углы, чтобы в будущем это вело себя так:
In [179]: np.zeros((1,), dtype=[('x','U10',(1,))])
Out[179]: array([([''],)], dtype=[('x', '<U10', (1,))])
In [180]: _['x'].shape
Out[180]: (1, 1)
Это сделает его совместимым с другими вариантами использования формы поля:
In [183]: np.zeros((1,), dtype=[('x','U10',2)])['x'].shape
Out[183]: (1, 2)
In [184]: np.zeros((1,), dtype=[('x','U10',(2,))])['x'].shape
Out[184]: (1, 2)
Спасибо за ответ, но все еще не ясно, поскольку мое понимание определения структурированного типа взято из документа, в котором говорится: «Структурированный тип данных может содержать ndarray со своим собственным типом dtype и формой», следовательно, часть (16) должна быть пустой формой. Какой я считаю (n,) формат? Или 16 не определяет пустую форму?