Работа с NumPy: эффективные вычисления и управление данными

Раздел: Библиотеки -> Научные вычисления

Введение в NumPy

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

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

Самый эффективный способ начать работу с NumPy - создать массив из списка с помощью np.array() и сразу применить поэлементные операции.

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

машинное обучение с помощью python (машинное обучение)

[ 3  5  7  9 11]

модуль numpy в python (модуль numpy в python)

Пояснение: np.array() преобразует список в массив. Арифметические операции применяются ко всем элементам одновременно. Это основной приём, который заменяет циклы в Python.

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

Для генерации последовательности чисел с фиксированным шагом используется np.arange() или np.linspace().

x = np.arange(0, 10, 2)   # от 0 до 9 с шагом 2
y = np.linspace(1, 5, 4)  # 4 точки от 1 до 5 включительно
print(x)
print(y)
[0 2 4 6 8]
[1.         2.33333333 3.66666667 5.        ]

Цель: быстро сформировать данные для осей графиков, временные ряды, интервалы.

Типичная ошибка:

Использование np.arange() с дробным шагом может привести к неожиданному количеству элементов из-за погрешности чисел с плавающей точкой. Рекомендуется применять np.linspace() для дробных интервалов, так как она гарантирует точное количество точек.

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

Функции np.zeros() и np.ones() создают массивы заданной формы с константными значениями.

zeros = np.zeros((2, 3))   # матрица 2x3 из нулей
ones = np.ones((4,))       # одномерный массив из четырёх единиц
print(zeros)
print(ones)
[[0. 0. 0.]
 [0. 0. 0.]]
[1. 1. 1. 1.]

Используется для инициализации весов в нейронных сетях, резервирования памяти, создания единичных матриц (с помощью np.eye()).

Проблема dtype:

По умолчанию np.zeros() и np.ones() создают массивы с типом float64. Для целочисленных массивов нужно указывать параметр dtype=int.

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

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

a = np.array([[1,2,3],[4,5,6]])  # 2x3
b = np.array([10,20,30])          # 1x3
c = a + b
print(c)
[[11 22 33]
 [14 25 36]]

Пояснение: массив b 'растягивается' по строкам. Broadcasting применяется, когда размеры совпадают по одному из измерений или одно из измерений равно 1. Это экономит память и упрощает код.

Ошибка несовместимости форм:

Если формы не подходят для broadcasting (например, (3,) и (4,)), возникает исключение ValueError: operands could not be broadcast together. Нужно проверять размеры или явно изменять форму через reshape().

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

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

arr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(arr[1, 2])        # элемент второй строки, третьего столбца
print(arr[:2, 1:3])     # первые две строки, столбцы 1 и 2
print(arr[arr % 2 == 0])# все чётные элементы
7
[[2 3]
 [6 7]]
[ 2  4  6  8 10 12]

Цель: выборка строк/столбцов для анализа, фильтрация данных, создание подвыборок.

Нюанс с копированием:

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

Расширенные примеры работы с NumPy

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

Использование случайных чисел для симуляции

Генерация случайных матриц с помощью np.random.

Пример
import numpy as np
# Массив 3x4 из равномерного распределения [0,1)
rand_uniform = np.random.rand(3, 4)
# Массив из стандартного нормального распределения
rand_normal = np.random.randn(5)
# Целочисленные случайные числа от 0 до 10
rand_int = np.random.randint(0, 10, size=(2,3))
print('Uniform:\n', rand_uniform)
print('Normal:\n', rand_normal)
print('Integer:\n', rand_int)
Uniform:
 [[0.123 0.456 0.789 0.012]
 [0.345 0.678 0.901 0.234]
 [0.567 0.890 0.111 0.222]]
Normal:
 [-0.45  1.23 -0.67  0.89  2.10]
Integer:
 [[3 7 2]
 [8 1 5]]

Применение: Монте-Карло симуляции, инициализация параметров моделей, генерация тестовых данных.

Матричные операции и линейная алгебра

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

