Библиотека NumPy: основы работы с массивами в Python

Раздел: Python -> Библиотеки анализа данных

Основные возможности NumPy

Библиотека NumPy предоставляет эффективные структуры данных и функции для работы с многомерными массивами. Основным объектом является ndarray. Ниже рассматриваются ключевые операции.

Как создать массив NumPy?

Самый универсальный способ создания массива - функция np.array. Она преобразует список, кортеж или другой итерируемый объект в ndarray.

import numpy as np
a = np.array([1, 2, 3, 4, 5])
print(a)
print(type(a))

библиотека numpy python (библиотека numpy для python)

[1 2 3 4 5]
<class 'numpy.ndarray'>

библиотека pandas python (библиотека pandas для python)

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

Как создать массивы заранее определённой формы и заполнить их нулями, единицами или последовательностью чисел?

Для этого применяются специализированные функции:

  • np.zeros(shape) - массив из нулей.
  • np.ones(shape) - массив из единиц.
  • np.arange(start, stop, step) - равномерная последовательность целых чисел.
  • np.linspace(start, stop, num) - равномерно распределённые числа с заданным количеством точек.
zeros = np.zeros((2, 3))
ones = np.ones(4)
seq = np.arange(0, 10, 2)
space = np.linspace(0, 1, 5)
print('zeros:', zeros)
print('ones:', ones)
print('seq:', seq)
print('space:', space)
zeros: [[0. 0. 0.]
 [0. 0. 0.]]
ones: [1. 1. 1. 1.]
seq: [0 2 4 6 8]
space: [0.   0.25 0.5  0.75 1.  ]

Типичные ошибки: передача в np.array вложенных списков разной длины приводит к созданию одномерного массива объектов, а не многомерного. Для создания массива из 0 и 1 с целыми числами следует указать dtype=int.

# Ошибочная передача
bad = np.array([1, 2], [3, 4])  # SyntaxError - забыты скобки
good = np.array([[1, 2], [3, 4]])  # Правильно

Как выполнить индексацию и срезы?

Основной способ доступа к элементам - квадратные скобки с указанием индексов. Для многомерных массивов индексы перечисляются через запятую. Срезы работают по правилам Python.

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print('Элемент [1,2]:', arr[1, 2])
print('Первая строка:', arr[0, :])
print('Второй столбец:', arr[:, 1])
print('Подматрица 2x2:', arr[:2, :2])
Элемент [1,2]: 6
Первая строка: [1 2 3]
Второй столбец: [2 5 8]
Подматрица 2x2: [[1 2]
 [4 5]]

Как выбрать элементы по условию или произвольному набору индексов?

Булева индексация - создание маски: arr[arr > 5]. Fancy indexing - передача списка индексов: arr[[0, 2], [1, 2]].

arr = np.array([10, 20, 30, 40, 50])
mask = arr > 25
print('Булева маска:', mask)
print('Элементы > 25:', arr[mask])
indices = [0, 3, 4]
print('По индексам:', arr[indices])
Булева маска: [False False  True  True  True]
Элементы > 25: [30 40 50]
По индексам: [10 40 50]

Возможные проблемы: при булевой индексации массив должен иметь ту же форму, что и исходный, иначе возникает ValueError. Для fancy indexing индексы не должны выходить за границы.

Как выполнять арифметические операции?

Поэлементные операции (сложение, вычитание, умножение, деление) выполняются напрямую над массивами одинаковой формы или с использованием вещания (broadcasting).

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print('Сложение:', a + b)
print('Умножение на число:', a * 10)
print('Степень:', a ** 2)
Сложение: [5 7 9]
Умножение на число: [10 20 30]
Степень: [1 4 9]

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

Используются функции np.dot, np.matmul или оператор @.

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print('np.dot:', np.dot(A, B))
print('@:', A @ B)
np.dot: [[19 22]
 [43 50]]
@: [[19 22]
 [43 50]]

Ошибки вещания: если формы массивов несовместимы (например, (3,) и (4,)), возникает ValueError: operands could not be broadcast together. Всегда проверяйте размерности через .shape.

