Массивы NumPy: инструменты для научных вычислений

Раздел: Машинное обучение -> numpy

Основные понятия и работа с массивами NumPy

Массив NumPy (numpy.ndarray) представляет собой многомерный контейнер для однотипных данных. В отличие от списков Python, массивы NumPy обеспечивают векторные операции, эффективное использование памяти и высокую скорость вычислений. Основное решение для создания массива – функция np.array(). Она принимает итерируемый объект (список, кортеж) и возвращает массив.

import numpy as np

# Одномерный массив
arr1d = np.array([1, 2, 3, 4, 5])
print(arr1d)  # [1 2 3 4 5]

# Двумерный массив (матрица)
arr2d = np.array([[1, 2], [3, 4], [5, 6]])
print(arr2d)
# [[1 2]
#  [3 4]
#  [5 6]]

# Атрибуты массива
print(f"Форма: {arr2d.shape}, Размерность: {arr2d.ndim}, Число элементов: {arr2d.size}, Тип данных: {arr2d.dtype}")
# Форма: (3, 2), Размерность: 2, Число элементов: 6, Тип данных: int64

Python numpy array (массив numpy в python)

Индексация и срезы: доступ к элементам осуществляется как в обычных списках, но для многомерных массивов используется синтаксис с запятыми.

arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
print(arr[0, 1])   # 20
print(arr[:, 1])   # [20 50 80]  (второй столбец)
print(arr[1:, :2]) # [[40 50]
                   #  [70 80]]

Типичная ошибка: передача списка с элементами разных типов приводит к автоматическому приведению к наиболее общему типу (например, int и float → float). Чтобы избежать неожиданностей, следует явно задавать dtype.

arr_mixed = np.array([1, 2.5, 3])  # массив будет float64
print(arr_mixed.dtype)  # float64

Как создать массив, состоящий только из нулей?

Функция np.zeros(shape, dtype=float) создаёт массив заданной формы, заполненный нулями. Часто используется для резервирования памяти или инициализации переменных.

zeros = np.zeros((3, 4), dtype=int)
print(zeros)
# [[0 0 0 0]
#  [0 0 0 0]
#  [0 0 0 0]]

Проблема: если передать отрицательный размер, возникает ошибка ValueError. Также важно помнить, что np.zeros возвращает новый массив, а не ссылку на существующий.

Как получить массив, заполненный единицами?

np.ones(shape, dtype=None) работает аналогично np.zeros, но заполняет массив единицами. Применяется для создания масок или начальных значений в алгоритмах.

ones = np.ones((2, 3))
print(ones)
# [[1. 1. 1.]
#  [1. 1. 1.]]

Ошибка: если не указать dtype, по умолчанию будет float64. Для целочисленных единиц нужно явно указать dtype=int.

Как заполнить массив произвольным числом?

np.full(shape, fill_value, dtype=None) создаёт массив, все элементы которого равны заданному значению fill_value.

full_arr = np.full((2, 2), 3.14, dtype=float)
print(full_arr)
# [[3.14 3.14]
#  [3.14 3.14]]

Как создать последовательность чисел с постоянным шагом?

np.arange(start, stop, step, dtype=None) возвращает одномерный массив равномерно расположенных значений от start (включительно) до stop (исключительно) с шагом step. Аналог встроенной range(), но для массивов.

arr = np.arange(0, 10, 2)  # [0 2 4 6 8]
print(arr)

# С плавающей точкой
arr_float = np.arange(0.0, 1.0, 0.2)
print(arr_float)  # [0.  0.2 0.4 0.6 0.8]

Типичная ошибка: при использовании дробного шага возможны проблемы с округлением. Например, np.arange(0, 1, 0.1) может выдать массив с 10 элементами, но иногда из-за погрешностей число элементов оказывается меньше. Рекомендуется использовать np.linspace для контролируемого количества точек.

Как создать последовательность с заданным количеством точек?

np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None) создаёт массив из num равноотстоящих значений от start до stop (включая stop, если endpoint=True). Полезно для построения графиков или равномерной дискретизации.

lin = np.linspace(0, 1, 5)  # [0.   0.25 0.5  0.75 1.  ]
print(lin)

# Если endpoint=False, stop не включается
lin2 = np.linspace(0, 1, 5, endpoint=False)  # [0.  0.2 0.4 0.6 0.8]
print(lin2)

