Создание массивов#

Введение#

Существует 6 общих механизмов создания массивов:

  1. Преобразование из других структур Python (т.е. списков и кортежей)

  2. Встроенные функции создания массивов NumPy (например, arange, ones, zeros и т.д.)

  3. Репликация, объединение или изменение существующих массивов

  4. Чтение массивов с диска, либо из стандартных, либо из пользовательских форматов

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

  6. Использование специальных библиотечных функций (например, random)

Вы можете использовать эти методы для создания ndarrays или Структурированные массивы. Этот документ охватит общие методы создания ndarray.

1) Преобразование последовательностей Python в массивы NumPy#

Массивы NumPy могут быть определены с использованием последовательностей Python, таких как списки и кортежи. Списки и кортежи определяются с помощью [...] и (...), соответственно. Списки и кортежи могут определять создание ndarray:

  • список чисел создаст одномерный массив,

  • список списков создаст двумерный массив,

  • дальнейшие вложенные списки создадут массивы более высокой размерности. В общем случае любой объект массива называется ndarray в NumPy.

>>> import numpy as np
>>> a1D = np.array([1, 2, 3, 4])
>>> a2D = np.array([[1, 2], [3, 4]])
>>> a3D = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

Когда вы используете numpy.array чтобы определить новый массив, вам следует учитывать dtype элементов в массиве, которые могут быть указаны явно. Эта функция дает вам больше контроля над базовыми структурами данных и тем, как элементы обрабатываются в функциях C/C++. Когда значения не помещаются и вы используете dtype, NumPy может вызвать ошибку:

>>> import numpy as np
>>> np.array([127, 128, 129], dtype=np.int8)
Traceback (most recent call last):
...
OverflowError: Python integer 128 out of bounds for int8

8-битное знаковое целое число представляет целые числа от -128 до 127. Присвоение int8 преобразование массива в целые числа вне этого диапазона приводит к переполнению. Эта функция часто может быть неправильно понята. Если вы выполняете вычисления с несоответствующими dtypes, вы можете получить нежелательные результаты, например:

>>> import numpy as np
>>> a = np.array([2, 3, 4], dtype=np.uint32)
>>> b = np.array([5, 6, 7], dtype=np.uint32)
>>> c_unsigned32 = a - b
>>> print('unsigned c:', c_unsigned32, c_unsigned32.dtype)
unsigned c: [4294967293 4294967293 4294967293] uint32
>>> c_signed32 = a - b.astype(np.int32)
>>> print('signed c:', c_signed32, c_signed32.dtype)
signed c: [-3 -3 -3] int64

Обратите внимание, что при выполнении операций с двумя массивами одинакового dtype: uint32, результирующий массив имеет тот же тип. Когда вы выполняете операции с разными dtype, NumPy будет назначать новый тип, который удовлетворяет всем элементам массива, участвующим в вычислениях, здесь uint32 и int32 могут быть представлены в как int64.

Поведение NumPy по умолчанию — создавать массивы в 32- или 64-битных знаковых целых числах (зависит от платформы и соответствует C long размер) или числа с плавающей точкой двойной точности. Если вы ожидаете, что ваши целочисленные массивы будут определённого типа, то вам нужно указать dtype при создании массива.

2) Встроенные функции создания массивов NumPy#

NumPy имеет более 40 встроенных функций для создания массивов, как описано в Процедуры создания массивов. Эти функции можно разделить примерно на три категории в зависимости от размерности создаваемого массива:

  1. 1D массивы

  2. 2D массивы

  3. ndarrays

1 - Функции создания 1D массивов#

Функции создания 1D массивов, например numpy.linspace и numpy.arange обычно требуется как минимум два входа, start и stop.

numpy.arange создает массивы с регулярно увеличивающимися значениями. Проверьте документацию для получения полной информации и примеров. Несколько примеров показаны:

>>> import numpy as np
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.arange(2, 10, dtype=float)
array([2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.arange(2, 3, 0.1)
array([2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])

Примечание: лучшая практика для numpy.arange использовать целочисленные значения начала, конца и шага. Есть некоторые тонкости относительно dtype. Во втором примере, dtype определён. В третьем примере массив dtype=float чтобы учесть размер шага 0.1. Из-за ошибок округления, stop значение иногда включается.

numpy.linspace создаст массивы с заданным количеством элементов, равномерно распределённых между указанными начальным и конечным значениями. Например:

>>> import numpy as np
>>> np.linspace(1., 4., 6)
array([1. ,  1.6,  2.2,  2.8,  3.4,  4. ])

Преимущество этой функции создания в том, что вы гарантируете количество элементов и начальную и конечную точки. Предыдущий arange(start, stop, step) не будет включать значение stop.

2 - Функции создания 2D массивов#

Функции создания 2D массивов, например, numpy.eye, numpy.diag, и numpy.vander определяют свойства специальных матриц, представленных в виде двумерных массивов.

np.eye(n, m) определяет 2D единичную матрицу. Элементы, где i=j (индекс строки и индекс столбца равны), равны 1, а остальные 0, как показано:

>>> import numpy as np
>>> np.eye(3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
>>> np.eye(3, 5)
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.]])

numpy.diag может определить либо квадратный 2D массив с заданными значениями вдоль диагонали или . Этот механизм основан на файле .ini, который содержит все опции. Файл .ini очень похож на файлы .pc, используемые утилитой pkg-config в Unix:

>>> import numpy as np
>>> np.diag([1, 2, 3])
array([[1, 0, 0],
       [0, 2, 0],
       [0, 0, 3]])
>>> np.diag([1, 2, 3], 1)
array([[0, 1, 0, 0],
       [0, 0, 2, 0],
       [0, 0, 0, 3],
       [0, 0, 0, 0]])
>>> a = np.array([[1, 2], [3, 4]])
>>> np.diag(a)
array([1, 4])

vander(x, n) определяет матрицу Вандермонда как двумерный массив NumPy. Каждый столбец матрицы Вандермонда представляет собой убывающую степень входного одномерного массива, списка или кортежа, x где наивысший порядок полинома n-1. Эта процедура создания массива полезна при генерации линейных моделей наименьших квадратов, например:

>>> import numpy as np
>>> np.vander(np.linspace(0, 2, 5), 2)
array([[0. , 1. ],
      [0.5, 1. ],
      [1. , 1. ],
      [1.5, 1. ],
      [2. , 1. ]])
>>> np.vander([1, 2, 3, 4], 2)
array([[1, 1],
       [2, 1],
       [3, 1],
       [4, 1]])
>>> np.vander((1, 2, 3, 4), 4)
array([[ 1,  1,  1,  1],
       [ 8,  4,  2,  1],
       [27,  9,  3,  1],
       [64, 16,  4,  1]])

3 - общие функции создания ndarray#

Функции создания ndarray, например, numpy.ones, numpy.zeros, и random определить массивы на основе желаемой формы. Функции создания ndarray могут создавать массивы с любым количеством измерений, указывая, сколько измерений и длину вдоль этого измерения в кортеже или списке.

numpy.zeros создаст массив, заполненный значениями 0 с указанной формой. Тип данных по умолчанию — float64:

>>> import numpy as np
>>> np.zeros((2, 3))
array([[0., 0., 0.],
       [0., 0., 0.]])
>>> np.zeros((2, 3, 2))
array([[[0., 0.],
        [0., 0.],
        [0., 0.]],

       [[0., 0.],
        [0., 0.],
        [0., 0.]]])

numpy.ones создаст массив, заполненный значениями 1. Он идентичен zeros во всех остальных отношениях как таковой:

>>> import numpy as np
>>> np.ones((2, 3))
array([[1., 1., 1.],
       [1., 1., 1.]])
>>> np.ones((2, 3, 2))
array([[[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]]])

The random метод результата default_rng создаст массив, заполненный случайными значениями от 0 до 1. Он включен в numpy.random библиотеке. Ниже созданы два массива с формами (2,3) и (2,3,2) соответственно. Сид установлен на 42, чтобы вы могли воспроизвести эти псевдослучайные числа:

>>> import numpy as np
>>> from numpy.random import default_rng
>>> default_rng(42).random((2,3))
array([[0.77395605, 0.43887844, 0.85859792],
       [0.69736803, 0.09417735, 0.97562235]])
>>> default_rng(42).random((2,3,2))
array([[[0.77395605, 0.43887844],
        [0.85859792, 0.69736803],
        [0.09417735, 0.97562235]],
       [[0.7611397 , 0.78606431],
        [0.12811363, 0.45038594],
        [0.37079802, 0.92676499]]])

numpy.indices создаст набор массивов (уложенных как массив на одно измерение выше), по одному на измерение, каждый из которых представляет вариацию в этом измерении:

>>> import numpy as np
>>> np.indices((3,3))
array([[[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2]],
       [[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2]]])