Как использовать универсальные функции (ufunc)?

Универсальные функции применяются поэлементно к массиву. Примеры: np.sin, np.cos, np.exp, np.sqrt.

x = np.linspace(0, np.pi, 3)
print('sin(x):', np.sin(x))
print('exp(x):', np.exp([0, 1, 2]))
sin(x): [0.0000000e+00 1.0000000e+00 1.2246468e-16]
exp(x): [1.         2.71828183 7.3890561 ]

Как создать собственную универсальную функцию?

Функция np.frompyfunc преобразует пользовательскую функцию в ufunc, работающую с массивами.

def custom(x):
    return x**2 + 1 if x > 0 else -x

ufunc_custom = np.frompyfunc(custom, 1, 1)
arr = np.array([-2, 0, 3])
print('Результат:', ufunc_custom(arr))
Результат: [2 0 10]

Однако для высокой производительности лучше использовать векторизованные выражения.

Проблема с типами: ufunc через frompyfunc возвращает массив объектов (dtype=object). Для числовых массивов лучше применять np.vectorize с указанием otypes.

Как решать задачи линейной алгебры?

Подмодуль np.linalg содержит функции для решения систем уравнений, вычисления собственных значений, сингулярного разложения и т.д.

# Решение Ax = b
A = np.array([[2, 1], [1, 1]])
b = np.array([5, 3])
x = np.linalg.solve(A, b)
print('Решение:', x)
print('Проверка:', A @ x)
Решение: [2. 1.]
Проверка: [5. 3.]

Как вычислить обратную матрицу или собственные значения?

Функции np.linalg.inv и np.linalg.eig.

M = np.array([[1, 2], [3, 4]])
invM = np.linalg.inv(M)
print('Обратная матрица:')
print(invM)
eigvals, eigvecs = np.linalg.eig(M)
print('Собственные значения:', eigvals)
print('Собственные векторы:')
print(eigvecs)
Обратная матрица:
[[-2.   1. ]
 [ 1.5 -0.5]]
Собственные значения: [-0.37228132  5.37228132]
Собственные векторы:
[[-0.82456484 -0.41597356]
 [ 0.56576746 -0.90937671]]

Ошибка: при попытке обратить сингулярную матрицу возникает np.linalg.LinAlgError. Всегда проверяйте определитель через np.linalg.det.

Как генерировать случайные числа?

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

# Равномерное распределение [0,1)
uniform = np.random.rand(3, 2)
print('Равномерное:')
print(uniform)
# Стандартное нормальное
normal = np.random.randn(4)
print('Нормальное:', normal)
Равномерное:
[[0.37454012 0.95071431]
 [0.73199394 0.59865848]
 [0.15601864 0.15599452]]
Нормальное: [ 0.05808361 -0.42428822 -1.04496123  0.50807724]

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

np.random.randint для целых, np.random.choice для выборки из массива. Для воспроизводимости устанавливается seed с помощью np.random.seed.

np.random.seed(42)
ints = np.random.randint(0, 10, size=5)
print('Целые:', ints)
sample = np.random.choice(['a', 'b', 'c'], size=3, p=[0.1, 0.2, 0.7])
print('Выборка:', sample)
Целые: [6 3 7 4 6]
Выборка: ['c' 'c' 'b']

Типичная ошибка: вызов np.random.seed внутри цикла сбрасывает генератор, что приводит к повторяющимся числам. Устанавливайте seed один раз в начале.

Как сохранять и загружать массивы?

Для бинарного сохранения в собственном формате NumPy используются np.save и np.load. Файл получает расширение .npy.

arr = np.array([[1, 2], [3, 4]])
np.save('matrix.npy', arr)
loaded = np.load('matrix.npy')
print('Загруженный массив:')
print(loaded)
Загруженный массив:
[[1 2]
 [3 4]]

Как сохранить массив в текстовый формат (CSV) и прочитать его обратно?

Функции np.savetxt и np.loadtxt.

np.savetxt('data.csv', arr, delimiter=',')
loaded_csv = np.loadtxt('data.csv', delimiter=',')
print('Из CSV:', loaded_csv)
Из CSV: [[1. 2.]
 [3. 4.]]