Внимание: при retstep=True функция возвращает кортеж (массив, шаг). Это удобно, если нужно знать расстояние между точками.

Как создать единичную матрицу?

np.eye(N, M=None, k=0, dtype=float) возвращает двумерный массив с единицами на главной диагонали и нулями в остальных позициях. Параметр k позволяет сдвинуть диагональ.

eye = np.eye(3, dtype=int)
print(eye)
# [[1 0 0]
#  [0 1 0]
#  [0 0 1]]

# Сдвинутая диагональ
eye_shift = np.eye(4, k=1, dtype=int)
print(eye_shift)
# [[0 1 0 0]
#  [0 0 1 0]
#  [0 0 0 1]
#  [0 0 0 0]]

Как создать массив со случайными числами?

NumPy предоставляет несколько функций в модуле np.random:

  • np.random.rand(d0, d1, ..., dn) – равномерное распределение на [0, 1).
  • np.random.randn(d0, d1, ..., dn) – стандартное нормальное распределение.
  • np.random.randint(low, high=None, size=None, dtype=int) – случайные целые числа из диапазона.
# Равномерное распределение
uniform = np.random.rand(2, 3)
print(uniform)
# [[0.5488 0.7152 0.6028]
#  [0.5449 0.4237 0.6459]]

# Нормальное распределение
normal = np.random.randn(3)
print(normal)  # [-0.5187  0.7264 -1.3015]

# Случайные целые
ints = np.random.randint(0, 10, size=(2, 2))
print(ints)
# [[9 3]
#  [4 7]]

Ошибка: если не указать size в randint, возвращается одно число, а не массив. Для воспроизводимости результатов необходимо установить seed: np.random.seed(42).

Общие проблемы и их решения

Проблема копирования vs просмотра (view): срезы массива возвращают ссылку на исходные данные (view), а не копию. Изменение view меняет исходный массив. Для получения независимой копии используйте .copy().

arr = np.array([1, 2, 3, 4])
view = arr[1:3]
view[0] = 99
print(arr)  # [ 1 99  3  4]  – исходный изменился!

# Копия
copy = arr[1:3].copy()
copy[0] = 0
print(arr)  # [ 1 99  3  4]  – без изменений

Изменение типа данных: arr.astype(new_dtype) создаёт копию с указанным типом. Нельзя изменить тип на месте.

float_arr = np.array([1.5, 2.7])
int_arr = float_arr.astype(int)
print(int_arr)  # [1 2]  – дробная часть отбрасывается

Ошибки совместимости форм (broadcasting): при арифметических операциях формы должны совпадать или быть расширяемыми. Если форма несовместима, возникает ValueError.

a = np.ones((3, 2))
b = np.ones((2,))   # можно расширить до (1,2), а потом до (3,2) – работает
c = a + b
print(c.shape)  # (3, 2)

# Ошибка
b_bad = np.ones((3,))
# a + b_bad  # ValueError: operands could not be broadcast together

Индексация вне границ: приводит к IndexError. Всегда проверяйте размеры по атрибутам shape.

Продвинутые примеры использования массивов NumPy

В этом разделе представлены расширенные операции с массивами: преобразование формы, объединение, разделение, универсальные функции, индексация и работа с файлами.

Изменение формы массива (reshape)

reshape(new_shape) возвращает новый вид на те же данные с другой формой. Должен сохраняться общий размер элементов.

Пример
arr = np.arange(12)
reshaped = arr.reshape(3, 4)
print(reshaped)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

# Ошибка: размер не совпадает
# arr.reshape(3, 5)  # ValueError
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Объединение массивов (concatenate, vstack, hstack)

Функции np.concatenate, np.vstack (вертикально) и np.hstack (горизонтально) объединяют несколько массивов.

Пример
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])

# Вертикальное объединение (строки)
vert = np.vstack((a, b))
print(vert)
# [[1 2]
#  [3 4]
#  [5 6]]

# Горизонтальное объединение (столбцы)
c = np.array([[7], [8]])
horiz = np.hstack((a, c))
print(horiz)
# [[1 2 7]
#  [3 4 8]]

# concatenate с указанием оси
con = np.concatenate((a, b), axis=0)  # то же, что vstack
print(con)
[[1 2]
 [3 4]
 [5 6]]
