Numpy - разница в форме между 16, (16) и (16,)

Вопрос

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

Фон

Насколько я понимаю, структурированный тип 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'.

Однако второй код не работает и хотелось бы понять почему.

  1. Согласно предупреждению в приведенном выше коде, я считаю, что 16 и (16) оба являются (16,). Это правильно или зависит от dtype?
  2. Я думаю, что строка Unicode является массивом в Python как "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. В этом случае требуется не форма, а длина.

Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python.
Некоторые методы, о которых вы не знали, что они существуют в Python.
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
306
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Как указывает ошибка, третья позиция в ('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,))   
  ]
)

Спасибо за ответ, но все еще не ясно, поскольку мое понимание определения структурированного типа взято из документа, в котором говорится: «Структурированный тип данных может содержать ndarray со своим собственным типом dtype и формой», следовательно, часть (16) должна быть пустой формой. Какой я считаю (n,) формат? Или 16 не определяет пустую форму?

mon 21.12.2020 04:00

Правильно ли я думаю, что вы хотите, чтобы 'last' был массивом из 16 текстовых полей Unicode? Какого размера строки юникода?

Grismar 21.12.2020 05:25

В «последних» и «классах» кортеж представляет собой форму. Остальные - просто альтернативные способы указания «U16».

hpaulj 21.12.2020 07:59

В «последних» и «классах» кортеж представляет собой форму. Остальные - просто альтернативные способы указания «U16».

hpaulj 21.12.2020 07:59

Согласен @hpaulj - тогда я оставил без изменений исходный код, предоставленный OP, но объяснил, что именно вы говорите в ответе. Но поскольку вы не убрали это из предоставленного решения, я добавлю менее запутанное.

Grismar 21.12.2020 08:05

Мой комментарий предназначался больше для ОП, который все еще кажется немного сбитым с толку различными вариантами использования.

hpaulj 21.12.2020 08:32

Вы написали,

«Я считаю, что 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)    
Ответ принят как подходящий
  1. Согласно предупреждению в приведенном выше коде, я считаю, что 16 и (16) оба (16). Это правильно или зависит от dtype?

В Python 16 — это целочисленный литерал, а (16) — выражение в скобках Python, результатом которого является значение 16. (Помните, что когда вы заключаете выражение в круглые скобки, вы делаете это для управления порядком вычисления операторов, а не для преобразования выражения в кортеж. Например, в выражении (2 + 3)/2 круглые скобки, окружающие 2 + 3, не приводят к кортеж; скорее, они служат только для того, чтобы оператор + оценивался перед оператором /).

В Python (16,) определенно является кортежем. Следовательно, это не эквивалентно 16 или (16).

  1. Я думаю, что строка 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)

Другие вопросы по теме