NumPy: подробное объяснение структурированных массивов

NumPy

Введение

Обычный массив — это массив объектов одного типа. Структурированный массив — это формат для хранения различных объектов в массиве.

Сегодня мы подробно обсудим структурированные массивы в NumPy.

Поле поля в структурированном массиве

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

Каждое поле состоит из 3 частей, а именно: имя строкового типа, тип любого допустимого типа dtype и необязательныйtitle.

См. пример построения dtype с использованием filed:

In [165]: np.dtype([('name', 'U10'), ('age', 'i4'), ('weight', 'f4')])
Out[165]: dtype([('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])

Мы можем использовать приведенный выше dtype для создания нового массива:

In [166]: x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)],
     ...:     dtype=[('name', 'U10'), ('age', 'i4'), ('weight', 'f4')])
     ...:

In [167]: x
Out[167]:
array([('Rex', 9, 81.), ('Fido', 3, 27.)],
      dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])

x представляет собой одномерный массив, каждый элемент которого содержит три поля: имя, возраст и вес. И укажите их типы данных соответственно.

Доступ к строке данных можно получить через индекс:

In [168]: x[1]
Out[168]: ('Fido', 3, 27.)

Вы также можете получить доступ к столбцу данных по имени:

In [170]: x['name']
Out[170]: array(['Rex', 'Fido'], dtype='<U10')

Вы также можете присвоить одинаковые значения всем столбцам:

In [171]: x['age']
Out[171]: array([9, 3], dtype=int32)

In [172]: x['age'] = 10

In [173]: x
Out[173]:
array([('Rex', 10, 81.), ('Fido', 10, 27.)],
      dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])

структурированный тип данных

Приведенный выше пример дает нам базовое понимание структурированных типов данных. Структурированный тип данных представляет собой набор файлов.

Создание структурированных типов данных

Структурированные типы данных создаются из базовых типов следующими способами:

создать из кортежа

Каждый кортеж находится в формате (имя поля, тип данных, форма), где форма не является обязательной. fieldname — это заголовок поля.

In [174]: np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2, 2))])
Out[174]: dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))])

Если имя поля является пустым символом, оно будет создано по умолчанию в форме, начинающейся с f.

In [177]: np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')])
Out[177]: dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')])

Создать из dtype, разделенных запятыми

При желании создайте из типов dtype, разделенных запятыми:

In [178]: np.dtype('i8, f4, S3')
Out[178]: dtype([('f0', '<i8'), ('f1', '<f4'), ('f2', 'S3')])

In [179]: np.dtype('3int8, float32, (2, 3)float64')
Out[179]: dtype([('f0', 'i1', (3,)), ('f1', '<f4'), ('f2', '<f8', (2, 3))])

создать из словаря

Из словаря создается следующий формат: {'names': ..., 'formats': ..., 'offsets': ..., 'titles': ..., 'itemsize': ...}

Эта нотация может указывать список имен и список форматов.

offsetsОтносится к смещению в байтах каждого поля. titles — название поля,itemsizeэто размер всего dtype.

In [180]: np.dtype({'names': ['col1', 'col2'], 'formats': ['i4', 'f4']})
Out[180]: dtype([('col1', '<i4'), ('col2', '<f4')])

In [181]: np.dtype({'names': ['col1', 'col2'],
     ...: ...           'formats': ['i4', 'f4'],
     ...: ...           'offsets': [0, 4],
     ...: ...           'itemsize': 12})
     ...:
Out[181]: dtype({'names':['col1','col2'], 'formats':['<i4','<f4'], 'offsets':[0,4], 'itemsize':12})

Управление структурированными типами данных

Доступ к свойствам структурированных типов данных можно получить через имена и поля полей типа dtype:

>>> d = np.dtype([('x', 'i8'), ('y', 'f4')])
>>> d.names
('x', 'y')
>>> d.fields
mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)})

Смещения и выравнивания

Для структурированных типов, поскольку dtype содержит несколько типов данных, эти типы данных не выравниваются по умолчанию.

Мы можем рассмотреть каждый тип смещения на следующем примере:

>>> def print_offsets(d):
...     print("offsets:", [d.fields[name][1] for name in d.names])
...     print("itemsize:", d.itemsize)
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2'))
offsets: [0, 1, 2, 6, 7, 15]
itemsize: 17

Если при создании типа dtype указано align=True, то эти типы могут быть выровнены в соответствии со структурой C-struct.

Преимущество выравнивания в том, что оно может повысить эффективность обработки. Рассмотрим пример выравнивания:

>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2', align=True))
offsets: [0, 1, 4, 8, 16, 24]
itemsize: 32

Field Titles

В дополнение к имени каждый Filed также может содержать заголовок.

Есть два способа указать заголовок, первый способ:

In [182]: np.dtype([(('my title', 'name'), 'f4')])
Out[182]: dtype([(('my title', 'name'), '<f4')])

Второй способ:

In [183]: np.dtype({'name': ('i4', 0, 'my title')})
Out[183]: dtype([(('my title', 'name'), '<i4')])

Взгляните на структуру полей:

In [187]: d.fields
Out[187]:
mappingproxy({'my title': (dtype('float32'), 0, 'my title'),
              'name': (dtype('float32'), 0, 'my title')})

структурированный массив

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

назначать

Мы можем присваивать значения структурированным массивам из кортежей:

>>> x = np.array([(1, 2, 3), (4, 5, 6)], dtype='i8, f4, f8')
>>> x[1] = (7, 8, 9)
>>> x
array([(1, 2., 3.), (7, 8., 9.)],
     dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '<f8')])

Структурированным массивам также можно присваивать значения из скаляров:

>>> x = np.zeros(2, dtype='i8, f4, ?, S1')
>>> x[:] = 3
>>> x
array([(3, 3., True, b'3'), (3, 3., True, b'3')],
      dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])
>>> x[:] = np.arange(2)
>>> x
array([(0, 0., False, b'0'), (1, 1., True, b'1')],
      dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])

Структурированные массивы также могут быть присвоены неструктурированным массивам, но только в том случае, если структурированный массив имеет только одно поле:

>>> twofield = np.zeros(2, dtype=[('A', 'i4'), ('B', 'i4')])
>>> onefield = np.zeros(2, dtype=[('A', 'i4')])
>>> nostruct = np.zeros(2, dtype='i4')
>>> nostruct[:] = twofield
Traceback (most recent call last):
...
TypeError: Cannot cast array data from dtype([('A', '<i4'), ('B', '<i4')]) to dtype('int32') according to the rule 'unsafe'

Структурированные массивы также могут присваивать значения друг другу:

>>> a = np.zeros(3, dtype=[('a', 'i8'), ('b', 'f4'), ('c', 'S3')])
>>> b = np.ones(3, dtype=[('x', 'f4'), ('y', 'S3'), ('z', 'O')])
>>> b[:] = a
>>> b
array([(0., b'0.0', b''), (0., b'0.0', b''), (0., b'0.0', b'')],
      dtype=[('x', '<f4'), ('y', 'S3'), ('z', 'O')])

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

Как упоминалось ранее, вы можете получить доступ и изменить столбец данных по имени файла:

>>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])
>>> x['foo']
array([1, 3])
>>> x['foo'] = 10
>>> x
array([(10, 2.), (10, 4.)],
      dtype=[('foo', '<i8'), ('bar', '<f4')])

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

Посмотрите на случай, когда поле представляет собой многомерный массив:

In [188]: np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])
Out[188]:
array([[(0, [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]),
        (0, [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])],
       [(0, [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]),
        (0, [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])]],
      dtype=[('a', '<i4'), ('b', '<f8', (3, 3))])

Вышеприведенное строит матрицу 2 * 2, первый столбец в этой матрице имеет тип int, а второй столбец представляет собой матрицу с плавающей запятой 3 * 3.

Мы можем просмотреть значение формы каждого столбца следующим образом:

>>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])
>>> x['a'].shape
(2, 2)
>>> x['b'].shape
(2, 2, 3, 3)

В дополнение к доступу к одному столбцу мы также можем получить доступ к нескольким столбцам данных одновременно:

>>> a = np.zeros(3, dtype=[('a', 'i4'), ('b', 'i4'), ('c', 'f4')])
>>> a[['a', 'c']]
array([(0, 0.), (0, 0.), (0, 0.)],
     dtype={'names':['a','c'], 'formats':['<i4','<f4'], 'offsets':[0,8], 'itemsize':12})

Назначьте несколько столбцов одновременно:

>>> a[['a', 'c']] = (2, 3)
>>> a
array([(2, 0, 3.), (2, 0, 3.), (2, 0, 3.)],
      dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<f4')])

Простая замена данных столбца:

>>> a[['a', 'c']] = a[['c', 'a']]

Record Arrays

Доступ к структурированным массивам возможен только через индекс, что очень неудобно, поэтому NumPy предоставляет подкласс numpy.recarray для многомерных массивов, к которым затем можно получить доступ через атрибуты.

Давайте рассмотрим несколько примеров:

>>> recordarr = np.rec.array([(1, 2., 'Hello'), (2, 3., "World")],
...                    dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
>>> recordarr.bar
array([ 2.,  3.], dtype=float32)
>>> recordarr[1:2]
rec.array([(2, 3., b'World')],
      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])
>>> recordarr[1:2].foo
array([2], dtype=int32)
>>> recordarr.foo[1:2]
array([2], dtype=int32)
>>> recordarr[1].baz
b'World'

Результатом, возвращаемым recarray, является rec.array. В дополнение к использованию np.rec.array для создания вы также можете использовать представление:

In [190]: arr = np.array([(1, 2., 'Hello'), (2, 3., "World")],
     ...: ...                dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'a10')])
     ...:

In [191]: arr
Out[191]:
array([(1, 2., b'Hello'), (2, 3., b'World')],
      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])

In [192]: arr.view(dtype=np.dtype((np.record, arr.dtype)),
     ...: ...                      type=np.recarray)
     ...:
Out[192]:
rec.array([(1, 2., b'Hello'), (2, 3., b'World')],
          dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])

Если это объект rec.array, его тип dtype будет автоматически преобразован в тип np.record:

In [200]: recordarr.dtype
Out[200]: dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]))

Чтобы преобразовать обратно в исходный тип np.ndarray, можно сделать следующее:

In [202]: recordarr.view(recordarr.dtype.fields or recordarr.dtype, np.ndarray)
Out[202]:
array([(1, 2., b'Hello'), (2, 3., b'World')],
      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])

Если доступ к полю объекта rec.array осуществляется по индексу или полю, если поле является структурным типом, оно вернет numpy.recarray, если это неструктурный тип, оно вернет numpy.ndarray:

>>> recordarr = np.rec.array([('Hello', (1, 2)), ("World", (3, 4))],
...                 dtype=[('foo', 'S6'),('bar', [('A', int), ('B', int)])])
>>> type(recordarr.foo)
<class 'numpy.ndarray'>
>>> type(recordarr.bar)
<class 'numpy.recarray'>

Эта статья была включена вwoohoo.floydpress.com/05-python-is…

Самая популярная интерпретация, самая глубокая галантерея, самые краткие уроки и множество трюков, о которых вы не знаете, ждут вас!

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: «Программируйте эти вещи», разбирайтесь в технологиях, лучше поймите себя!