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

NumPy предоставляет десятки способов создания массивов ndarray — основной структуры данных библиотеки. Правильный выбор функции создания экономит память, ускоряет работу и делает код выразительным.

В этой статье мы рассмотрим базовые функции: преобразование из Python-списков, создание заполненных нулями/единицами массивов, генерация числовых последовательностей и работу с типами данных dtype.

Зачем нужно

Когда вы пишете программу для обработки данных с Arduino (например, измерений температуры за час), удобнее работать не с обычным Python-списком, а с массивом NumPy. Причины:

  • Скорость. Операции над ndarray написаны на C и выполняются в десятки раз быстрее цикла for.

  • Память. Массив фиксированного типа хранится компактно, без накладных расходов на объекты Python.

  • Удобство. Векторные операции (a + b, a * 2) применяются ко всем элементам сразу.

Пример 1. Создание из Python-списка

Самый простой способ — преобразовать список или вложенный список через np.array.

import numpy as np

# Температура с датчика DS18B20 за 5 секунд
temps = np.array([23.1, 23.4, 23.5, 23.3, 23.6])
print(temps)
print(temps.dtype, temps.shape)
[23.1 23.4 23.5 23.3 23.6]
float64 (5,)

Атрибут dtype показывает тип элементов, shape — форму массива (одномерный из 5 элементов).

Пример 2. Двумерный массив (матрица)

Передаём вложенный список — получаем матрицу. Удобно для табличных данных.

import numpy as np

# 3 датчика, 4 измерения
data = np.array([
    [20.1, 20.2, 20.0, 20.3],
    [21.5, 21.4, 21.6, 21.5],
    [19.8, 19.9, 19.7, 19.8],
])
print(data.shape)
print(data)
(3, 4)
[[20.1 20.2 20.  20.3]
 [21.5 21.4 21.6 21.5]
 [19.8 19.9 19.7 19.8]]

Пример 3. np.zeros и np.ones

Часто нужно создать массив-«заготовку», заполненный нулями или единицами, чтобы потом записать в него данные.

import numpy as np

buffer = np.zeros(8)            # буфер на 8 значений
mask = np.ones((2, 3), dtype=int)  # матрица 2x3 из единиц, тип int
print(buffer)
print(mask)
[0. 0. 0. 0. 0. 0. 0. 0.]
[[1 1 1]
 [1 1 1]]

По умолчанию np.zeros создаёт массив типа float64. Если нужны целые числа — указывайте dtype=int.

Пример 4. np.arange — диапазон с шагом

Аналог встроенной функции range, но возвращает массив. Полезен для генерации индексов и временных шкал.

import numpy as np

indices = np.arange(0, 10, 2)   # от 0 до 10 с шагом 2
t_ms = np.arange(0, 1000, 100)  # моменты времени в мс
print(indices)
print(t_ms)
[0 2 4 6 8]
[  0 100 200 300 400 500 600 700 800 900]

Шаг может быть дробным, в отличие от range: np.arange(0, 1, 0.25) даёт [0. 0.25 0.5 0.75].

Пример 5. np.linspace — равномерное разбиение

В отличие от arange, linspace принимает не шаг, а количество точек. Удобно для построения графиков и калибровочных кривых.

import numpy as np

# 5 уровней ШИМ от 0 до 255
pwm = np.linspace(0, 255, 5)
print(pwm)
[  0.    63.75 127.5  191.25 255.  ]

По умолчанию конечная точка включена. Чтобы исключить — передайте endpoint=False.

Пример 6. np.full — заполнение константой

Создаёт массив заданной формы, заполненный одним значением.

import numpy as np

default_pwm = np.full((4,), 128, dtype=np.uint8)
print(default_pwm)
print(default_pwm.dtype)
[128 128 128 128]
uint8

Тип uint8 (0–255) идеально подходит для ШИМ-значений Arduino — экономит память по сравнению с int64.

Пример 7. np.empty — без инициализации

Самый быстрый способ создать массив, но содержимое — «мусор» из памяти.

import numpy as np

buf = np.empty(5)
print(buf)  # значения непредсказуемые
[6.9e-310 6.9e-310 0.0e+000 0.0e+000 0.0e+000]

Используйте, только когда сразу же перезаписываете все элементы — иначе получите случайные числа.

Пример 8. Случайные массивы и единичная матрица

import numpy as np

rng = np.random.default_rng(seed=42)
noise = rng.normal(0, 1, size=5)    # гауссов шум
eye3 = np.eye(3)                    # единичная матрица 3x3
print(noise)
print(eye3)
[ 0.30471708 -1.03998411  0.7504512   0.94056472 -1.95103519]
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

Подводные камни

  • Смешанные типы. np.array([1, 2.0, 3]) даст float64 — NumPy приведёт всё к общему типу. Если в списке окажется строка, тип станет <U… и арифметика сломается.

  • arange и float-шаг. Из-за погрешностей плавающей точки np.arange(0, 1, 0.1) может дать 10 или 11 элементов. Для дробных шагов предпочтительнее np.linspace.

  • empty != zeros. Новички ожидают, что np.empty вернёт нули. Это не так — память не инициализируется.

  • Копия или вид. np.array(other_array) по умолчанию создаёт копию. np.asarray — нет, если тип совпадает. Это важно при работе с большими данными.

См. также

Примечание

Лицензия и источники

Техническое описание адаптировано из официальной документации NumPy (https://numpy.org/doc/stable/), доступной под BSD-3-Clause License. Перевод на русский, примеры и пояснения — © AlashEd Wiki.