Проблемы: при загрузке текстового файла с пропущенными значениями или разными разделителями возникает ошибка. Рекомендуется использовать genfromtxt с параметрами missing_values.

Дополнительные расширенные примеры

Ниже приведены более сложные примеры использования NumPy, демонстрирующие векторизацию, вещание и продвинутую индексацию.

Пример 1: Векторизация вычислений с вещанием

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

Пример
import numpy as np

# Набор точек (1000 точек, 2 координаты)
points = np.random.randn(1000, 2)
center = np.array([0.5, 0.5])

# Вычисление квадратов разностей по всем осям
diff = points - center  # broadcasting (1000,2) - (2,) -> (1000,2)
sq_dist = np.sum(diff**2, axis=1)  # сумма вдоль осей координат
distances = np.sqrt(sq_dist)

print('Первые 5 расстояний:', distances[:5])
print('Среднее расстояние:', np.mean(distances))
Первые 5 расстояний: [2.04091869 2.08891257 1.25594823 2.36482933 1.74414421]
Среднее расстояние: 1.8107927643186705

Пример 2: Булева индексация и изменение значений

Используется для замены всех отрицательных элементов на ноль и подсчёта положительных.

Пример
matrix = np.random.randn(4, 4)
print('Исходная матрица:')
print(matrix)

# Маска отрицательных элементов
neg_mask = matrix < 0
print('Количество отрицательных:', np.sum(neg_mask))

# Замена отрицательных на 0
matrix[neg_mask] = 0
print('После замены:')
print(matrix)
Исходная матрица:
[[ 0.05391037 -0.68694083  0.07965616 -0.69308327]
 [ 0.30919091 -0.26241111 -0.47199352  0.41746327]
 [-1.0409562  -0.47912438 -0.41263476 -0.12011362]
 [-1.20713113  0.38424814 -0.28215343  1.11440759]]
Количество отрицательных: 10
После замены:
[[0.05391037 0.         0.07965616 0.        ]
 [0.30919091 0.         0.         0.41746327]
 [0.         0.         0.         0.        ]
 [0.         0.38424814 0.         1.11440759]]

Пример 3: Свертка одномерного сигнала с помощью np.convolve

Пример
signal = np.array([1, 2, 3, 4, 5])
kernel = np.array([0.2, 0.5, 0.3])

# Полная свертка
conv_full = np.convolve(signal, kernel, mode='full')
print('Полная свертка:', conv_full)

# Свертка с тем же размером (режим 'same')
conv_same = np.convolve(signal, kernel, mode='same')
print('Тот же размер:', conv_same)
Полная свертка: [0.2 0.9 1.9 3.  4.  3.1 1.5]
Тот же размер: [0.9 1.9 3.  4.  3.1]

Пример 4: Создание и работа с mask-массивами (np.ma)

Маскированные массивы позволяют игнорировать пропущенные или некорректные данные.

Пример
data = np.array([1.0, -999, 3.0, -999, 5.0])
masked = np.ma.masked_where(data == -999, data)
print('Маскированный массив:', masked)
print('Среднее без учета -999:', np.ma.mean(masked))

# Заполнение маскированных значений
filled = masked.filled(0)
print('Заполненный:', filled)
Маскированный массив: [1.0 -- 3.0 -- 5.0]
Среднее без учета -999: 3.0
Заполненный: [1. 0. 3. 0. 5.]

Пример 5: Использование np.einsum для сложных тензорных операций

Вычисление следа матричного произведения через суммирование с индексами.

Пример
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# Эквивалент np.trace(A @ B)
trace_AB = np.einsum('ij,jk->ik', A, B).diagonal().sum()
trace_einsum = np.einsum('ij,jk->ik', A, B).diagonal().sum()
print('Трасса (стандарт):', np.trace(A @ B))
print('Трасса (einsum):', np.einsum('ij,jk->ik', A, B).diagonal().sum())
Трасса (стандарт): 99
Трасса (einsum): 99

Библиотека NumPy для Python - comments

En
библиотека numpy python (python)