[[1 2 7]
 [3 4 8]]
[[1 2]
 [3 4]
 [5 6]]

Разделение массивов (split, vsplit, hsplit)

np.split(ary, indices_or_sections, axis=0) делит массив на несколько подмассивов.

Пример
arr = np.arange(10)
parts = np.split(arr, [2, 5])  # разделить на позициях 2 и 5
print(parts)
# [array([0, 1]), array([2, 3, 4]), array([5, 6, 7, 8, 9])]
[array([0, 1]), array([2, 3, 4]), array([5, 6, 7, 8, 9])]

Универсальные функции (ufunc) и операции по оси

Многие математические операции (sum, mean, max, min) можно выполнять вдоль заданной оси.

Пример
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print("Сумма по строкам (axis=1):", matrix.sum(axis=1))  # [ 6 15]
print("Среднее по столбцам (axis=0):", matrix.mean(axis=0))  # [2.5 3.5 4.5]
print("Максимум по всем элементам:", matrix.max())  # 6
Сумма по строкам (axis=1): [ 6 15]
Среднее по столбцам (axis=0): [2.5 3.5 4.5]
Максимум по всем элементам: 6

Булева индексация

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

Пример
arr = np.array([10, 20, 30, 40, 50])
mask = arr > 25
print("Маска:", mask)  # [False False  True  True  True]
print("Выбранные элементы:", arr[mask])  # [30 40 50]

# Условная замена
arr[arr < 30] = 0
print(arr)  # [ 0  0 30 40 50]
Маска: [False False  True  True  True]
Выбранные элементы: [30 40 50]
[ 0  0 30 40 50]

Fancy indexing (индексация по целочисленным массивам)

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

Пример
arr = np.array([10, 20, 30, 40, 50])
indices = np.array([1, 3, 4])
print(arr[indices])  # [20 40 50]

# Многомерная fancy indexing
matrix = np.arange(12).reshape(3, 4)
rows = np.array([0, 2])
cols = np.array([1, 3])
print(matrix[rows, cols])  # [1 11]  (элементы (0,1) и (2,3))
[20 40 50]
[ 1 11]

Использование np.where

np.where(condition, x, y) возвращает элементы из x, если условие истинно, иначе из y. Если задан только condition, возвращает индексы истинных элементов.

Пример
arr = np.array([1, -2, 3, -4, 5])
result = np.where(arr > 0, arr, 0)
print(result)  # [1 0 3 0 5]

# Только индексы
indices = np.where(arr > 0)
print(indices)  # (array([0, 2, 4]),)
[1 0 3 0 5]
(array([0, 2, 4]),)

Сортировка и уникальные значения

np.sort(arr) возвращает отсортированную копию, np.unique() – отсортированные уникальные элементы.

Пример
arr = np.array([3, 1, 2, 3, 2, 1])
print("Отсортированный:", np.sort(arr))  # [1 1 2 2 3 3]
print("Уникальные:", np.unique(arr))     # [1 2 3]
Отсортированный: [1 1 2 2 3 3]
Уникальные: [1 2 3]

Сохранение и загрузка массивов

NumPy поддерживает собственный бинарный формат (.npy) и текстовый (.csv через np.savetxt).

Пример
arr = np.array([[1.5, 2.3], [3.7, 4.1]])
# Бинарный формат
np.save('my_array.npy', arr)
loaded = np.load('my_array.npy')
print(loaded)

# Текстовый формат
np.savetxt('my_array.csv', arr, delimiter=',', fmt='%.2f')
loaded_csv = np.loadtxt('my_array.csv', delimiter=',')
print(loaded_csv)
[[1.5 2.3]
 [3.7 4.1]]
[[1.5 2.3]
 [3.7 4.1]]

Использование broadcasting с разными размерностями

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

Пример
a = np.array([1, 2, 3])          # shape (3,)
b = np.array([[0], [10], [20]])  # shape (3,1)
result = a + b
print(result)
# [[ 1  2  3]
#  [11 12 13]
#  [21 22 23]]
[[ 1  2  3]
 [11 12 13]
 [21 22 23]]

Примечание: правила broadcasting: размерности сравниваются с конца; если размерность не совпадает и не равна 1, возникает ошибка.

Массив NumPy в Python - comments

En
Python numpy array (python)