Пример
A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])
# Произведение матриц
C = np.dot(A, B)          # или A @ B
print('A @ B =\n', C)
# Определитель
det = np.linalg.det(A)
print('det(A) =', det)
# Собственные значения и векторы
eigenvals, eigenvecs = np.linalg.eig(A)
print('Eigenvalues:', eigenvals)
A @ B =
 [[19 22]
 [43 50]]
det(A) = -2.0
Eigenvalues: [-0.37228132  5.37228132]

Пояснение: np.linalg предоставляет функции для решения систем уравнений, сингулярного разложения и др.

Проблема точности:

Вычисления с плавающей точкой могут давать маленькие ошибки (например, определитель, близкий к нулю, вместо точного нуля). Для критичных задач рекомендуется использовать модуль np.linalg.svd для устойчивости.

Условная замена элементов с помощью np.where

Функция np.where позволяет заменить значения по условию без циклов.

Пример
arr = np.array([1, -2, 3, -4, 5])
# Заменить отрицательные на 0
result = np.where(arr < 0, 0, arr)
print(result)
# Также возвращает индексы элементов, удовлетворяющих условию
indices = np.where(arr < 0)[0]
print('Indices of negatives:', indices)
[1 0 3 0 5]
Indices of negatives: [1 3]

Используется для обработки выбросов, нормализации и фильтрации.

Эффективная статистика вдоль осей

Методы sum, mean, std, min, max с параметром axis.

Пример
data = np.random.randint(0, 100, size=(3, 5))
print('Data:\n', data)
print('Среднее по строкам:', data.mean(axis=1))
print('Сумма по столбцам:', data.sum(axis=0))
print('Максимум в каждой строке:', data.max(axis=1))
Data:
 [[12 45  7 88 23]
 [34 56 78  9 11]
 [90  2 45 67 33]]
Среднее по строкам: [35.  37.6 47.4]
Сумма по столбцам: [136 103 130 164  67]
Максимум в каждой строке: [88 78 90]

Агрегации по осям - ключевой приём в анализе табличных данных.

Векторизация пользовательских функций с np.vectorize

Если требуется применить собственную функцию к каждому элементу, np.vectorize создает векторизованную версию.

Пример
def my_func(x):
    if x > 0:
        return x ** 2
    else:
        return -x

vfunc = np.vectorize(my_func)
arr = np.array([-3, 0, 2, -1, 5])
print(vfunc(arr))
[3 0 4 1 25]

Пояснение: np.vectorize не ускоряет вычисления (он всё равно вызывает Python-функцию для каждого элемента), но упрощает синтаксис. Для производительности следует переписать функцию с использованием numpy-операций.

Неэффективность vectorize:

Для больших массивов лучше использовать np.where или булеву индексацию, так как они работают на скорости C.

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

Сортировка массивов и поиск уникальных элементов.

Пример
arr = np.array([3, 1, 2, 3, 2, 1])
print('Sorted:', np.sort(arr))
print('Unique:', np.unique(arr))
# Сортировка по строкам матрицы
mat = np.array([[2,1],[3,0]])
print('Sort rows:', np.sort(mat, axis=1))
Sorted: [1 1 2 2 3 3]
Unique: [1 2 3]
Sort rows: [[1 2]
 [0 3]]

Применение: подготовка данных к визуализации, удаление дубликатов, обработка временных рядов.

Работа с нулевыми значениями (NaN)

В реальных данных часто встречаются пропуски. np.nan и функции np.nanmean, np.nanstd игнорируют их.

Пример
arr = np.array([1.0, np.nan, 3.0, np.nan, 5.0])
print('Mean ignoring NaN:', np.nanmean(arr))
print('Std ignoring NaN:', np.nanstd(arr))
# Замена NaN на среднее значение
mean_val = np.nanmean(arr)
arr_filled = np.where(np.isnan(arr), mean_val, arr)
print('Filled array:', arr_filled)
Mean ignoring NaN: 3.0
Std ignoring NaN: 1.632993161855452
Filled array: [1. 3. 3. 3. 5.]

Обработка пропусков - обязательный этап предварительной обработки данных.

Модуль NumPy в Python - comments

En
модуль numpy в python (python)