🐍 Самоучитель по Python для начинающих. Часть 24: Основы работы с NumPy

Покажем основные методы обработки многомерных массивов и научим делать простые фильтры для изображений. В конце статьи – 10 инженерных и экономических задач с решениями.
🐍 Самоучитель по Python для начинающих. Часть 24: Основы работы с NumPy

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 предоставляет обширный выбор методов для всевозможных манипуляций над массивами – от сортировки и фильтрации до изменения формы.
🐍 Библиотека питониста
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»
🐍🎓 Библиотека собеса по Python
Подтянуть свои знания по Python вы можете на нашем телеграм-канале «Библиотека собеса по Python»
🐍🧩 Библиотека задач по Python
Интересные задачи по Python для практики можно найти на нашем телеграм-канале «Библиотека задач по Python»

Как создать массив в 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 практических инженерных и финансовых задач: от расчета кредитов и анализа портфеля акций до мониторинга производственных планов.

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