Важно: количество функций и внутренних модулей в библиотеках
math
и numpy очень велико. В этом уроке мы рассмотрим лишь ключевые возможности, которые пригодятся вам на практике. Более подробную информацию всегда можно найти в официальной документации — ссылка будет в конце параграфа.
Ключевые вопросы параграфа
- Какие возможности предоставляет модуль math для математических расчётов в Python?
- Как устроены массивы в numpy и чем они отличаются от обычных списков?
- Какие атрибуты и методы есть у объектов ndarray?
- Как создавать, изменять и обрабатывать массивы с помощью numpy?
- В чём преимущества numpy при выполнении математических операций и работе с данными?
Какие возможности даёт модуль math для математических расчётов в Python
Python — интерпретируемый язык, а интерпретируемые языки менее производительные, чем компилируемые. При этом он повсеместно используется для задач, в которых нужна высокая производительность, — например, в машинном обучении и анализе больших данных.
Это возможно благодаря специализированным библиотекам, таким как math
, numpy
, pandas
, которые реализованы на более низкоуровневых компилируемых языках (например, C и Fortran).
Модуль math
входит в стандартную библиотеку Python и предоставляет широкий набор математических функций и констант. Он особенно полезен, когда нужно выполнять точные вычисления с числами — от элементарной арифметики до более сложных операций, включая работу с тригонометрией и логарифмами.
Функции теории чисел и представления
Эти функции пригодятся для вычислений, связанных с комбинаторикой, делимостью и произведением чисел.
Сочетания
math.comb(n, k)
— вычисляет число сочетаний из n
по k
. Это количество способов выбрать k
объектов из n
, без учёта порядка и без повторений.
Рассмотрим пример. Допустим, у нас есть 12 различных объектов — например, книг, элементов или чисел. Фраза «выбрать 3 объекта из множества из 12 элементов» означает, что из этих 12 нужно выбрать любые 3, не обращая внимания на порядок.
Для этого можно использовать math.comb
:
1import math
2
3print(math.comb(12, 3)) # 220
Значит, существует 220 различных комбинаций из 3 элементов, выбранных из 12.
Факториал
math.factorial(x)
— возвращает факториал числа x
, то есть произведение всех целых чисел от 1 до x
. Используется, например, в комбинаторике и вероятностных расчётах.
1print(math.factorial(5)) # 120
Наибольший общий делитель (НОД)
math.gcd(*integers)
— возвращает наибольший общий делитель (НОД) для всех аргументов. С версии Python 3.9 можно передавать более двух чисел:
1print(math.gcd(120, 210, 360)) # 30
Наименьшее общее кратное (НОК)
math.lcm(*integers)
— возвращает наименьшее общее кратное. Полезно при приведении дробей к общему знаменателю. Также появилась в Python 3.9.
1print(math.lcm(10, 20, 30, 40)) # 120
Размещения и перестановки
math.perm(n, k=None)
— возвращает количество размещений из n
по k
элементов (учитывается порядок). Если k
не указано, считается количество перестановок — размещений всех элементов.
1print(math.perm(4, 2)) # 12
2print(math.perm(4)) # 24
Произведение элементов
math.prod(iterable, start=1)
— перемножает все элементы в переданном итерируемом объекте (например, списке или диапазоне). Если объект пуст, возвращается значение аргумента start (по умолчанию — 1).
1print(math.prod(range(10, 21))) # 6704425728000
Степенные и логарифмические функции
Эти функции используются для работы с экспонентами, логарифмами и возведением в степень.
Экспонента
math.exp(x)
— возвращает значение e^x
, где e
— математическая константа (приблизительно 2.71828).
1print(math.exp(3.5)) # 33.11545195869231
Логарифмы
math.log(x, base)
— вычисляет логарифм x
по основанию base
. Если основание не указано, возвращается натуральный логарифм (по основанию e).
1print(math.log(10)) # 2.302585092994046
2print(math.log(10, 2)) # 3.3219280948873626
Возведение в степень
math.pow(x, y)
— аналог оператора **
, но оба аргумента преобразуются в float
, что обеспечивает точность при вещественных вычислениях.
1print(math.pow(2, 10)) # 1024.0
2print(math.pow(4.5, 3.7)) # 261.1477575641718
Тригонометрические функции
Функции из этой группы работают с углами и используют радианы (а не градусы) по умолчанию.
math.sin(x)
,math.cos(x)
,math.tan(x)
— стандартные тригонометрические функции.math.asin(x)
,math.acos(x)
,math.atan(x)
— обратные функции.
Особые функции:
Евклидово расстояние
math.dist(p, q)
— расстояние между точками p
и q
в пространстве. Каждая точка задаётся как итерируемый объект.
1print(math.dist((0, 0, 0), (1, 1, 1))) # 1.7320508075688772
Гипотенуза и длина вектора
math.hypot(*coordinates)
— длина многомерного вектора, идущего от начала координат. Для двух координат работает как √(x² + y²).
1print(math.hypot(1, 1, 1)) # 1.7320508075688772
2print(math.hypot(3, 4)) # 5.0
Преобразование углов
Для работы с углами в разных системах измерения:
math.degrees(x)
— переводит угол из радиан в градусы.math.radians(x)
— переводит угол из градусов в радианы.
1print(round(math.degrees(math.asin(0.5)), 1)) # 30.0
2print(round(math.sin(math.radians(30)), 1)) # 0.5
Гиперболические функции
Включают функции:
sinh(x)
,cosh(x)
,tanh(x)
— гиперболические аналоги синуса, косинуса и тангенса;asinh(x)
,acosh(x)
,atanh(x)
— обратные к ним.
Эти функции редко используются в базовой практике, но важны в инженерных и научных вычислениях.
Специальные функции
Гамма-функция
math.gamma(x)
— это обобщение факториала на вещественные числа. Для целых n выполняется gamma(n) = (n-1)!
. Используется в статистике, физике и теории вероятностей.
1print(math.gamma(3)) # 2.0
2print(math.gamma(3.5)) # 3.323350970447842
3print(math.gamma(4)) # 6.0
Математические константы
math.pi
— значение числа π (3.1415926…)math.e
— основание натурального логарифма (примерно 2.71828)
Модуль math
покрывает широкий спектр математических задач, связанных с отдельными числами и стандартными функциями. Однако при работе с большими объёмами данных или сложными структурами — такими как векторы, матрицы или таблицы чисел — его возможностей становится недостаточно.
В таких случаях на помощь приходит библиотека numpy
, которая предназначена для эффективной работы с массивами и числовыми данными в целом. Именно она лежит в основе большинства научных и аналитических расчётов в Python.
Как устроены массивы в numpy
и чем они отличаются от обычных списков
Прежде чем углубиться в работу с массивами, стоит сделать два уточнения.
Во-первых, для полного понимания материала желательно освежить знания линейной алгебры — особенно понятия матриц и операций над ними (транспонирование, умножение, обращение и др.). numpy
устроена так, чтобы эффективно выполнять как базовые арифметические действия, так и более сложные линейные преобразования.
Во-вторых, библиотека numpy
не входит в стандартную поставку Python. Чтобы использовать её в своём коде, сначала её необходимо установить.
Установка numpy
Установка производится через репозиторий PyPI (Python Package Index) — это коллекция внешних библиотек, доступных для Python.
В PyPI доступно более 400 000 пакетов. Для установки библиотеки потребуется подключение к интернету и выполнение следующей команды в консоли:
1pip install numpy
После запуска этой команды произойдёт загрузка пакета и зависимостей, а затем установка. Если всё прошло успешно, вы увидите сообщение:
1Successfully installed numpy
Для использования библиотеки её нужно импортировать. В сообществе принято импортировать numpy
под псевдонимом np — это сокращает записи в коде и делает его более читаемым:
1import numpy as np
Теперь можно перейти к главному объекту библиотеки — массивам.
Что такое массивы и зачем они нужны
Во многих задачах — от научных расчётов до машинного обучения — требуется обрабатывать большие объёмы числовых данных: векторы, таблицы, матрицы, временные ряды. Для этого важна скорость вычислений и эффективное использование памяти. Обычные списки Python универсальны, но не всегда подходят для таких целей: они хранят объекты произвольных типов и не оптимизированы для числовой обработки.
Массивы — это структуры данных, в которых:
- все элементы имеют одинаковый тип (например, только числа с плавающей точкой);
- данные хранятся компактно и последовательно в памяти;
- возможна высокая производительность при выполнении операций сразу над всеми элементами.
Кроме того, массивы numpy
могут быть многомерными:
- одномерный массив — аналог списка;
- двумерный массив — аналог таблицы или матрицы;
- трёхмерный и выше — тензоры, которые применяются, например, в машинном обучении и компьютерном зрении.
Создать массив можно, например, из обычного списка с помощью функции np.array()
. Обращение к элементам массива выполняется по индексам, начиная с нуля:
1import numpy as np
2
3a = np.array([1, 2, 3, 4])
4b = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
5print(f"a[0] = {a[0]}")
6print(f"b[0] = {b[0]}")
7
8# Вывод программы
9
10# a[0] = 1
11
12# b[0] = [1 2]
В этом примере массив a одномерный — у него одна ось длиной 4. Массив b двумерный — у него две оси: первая длиной 4 (строки), вторая — длиной 2 (столбцы). В документации numpy
такие оси называются “axis”.
В numpy
каждый массив имеет чётко определённую структуру: количество осей, их длину и тип хранимых данных. Чтобы получить эту информацию, используются атрибуты объекта ndarray
— именно так называется тип, к которому принадлежат массивы в numpy
.
Какие атрибуты и методы есть у объектов ndarray
Массивы в numpy — это объекты класса ndarray
(англ. n-dimensional array), и каждый такой объект хранит информацию о своей структуре и содержимом. Ниже представлены наиболее важные атрибуты, которые позволяют понять, что именно находится внутри массива:
ndarray.ndim
— количество осей массива (его размерность);ndarray.shape
— кортеж с количеством элементов по каждой из осей;ndarray.size
— общее число элементов в массиве;ndarray.dtype
— тип данных, хранящихся в массиве;ndarray.itemsize
— количество байт, которое занимает один элемент.
Рассмотрим пример:
1import numpy as np
2
3a = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
4print(f"a.ndim = {a.ndim}, a.shape = {a.shape}, a.size = {a.size}, a.dtype = {a.dtype}")
5
6# Вывод программы
7
8# a.ndim = 2, a.shape = (4, 2), a.size = 8, a.dtype = int32
В этом примере массив состоит из 4 строк и 2 столбцов, всего 8 целых чисел. Каждый элемент имеет тип int32
— это целое число, занимающее 32 бита (4 байта) в памяти.
Типы данных в numpy
Типы данных в numpy
тесно связаны с типами в языке C. Благодаря этому массивы можно эффективно хранить в памяти, но появляется важное ограничение: каждый тип данных имеет фиксированный диапазон допустимых значений.
Например, тип int32
может хранить только целые числа от -2 147 483 648 до 2 147 483 647. Если попытаться записать значение, выходящее за этот диапазон, произойдёт переполнение. Покажем это на примере:
1import numpy as np
2
3a = np.array([1, 2, 3], dtype="uint8")
4a[0] = 256
5print(a)
6
7# Вывод программы
8
9# [0 2 3]
Массив был создан с типом uint8
— это целые числа без знака, которые занимают 8 бит (1 байт) и могут принимать значения от 0 до 255. Попытка записать 256 приводит к переполнению: значение обнуляется (по модулю 256).
Автоматический выбор типа данных
Если не указать явно тип при создании массива, numpy
попытается определить его автоматически на основе всех переданных значений. Тип будет выбран так, чтобы он подходил для хранения всех элементов.
Рассмотрим следующий пример:
1import numpy as np
2
3a = np.array([1, 2.5, 3])
4print(a)
5print(a.dtype)
6
7b = np.array(['text', 1, 2.5])
8print(b)
9print(b.dtype)
10
11# Вывод программы
12
13# [1. 2.5 3. ]
14
15# float64
16
17# ['text' '1' '2.5']
18
19# <U32
В массиве a есть вещественное число, поэтому все значения были приведены к типу float64
. В массиве b
присутствует строка, и в результате все элементы приведены к строковому типу <U32
, который обозначает Unicode-строки длиной до 32 символов.
С другими встроенными типами данных
numpy
можно ознакомиться в официальной документации.
Как создавать, изменять и обрабатывать массивы с помощью numpy
Теперь, когда вы познакомились с тем, как устроены массивы в numpy
, какие у них атрибуты и типы данных, можно перейти к практическому вопросу: как такие массивы создавать, изменять и обрабатывать.
Создание массивов
Для создания массива из нулей используется функция np.zeros()
. В неё передаётся кортеж с количеством элементов по каждой оси. По умолчанию создаются массивы с типом float64
, но можно указать и другой тип через аргумент dtype
:
1import numpy as np
2
3a = np.zeros((4, 3))
4print(a)
5print()
6a = np.zeros((4, 3), dtype="int32")
7print(a)
8
9# Вывод программы
10
11# [[0. 0. 0.]
12
13# [0. 0. 0.]
14
15# [0. 0. 0.]
16
17# [0. 0. 0.]]
18
19#
20
21# [[0 0 0]
22
23# [0 0 0]
24
25# [0 0 0]
26
27# [0 0 0]]
Функция np.ones()
создаёт массив аналогичного размера, но заполненный единицами:
1import numpy as np
2
3a = np.ones((4, 3))
4print(a)
5
6# Вывод
7
8# [[1. 1. 1.]
9
10# [1. 1. 1.]
11
12# [1. 1. 1.]
13
14# [1. 1. 1.]]
Для создания единичной матрицы (единицы на главной диагонали, нули в остальных ячейках) используется функция np.eye()
:
1import numpy as np
2
3a = np.eye(5, 5, dtype="int8")
4print(a)
5
6# Вывод
7
8# [[1 0 0 0 0]
9
10# [0 1 0 0 0]
11
12# [0 0 1 0 0]
13
14# [0 0 0 1 0]
15
16# [0 0 0 0 1]]
Диапазоны значений
np.arange()
создаёт массив, аналогичный range()
, но возвращает именно массив numpy
. Эта функция поддерживает как целые, так и вещественные шаги:
1import numpy as np
2
3a = np.arange(1, 10)
4print(a)
5print()
6a = np.arange(1, 5, 0.4)
7print(a)
8
9# Вывод
10
11# [1 2 3 4 5 6 7 8 9]
12
13#
14
15# [1. 1.4 1.8 2.2 2.6 3. 3.4 3.8 4.2 4.6]
np.linspace()
создаёт массив из заданного числа равномерно распределённых значений на отрезке:
1import numpy as np
2
3a = np.linspace(1, 5, 10)
4print(a)
5
6# Вывод
7
8[1. 1.44444444 1.88888889 2.33333333 2.77777778 3.22222222
9 3.66666667 4.11111111 4.55555556 5.]
Изменение размерности массива
Функция reshape()
позволяет задать новую форму массива. Количество элементов при этом должно сохраняться:
1import numpy as np
2
3a = np.zeros((4, 3), dtype="uint8")
4print(a)
5print()
6a = a.reshape((2, 6))
7print(a)
8
9# Вывод
10
11# [[0 0 0]
12
13# [0 0 0]
14
15# [0 0 0]
16
17# [0 0 0]]
18
19#
20
21# [[0 0 0 0 0 0]
22
23# [0 0 0 0 0 0]]
Метод resize()
работает похоже, но изменяет исходный массив, а не создаёт новый:
1import numpy as np
2
3a = np.zeros((4, 3), dtype="uint8")
4print(a)
5print()
6a.resize((2, 2, 3))
7print(a)
8
9# Вывод
10
11# [[0 0 0]
12
13# [0 0 0]
14
15# [0 0 0]
16
17# [0 0 0]]
18
19#
20
21# [[[0 0 0]
22
23# [0 0 0]]
24
25#
26
27# [[0 0 0]
28
29# [0 0 0]]]
Если в reshape()
указать -1
по одной из осей, numpy
автоматически вычислит нужное значение:
1import numpy as np
2
3a = np.zeros((4, 3), dtype="uint8")
4print(a)
5print()
6a = a.reshape((2, 3, -1))
7print(a)
8
9# Вывод
10
11# [[0 0 0]
12
13# [0 0 0]
14
15# [0 0 0]
16
17# [0 0 0]]
18
19#
20
21# [[[0 0]
22
23# [0 0]
24
25# [0 0]]
26
27#
28
29# [[0 0]
30
31# [0 0]
32
33# [0 0]]]
Арифметика и операции над массивами
Все стандартные арифметические операции (+
, -
, *
, /
) применяются поэлементно. Размерности массивов при этом должны совпадать:
1import numpy as np
2
3a = np.array([9, 8, 7])
4b = np.array([1, 2, 3])
5print(a + b)
6print(a - b)
7print(a * b)
8print(a / b)
9
10# Вывод
11
12# [10 10 10]
13
14# [8 6 4]
15
16# [9 16 21]
17
18# [9. 4. 2.33333333]
Матричное умножение
Для умножения матриц используется оператор @
или функция np.dot()
:
1import numpy as np
2
3a = np.array([[1, 2, 3],
4 [4, 5, 6],
5 [7, 8, 9]])
6b = np.array([[0, 0, 1],
7 [0, 1, 0],
8 [1, 0, 0]])
9print(a @ b)
10
11# Вывод
12
13# [[3 2 1]
14
15# [6 5 4]
16
17# [9 8 7]]
Транспонирование и поворот массива
Массив можно транспонировать или повернуть. transpose()
меняет оси местами, а rot90()
поворачивает массив на 90 градусов (по умолчанию — против часовой стрелки):
1import numpy as np
2
3a = np.arange(1, 13).reshape(4, 3)
4print(a)
5print("Транспонирование")
6print(a.transpose())
7print("Поворот влево")
8print(np.rot90(a))
9print("Поворот вправо")
10print(np.rot90(a, -1))
Вывод будет таким:
1# [[ 1 2 3]
2
3# [ 4 5 6]
4
5# [ 7 8 9]
6
7# [10 11 12]]
8
9# Транспонирование
10
11# [[ 1 4 7 10]
12
13# [ 2 5 8 11]
14
15# [ 3 6 9 12]]
16
17# Поворот влево
18
19# [[ 3 6 9 12]
20
21# [ 2 5 8 11]
22
23# [ 1 4 7 10]]
24
25# Поворот вправо
26
27# [[10 7 4 1]
28
29# [11 8 5 2]
30
31# [12 9 6 3]]
Агрегатные функции
Функции sum()
, min()
, max()
работают по умолчанию со всем массивом:
1import numpy as np
2
3a = np.array([[1, 2, 3],
4 [4, 5, 6],
5 [7, 8, 9]])
6print(a.sum())
7print(a.min())
8print(a.max())
9
10# Вывод
11
12# 45
13
14# 1
15
16# 9
Можно также указать ось, по которой нужно вычислять результат:
1import numpy as np
2
3a = np.array([[1, 2, 3],
4 [4, 5, 6],
5 [7, 8, 9]])
6print(a.sum(axis=0)) # Сумма по столбцам
7print(a.sum(axis=1)) # Сумма по строкам
8print(a.min(axis=0)) # Минимум по столбцам
9print(a.max(axis=1)) # Максимум по строкам
10
11# Вывод
12
13# [12 15 18]
14
15# [ 6 15 24]
16
17# [1 2 3]
18
19# [3 6 9]
Индексация, срезы и обход массива
Массивы numpy поддерживают расширенные возможности индексирования:
1import numpy as np
2
3a = np.arange(1, 13).reshape(3, 4)
4print(a)
5print()
6print(a[:2, 2:]) # Срез: 2 строки, последние 2 столбца
7print()
8print(a[:, ::2]) # Срез всех строк, каждый второй столбец
9
10# Вывод
11
12# [[ 1 2 3 4]
13
14# [ 5 6 7 8]
15
16# [ 9 10 11 12]]
17
18#
19
20# [[3 4]
21
22# [7 8]]
23
24#
25
26# [[ 1 3]
27
28# [ 5 7]
29
30# [ 9 11]]
По первой оси массива можно пройтись в цикле for
, как по списку:
1import numpy as np
2
3a = np.arange(1, 13).reshape(3, 4)
4print(a)
5for row in a:
6 print(row)
7
8# Вывод
9
10# [[ 1 2 3 4]
11
12# [ 5 6 7 8]
13
14# [ 9 10 11 12]]
15
16#
17
18# [1 2 3 4]
19
20# [5 6 7 8]
21
22# [ 9 10 11 12]
Линеаризация массива
Иногда бывает нужно обработать все элементы многомерного массива как единую последовательность. Такой процесс называется линеаризацией — он превращает, например, матрицу в простой список значений. Это удобно при подсчётах, фильтрации, агрегировании.
Для последовательного доступа ко всем элементам массива без явной линеаризации можно использовать итератор flat
:
1import numpy as np
2
3a = np.arange(1, 13).reshape(3, 4)
4print(a)
5print()
6print("; ".join(str(el) for el in a.flat))
7
8# Вывод
9
10# [[ 1 2 3 4]
11
12# [ 5 6 7 8]
13
14# [ 9 10 11 12]]
15
16#
17
18# 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12
В чём преимущества numpy
при выполнении математических операций и работе с данными
Одно из ключевых преимуществ библиотеки numpy
— высокая производительность при выполнении числовых операций. Это особенно заметно при обработке больших объёмов данных.
Ниже — пример, демонстрирующий разницу в скорости между стандартным подходом на основе списков и вычислениями с использованием массивов numpy
.
1import numpy as np
2from time import time
3
4t = time()
5print(f"Результат итератора: {sum(x **0.5 for x in range(10** 7))}.")
6print(f"{time() - t} с.")
7t = time()
8print(f"Результат numpy: {np.sqrt(np.arange(10 ** 7)).sum()}.")
9print(f"{time() - t} с.")
10
11# Вывод программы
12
13# Результат итератора: 21081849486.439312
14
15# 1.7823209762573242 с
16
17# Результат numpy: 21081849486.442448
18
19# 0.05197310447692871 с
Вычисления с использованием numpy
оказались примерно в 30 раз быстрее. Это достигается за счёт векторизации операций и низкоуровневой реализации библиотеки на языках C и Fortran.
Благодаря своей скорости и удобной работе с массивами numpy
часто применяют в вычислительных библиотеках более высокого уровня. С одной из таких библиотек вы познакомитесь в следующем параграфе.
✅ У вас получилось разобраться с модулями math и numpy
?
Ещё по теме
Для более детального изучения библиотеки numpy рекомендуем почитать документацию.
Что дальше
В этом параграфе вы разобрались, как устроены массивы numpy
и чем они отличаются от обычных списков. Вы научились создавать массивы различных форм, управлять их типами данных, менять размерность и извлекать нужные фрагменты с помощью срезов. Освоили базовые операции над массивами: арифметические вычисления, агрегирующие функции, транспонирование и повороты.
Кроме того, вы увидели, как numpy
обеспечивает высокую производительность при работе с большими объёмами чисел и почему именно эта библиотека становится основой для многих аналитических инструментов в Python.
Дальше мы перейдём к библиотеке pandas, которая расширяет подход numpy
и делает работу с табличными данными ещё удобнее. Вас ждёт знакомство с объектами Series
и DataFrame
, фильтрацией, группировкой, визуализацией и чтением реальных дата-сетов.
А пока вы не ушли дальше — закрепите материал на практике:
- Отметьте, что урок прочитан, при помощи кнопки ниже.
- Пройдите мини-квиз, чтобы проверить, насколько хорошо вы усвоили тему.
- Перейдите к задачам этого параграфа и потренируйтесь.
- Перед этим — загляните в короткий гайд о том, как работает система проверки.
Хотите обсудить, задать вопрос или не понимаете, почему код не работает? Мы всё предусмотрели — вступайте в сообщество Хендбука! Там студенты помогают друг другу разобраться.
Ключевые выводы параграфа
- Модуль
math
содержит базовые математические функции, работающие с отдельными числами. - Библиотека
numpy
обеспечивает быструю и удобную работу с массивами через объектndarray
. - Массивы
numpy
имеют атрибуты (shape
,dtype
и др.) и поддерживают операции изменения формы, арифметику и агрегирование. - Создание массивов возможно разными способами: из нулей, единиц, диапазонов, с заданным типом данных.
- При работе с большими объёмами данных
numpy
существенно опережает списки Python по скорости и эффективности.