NumPy – одна из самых популярных библиотек для инженерных и научных вычислений в Python: она содержит множество методов для работы с многомерными массивами и матрицами и отличается высокой скоростью обработки больших объемов данных. Мощная функциональность NumPy используется в нескольких других популярных библиотеках – Pandas, SciPy, Matplotlib, Scikit-learn и TensorFlow.
Основные сферы применения
NumPy используется везде, где нужна быстрая обработка больших массивов данных:
- Инженерные и научные вычисления в области физики, математики, биологии, химии и т.д.
- Обработка изображений и звука – изменение размера, применение фильтров, сжатие и т.п.
- Машинное обучение, искусственный интеллект и Data Science – обработка и статистический анализ данных.
- Компьютерное зрение – распознавание образов и сегментация изображений.
- Финансовые вычисления – расчет доходности активов и инвестиций, риск-менеджмент, оценка дисконтированной стоимости и т.д.
- Геофизические и геологические исследования – обработка геоданных и анализ графиков.
Массив ndarray
NumPy может работать с данными из списков, кортежей и словарей, однако основная цель библиотеки – предоставление мощного инструментария для работы с многомерными массивами и матрицами. Все, что можно сделать с массивами (сортировку, фильтрацию, изменение формы, все виды статистического анализа, любые вычисления и т.п.), NumPy делает максимально просто и эффективно.
Массивы NumPy используют меньше памяти и работают быстрее, чем обычные списки Python: инструменты библиотеки, для которых критична скорость, реализованы на C/C++. Кроме того, NumPy использует собственный тип данных, который называется ndarray (n-размерным массивом). Сравним быстродействие обычного Python кода и метода NumPy на примере сложения двух матриц 1000 х 1000:
import numpy as np
import time
# Определяем две матрицы размером 1000x1000 со случайными целыми числами от 0 до 9
A = np.random.randint(0, 10, size=(1000, 1000))
B = np.random.randint(0, 10, size=(1000, 1000))
# Сложение матриц средствами Python
start_time = time.time()
C = [[A[i][j] + B[i][j] for j in range(len(A[0]))] for i in range(len(A))]
end_time = time.time()
# Сложение матриц с помощью NumPy
start_time_np = time.time()
D = A + B
end_time_np = time.time()
# Выводим результаты и время выполнения
print(f"Время выполнения Python сложения: {end_time - start_time} сек")
print(f"Время выполнения NumPy сложения: {end_time_np - start_time_np} сек")
# Сравниваем результаты
print("Результаты совпадают:", np.array_equal(C, D))
Результат:
Время выполнения Python сложения: 1.432081937789917 сек
Время выполнения NumPy сложения: 0.004000186920166016 сек
Результаты совпадают: True
Ключевые особенности ndarray:
- Однородность. Все элементы массива относятся к одному типу данных. Это позволяет сделать все операции с ndarray быстрыми и экономичными.
- Многомерность. Массивы NumPy могут иметь любое количество измерений. С помощью одномерного массива можно представить список значений, двумерного – таблицу, трехмерного – изображение, четырехмерного – видео.
- Фиксированная размерность. Размерность массива нельзя изменить после его создания. Если нужен массив другой размерности, необходимо создать новый.
- Эффективность. Массивы NumPy поддерживают проведение множества операций над всеми элементами одновременно, что делает их более эффективными, чем обычные списки Python.
- Удобные методы. NumPy предоставляет обширный выбор методов для всевозможных манипуляций над массивами – от сортировки и фильтрации до изменения формы.
Как создать массив в NumPy
NumPy предоставляет несколько способов создания массивов, вот основные:
import numpy as np
# Создание массива из списка
a = np.array([1, 2, 3, 4, 5])
print(a)
# Создание двумерного массива из списка списков
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(b)
# Создание массива из диапазона значений
c = np.arange(0, 10, 2) # Создаем массив из значений от 0 до 10 с шагом 2
print(c)
# Создание массива из равномерно распределенных значений
d = np.linspace(0, 1, 5) # Создаем массив из 5 равномерно распределенных значений от 0 до 1
print(d)
# Создание массива из случайных значений
e = np.random.rand(3, 3) # Создаем массив из 3x3 случайных значений
print(e)
# Создание массива из нулей
f = np.zeros((2, 3)) # Создаем массив из нулей размером 2x3
print(f)
# Создание массива из единиц
g = np.ones((3, 3)) # Создаем массив из единиц размером 3x3
print(g)
Результат:
[1 2 3 4 5]
[[1 2 3]
[4 5 6]
[7 8 9]]
[0 2 4 6 8]
[0. 0.25 0.5 0.75 1. ]
[[0.66777626 0.88439902 0.22572609]
[0.09556062 0.40293789 0.58901436]
[0.33380993 0.20922535 0.42657995]]
[[0. 0. 0.]
[0. 0. 0.]]
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
Основные операции с массивами
Продемонстрируем базовые операции на примере двумерного массива:
import numpy as np
# Создаем двумерный массив
arr = np.array([[1, 2], [3, 4], [5, 6]])
# Выводим массив на экран
print(arr)
# Получаем размерность массива
print(arr.shape)
# Получаем элементы массива
print(arr[0, 1]) # Выведет 2
print(arr[2, 0]) # Выведет 5
# Изменяем элементы массива
arr[0, 1] = 7
arr[1, 1] = 8
arr[2, 1] = 9
print(arr)
# Выводим строку
print(arr[1]) # Выведет [3 8]
# Выводим столбец
print(arr[:, 0]) # Выведет [1 3 5]
# Выводим срез
print(arr[:2, 1]) # Выведет [7 8]
Результат:
[[1 2]
[3 4]
[5 6]]
(3, 2)
2
5
[[1 7]
[3 8]
[5 9]]
[3 8]
[1 3 5]
[7 8]
Методы NumPy позволяют легко проводить любые манипуляции над массивами и получать всевозможную статистическую информацию о них:
import numpy as np
# Создаем два массива размером 6x6 со случайными целыми числами от 0 до 9
a = np.random.randint(1, 10, size=(6, 6))
b = np.random.randint(1, 10, size=(6, 6))
# Выводим массивы на экран
print("Массив a:\n", a)
print("Массив b:\n", b)
# Вычитаем массивы
d = a - b
print("Разность массивов a и b:\n", d)
# Умножение массивов
m = np.dot(a, b)
print("Произведение массивов a и b:\n", m)
# Деление массивов (умножение на обратную матрицу)
# Вычисляем обратную матрицу b
b_inv = np.linalg.inv(b)
# Умножаем матрицу a на обратную матрицу b_inv
result = np.dot(a, b_inv)
print("Результат деления массива a на b:\n", result)
# Транспонируем массивы
h = np.transpose(a)
i = np.transpose(b)
print("Массив a после транспонирования:\n", h)
print("Массив b после транспонирования:\n", i)
# Получаем статистические параметры массивов
std_a = np.std(a) # Стандартное отклонение массива a
median_b = np.median(b) # Медиана массива b
variance_a = np.var(a) # Дисперсия массива a
min_b = np.min(b) # Минимальное значение массива b
max_a = np.max(a) # Максимальное значение массива a
sum_b = np.sum(b) # Сумма элементов массива b
product_a = np.product(a, dtype=np.uint64) # Произведение элементов массива a
prod_b = np.prod(b, dtype=np.uint64) # Еще один метод вычисления произведения элементов
print("Стандартное отклонение массива a:", std_a)
print("Медиана массива b:", median_b)
print("Дисперсия массива a:", variance_a)
print("Минимальное значение массива b:", min_b)
print("Максимальное значение массива a:", max_a)
print("Сумма элементов массива b:", sum_b)
print("Произведение элементов массива a:", product_a)
print("Произведение элементов массива b:", prod_b)
Результат:
Массив a:
[[2 2 3 9 8 2]
[4 7 6 6 1 5]
[6 8 5 7 6 7]
[5 3 2 6 5 5]
[8 5 3 7 3 3]
[5 8 2 6 1 1]]
Массив b:
[[4 1 6 4 9 4]
[6 8 2 2 3 4]
[2 4 9 9 7 6]
[9 4 2 9 2 3]
[5 8 4 7 9 8]
[2 7 4 6 9 9]]
Разность массивов a и b:
[[-2 1 -3 5 -1 -2]
[-2 -1 4 4 -2 1]
[ 4 4 -4 -2 -1 1]
[-4 -1 0 -3 3 2]
[ 3 -3 -1 0 -6 -5]
[ 3 1 -2 0 -8 -8]]
Произведение массивов a и b:
[[151 144 101 188 153 143]
[139 151 128 175 165 151]
[189 215 163 232 244 218]
[131 136 106 163 170 147]
[152 133 123 171 176 142]
[133 116 84 121 113 99]]
Результат деления массива a на b:
[[-5.55598908e-01 -1.94654702e+00 4.39719079e-01 -6.62504877e-01
5.72922357e+00 -3.83066719e+00]
[-2.90479906e-01 1.16172454e+00 8.37690207e-01 3.86071011e-01
-1.83749512e+00 1.11451424e+00]
[ 4.77955521e-03 6.06808428e-01 2.70971518e-01 2.91162700e-01
-3.96898166e-01 5.81057355e-01]
[ 3.19791260e-01 -5.26238783e-02 -2.67752634e-01 6.75039017e-01
-7.08788529e-01 1.02033750e+00]
[ 5.16972298e-02 2.57315646e-01 1.34998049e-01 5.37065938e-01
4.00897386e-01 -4.29379633e-01]
[-1.07915529e+00 -1.51531409e-01 7.47073742e-01 -5.26092470e-01
3.97107881e+00 -3.19444986e+00]]
Массив a после транспонирования:
[[2 4 6 5 8 5]
[2 7 8 3 5 8]
[3 6 5 2 3 2]
[9 6 7 6 7 6]
[8 1 6 5 3 1]
[2 5 7 5 3 1]]
Массив b после транспонирования:
[[4 6 2 9 5 2]
[1 8 4 4 8 7]
[6 2 9 2 4 4]
[4 2 9 9 7 6]
[9 3 7 2 9 9]
[4 4 6 3 8 9]]
Стандартное отклонение массива a: 2.27438772116208
Медиана массива b: 5.5
Дисперсия массива a: 5.172839506172839
Минимальное значение массива b: 1
Максимальное значение массива a: 9
Сумма элементов массива b: 197
Произведение элементов массива a: 18210394348833472512
Произведение элементов массива b: 14758432146536267776
Полезные методы NumPy
reshape() – меняет форму массива без изменения данных:
import numpy as np
# создаем массив размером 3x4
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# используем метод reshape() для изменения формы массива на 2x6
new_arr = arr.reshape(2, 6)
print("Исходный массив:")
print(arr)
print("Массив после изменения формы:")
print(new_arr)
Результат:
Исходный массив:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
Массив после изменения формы:
[[ 1 2 3 4 5 6]
[ 7 8 9 10 11 12]]
concatenate() – объединяет массивы по строкам или по столбцам:
import numpy as np
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.array([[9, 10], [11, 12]])
#объединяем массивы по горизонтальной оси (по строкам)
d = np.concatenate((a, b, c), axis=1)
print(d)
#объединяем массивы по вертикальной оси (по столбцам)
e = np.concatenate((a, b, c), axis=0)
print(e)
Результат:
[[ 1 2 5 6 9 10]
[ 3 4 7 8 11 12]]
[[ 1 2]
[ 3 4]
[ 5 6]
[ 7 8]
[ 9 10]
[11 12]]
flatten() – превращает многомерный массив в одномерный:
import numpy as np
a = np.array([[1, 2, 9, 8], [7, 5, 3, 4], [2, 1, 5, 6], [4, 8, 7, 8]])
print(a.flatten())
Результат;
[1 2 9 8 7 5 3 4 2 1 5 6 4 8 7 8]
argmax() и argmin() – возвращают индексы максимального (минимального) элемента массива:
import numpy as np
a = np.array([[4, 2, 9, 89], [17, 5, 3, 14], [25, -5, 5, 6], [4, 18, 17, 8]])
print(f"Индекс максимального элемента: {np.argmax(a)}")
print(f"Индекс минимального элемента: {np.argmin(a)}")
Результат:
Индекс максимального элемента: 3
Индекс минимального элемента: 9
where() – выбирает из массива элементы, удовлетворяющие определенному условию:
import numpy as np
arr = np.array([[14, 22, 91, 19], [11, 51, 32, 14], [25, 22, 50, 60], [14, 18, 17, 28]])
# формируем новый массив из четных элементов исходного, заменяя нечетные на цифру 5
new_arr = np.where(arr % 2 == 0, arr, 5)
print(new_arr)
Результат:
[[14 22 5 5]
[ 5 5 32 14]
[ 5 22 50 60]
[14 18 5 28]]
В NumPy есть еще один метод, похожий на where() – argwhere(). Он возвращает индексы элементов, соответствующих условию, в виде массива:
import numpy as np
# Создание двумерного массива
arr = np.random.randint(0, 5, size=(5, 5))
print("Массив:")
print(arr)
result = np.argwhere(arr != 3)
print(result)
Результат:
Массив:
[[4 3 0 1 1]
[0 0 0 4 1]
[1 1 2 0 0]
[3 0 0 3 1]
[2 2 4 3 0]]
[[0 0]
[0 2]
[0 3]
[0 4]
[1 0]
[1 1]
[1 2]
[1 3]
[1 4]
[2 0]
[2 1]
[2 2]
[2 3]
[2 4]
[3 1]
[3 2]
[3 4]
[4 0]
[4 1]
[4 2]
[4 4]]
nonzero() – возвращает индексы ненулевых элементов:
import numpy as np
# Создание двумерного массива
arr = np.random.randint(0, 2, size=(5, 5))
print("Массив:")
print(arr)
result = np.nonzero(arr)
print(result)
Результат:
Массив:
[[0 1 0 0 0]
[1 1 0 1 0]
[1 0 1 0 1]
[1 0 1 1 0]
[0 1 0 1 0]]
(array([0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4], dtype=int32), array([1, 0, 1, 3, 0, 2, 4, 0, 2, 3, 1, 3], dtype=int32))
unique() – возвращает уникальные элементы:
import numpy as np
# создание массива случайных чисел размером 500x500
arr = np.random.randint(low=0, high=10, size=(500, 500))
# вывод уникальных значений массива
unique_values, counts = np.unique(arr, return_counts=True)
print(arr)
print(unique_values)
print(f"Количество вхождений каждого уникального числа: {counts}")
Результат:
[[1 8 1 ... 6 3 0]
[4 5 5 ... 6 4 9]
[3 1 4 ... 2 5 8]
...
[3 5 3 ... 0 7 7]
[0 4 0 ... 8 5 4]
[6 3 5 ... 0 2 2]]
[0 1 2 3 4 5 6 7 8 9]
Количество вхождений каждого уникального числа: [25119 25042 25089 25186 24868 24888 24926 24941 24854 25087]
sort() – сортирует массив по возрастанию / убыванию:
import numpy as np
arr = np.array([3, 12, 4, 1, 5, 9, 2, 6, 15, -4, 10, -2, 0, 11, 7])
# сортировка по возрастанию
sorted_asc = np.sort(arr)
# сортировка по убыванию
sorted_des = np.sort(arr)[::-1]
print(sorted_asc)
print(sorted_des)
Результат:
[-4 -2 0 1 2 3 4 5 6 7 9 10 11 12 15]
[15 12 11 10 9 7 6 5 4 3 2 1 0 -2 -4]
diag() – возвращает диагональ матрицы. Также метод можно использовать для создания диагональной матрицы из заданных элементов:
import numpy as np
# Создание двумерного массива
arr = np.arange(16).reshape((4, 4))
print("Исходный массив:")
print(arr)
# Получение значения главной диагонали
print("\nГлавная диагональ массива:")
print(np.diag(arr))
# Создание диагональной матрицы из указанных элементов
diag_matrix = np.diag([1, 2, 3, 4, 5])
print("\nДиагональная матрица:")
print(diag_matrix)
Результат:
Исходный массив:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
Главная диагональ массива:
[ 0 5 10 15]
Диагональная матрица:
[[1 0 0 0 0]
[0 2 0 0 0]
[0 0 3 0 0]
[0 0 0 4 0]
[0 0 0 0 5]]
trace() вычисляет след матрицы, то есть сумму элементов на ее главной диагонали:
import numpy as np
# Создание двумерного массива
arr = np.random.randint(0, 10, size=(20, 20))
print("Исходный массив:")
print(arr)
# Вычисление следа матрицы
trace = np.trace(arr)
print("След матрицы:")
print(trace)
Результат:
Исходный массив:
[[0 2 9 3 8 9 6 5 4 8 2 1 9 6 0 5 9 1 4 8]
[8 1 3 1 5 8 9 4 6 7 7 3 6 5 7 3 4 6 4 7]
[4 4 9 2 8 3 1 0 9 1 7 6 6 3 5 5 3 8 5 2]
[7 9 5 9 5 5 5 4 2 2 8 5 8 8 8 1 6 9 9 0]
[5 3 8 2 3 2 6 7 8 5 2 9 7 4 7 8 3 2 8 0]
[7 1 4 8 7 0 5 6 1 5 7 2 1 5 3 7 9 3 0 1]
[9 9 1 5 3 2 9 5 4 3 2 1 9 5 5 3 5 1 0 7]
[3 8 6 0 7 0 8 8 8 7 4 6 7 3 8 0 0 7 5 8]
[3 5 5 7 6 1 0 6 3 8 0 9 7 1 3 4 1 8 3 4]
[1 9 4 3 6 3 2 4 6 2 4 3 8 5 8 5 4 5 4 8]
[5 4 2 0 9 8 1 3 8 0 9 1 6 9 4 2 0 8 3 4]
[6 4 1 4 9 8 6 0 7 7 0 1 9 3 5 3 8 7 9 2]
[9 4 9 2 5 2 8 6 0 9 0 6 4 1 7 0 6 4 2 8]
[2 6 7 2 5 1 7 0 9 9 4 8 2 1 8 6 8 9 3 0]
[1 8 3 8 2 6 3 0 0 0 4 0 1 4 2 4 3 2 8 9]
[3 9 2 6 6 7 8 4 3 6 6 0 1 6 2 8 8 6 8 7]
[6 2 1 3 0 1 6 5 7 6 8 6 4 5 3 9 3 1 3 2]
[6 2 9 4 4 8 1 3 1 8 1 2 4 9 8 2 3 9 7 8]
[1 2 8 7 4 6 9 5 4 9 3 9 2 8 3 8 1 8 2 3]
[9 8 6 2 5 9 9 6 3 0 6 5 6 5 7 7 7 9 4 7]]
След матрицы:
90
Отлично! Вы освоили весь основной инструментарий библиотеки NumPy.
Вы знаете, почему NumPy так быстр, умеете создавать массивы, выполнять с ними математические операции и применять десятки полезных методов для их преобразования. У вас в руках полный набор инструментов для работы с данными.
Теперь пора посмотреть, какие реальные и впечатляющие задачи он позволяет решать. В полной версии урока вы:
- Научитесь применять NumPy для обработки изображений: изменять цвета и создавать простые фото-фильтры.
- Поймете, как визуализировать данные из файлов с помощью связки NumPy и Matplotlib.
- Решите 10 практических инженерных и финансовых задач: от расчета кредитов и анализа портфеля акций до мониторинга производственных планов.
Комментарии