Это особенно полезно для вычисления функций нескольких измерений на регулярной сетке.

3) Репликация, объединение или изменение существующих массивов#

После создания массивов вы можете реплицировать, объединять или изменять эти существующие массивы для создания новых массивов. При присвоении массива или его элементов новой переменной необходимо явно numpy.copy массив, в противном случае переменная является представлением исходного массива. Рассмотрим следующий пример:

>>> import numpy as np
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> b = a[:2]
>>> b += 1
>>> print('a =', a, '; b =', b)
a = [2 3 3 4 5 6] ; b = [2 3]

В этом примере вы не создали новый массив. Вы создали переменную, b который просматривал первые 2 элемента a. Когда вы добавили 1 к b вы получите тот же результат, добавив 1 к a[:2]. Если вы хотите создать новый массив, используйте numpy.copy функция создания массива как таковая:

>>> import numpy as np
>>> a = np.array([1, 2, 3, 4])
>>> b = a[:2].copy()
>>> b += 1
>>> print('a = ', a, 'b = ', b)
a =  [1 2 3 4] b =  [2 3]

Для получения дополнительной информации и примеров смотрите Копии и представления.

Существует ряд процедур для объединения существующих массивов, например. numpy.vstack, numpy.hstack, и numpy.block. Вот пример объединения четырех массивов 2x2 в массив 4x4 с использованием block:

>>> import numpy as np
>>> A = np.ones((2, 2))
>>> B = np.eye(2, 2)
>>> C = np.zeros((2, 2))
>>> D = np.diag((-3, -4))
>>> np.block([[A, B], [C, D]])
array([[ 1.,  1.,  1.,  0.],
       [ 1.,  1.,  0.,  1.],
       [ 0.,  0., -3.,  0.],
       [ 0.,  0.,  0., -4.]])

Другие процедуры используют аналогичный синтаксис для объединения ndarrays. Проверьте документацию процедуры для дополнительных примеров и синтаксиса.

4) Чтение массивов с диска, либо из стандартных, либо из пользовательских форматов#

Это наиболее распространённый случай создания больших массивов. Детали сильно зависят от формата данных на диске. В этом разделе даются общие указания по обработке различных форматов. Более подробные примеры ввода-вывода можно найти в Как читать и записывать файлы.

Стандартные двоичные форматы#

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

HDF5: h5py
FITS: Astropy

Примеры форматов, которые нельзя прочитать напрямую, но которые несложно преобразовать, — это форматы, поддерживаемые библиотеками, такими как PIL (способные читать и записывать многие форматы изображений, такие как jpg, png и т.д.).

Общие форматы ASCII#

Разделительные файлы, такие как файлы с разделителями-запятыми (csv) и файлы с разделителями-табуляцией (tsv), используются в программах вроде Excel и LabView. Функции Python могут читать и разбирать эти файлы построчно. NumPy имеет две стандартные процедуры для импорта файла с разделительными данными numpy.loadtxt и numpy.genfromtxt. Эти функции имеют более сложные случаи использования в Чтение и запись файлов. Простой пример, учитывая simple.csv:

$ cat simple.csv
x, y
0, 0
1, 1
2, 4
3, 9

Импорт simple.csv осуществляется с помощью numpy.loadtxt:

>>> import numpy as np
>>> np.loadtxt('simple.csv', delimiter = ',', skiprows = 1) 
array([[0., 0.],
       [1., 1.],
       [2., 4.],
       [3., 9.]])

Более общие ASCII-файлы можно читать с помощью scipy.io и Pandas.

5) Создание массивов из сырых байтов с использованием строк или буферов#

Существует несколько подходов, которые можно использовать. Если файл имеет относительно простой формат, можно написать простую библиотеку ввода-вывода и использовать NumPy fromfile() функция и .tofile() метод для чтения и записи массивов NumPy напрямую (учитывайте порядок байтов!). Если существует хорошая библиотека на C или C++, которая читает данные, можно обернуть эту библиотеку с помощью различных техник, хотя это, конечно, требует гораздо больше работы и значительно более продвинутых знаний для взаимодействия с C или C++.

6) Использование специальных библиотечных функций (например, SciPy, pandas и OpenCV)#

NumPy — это фундаментальная библиотека для контейнеров массивов в стеке научных вычислений Python. Многие библиотеки Python, включая SciPy, Pandas и OpenCV, используют ndarrays NumPy в качестве общего формата для обмена данными. Эти библиотеки могут создавать, оперировать и работать с массивами NumPy.