====== Числовые данные в Python ======
Встроенные средства Python не слишком хороши для работы с числовыми данными, поэтому для этого существуют отдельные модули. Наиболее важной из них является модуль [[http://www.numpy.org/|NumPy]]pip, высокоэффективные методы для работы с числовыми массивам.
====== NumPy ======
Ключевой компонент numpy — высокопроизводительные многомерные статические числовые массивы ''np.array''. Также в NumPy имеются многочисленные методы которые позволяют избежать накладных расходов при преобразовании ''np.array'' к типам Python и обратно.
Некоторые свойства ''np.array'':
^ Свойство ^ Значение ^
| ''a.ndim'' | Число измерений массива ''a'' |
| ''a.shape'' | Размер по осям (кортеж) массива ''a'' |
| ''a.size'' | Общее число элементов в ''a'' |
| ''a.dtype'' | Тип данных массива ''a'' |
===== Создание массивов =====
import numpy as np
# Из списка Python
a = np.array([1,2,3,4,5])
# a == array([1, 2, 3, 4, 5])
# Заполнение нулями
a = np.zeros(5)
# a == array([ 0., 0., 0., 0., 0.])
# 5 точек от 0 до 5 включительно
a = np.linspace(0,5,5)
# a == array([ 0. , 1.25, 2.5 , 3.75, 5. ])
# точки от 0 до 5 с шагом 1
a = np.arange(0,5,1)
# a == array([0, 1, 2, 3, 4])
===== Многомерные массивы =====
Класс ''np.array'' также позволяет работать с многомерными массивами. При этом задается параметр ''shape'' определяющий размерность.
import numpy as np
# Массив 2x3
a = np.zeros((2,3))
# a == array([[ 0., 0., 0.],
# [ 0., 0., 0.]])
a = np.linspace(0,5,6)
# Изменение формы массива
b = a.reshape((3,2))
# b == array([[ 0., 1.],
# [ 2., 3.],
# [ 4., 5.]])
# Изменение порядка осей
b = np.moveaxis(b,0,-1)
# b == array([[0., 2., 4.],
# [1., 3., 5.]])
===== Индексация в массиве =====
При индексации сохраняется порядок индексов аналогичный фигуре
import numpy as np
b = np.linspace(0,5,6).reshape((3,2))
# b == array([[ 0., 1.],
# [ 2., 3.],
# [ 4., 5.]])
# Один элемент
v = b[1,1]
# v == 3.0
# Строка
v = b[1,:]
# v == array([ 2., 3.])
# Столбец
v = b[:,1]
# v == array([ 1., 3., 5.])
# Выборка и использованием срезов
v = b[1:-1,1]
# v == array([ 3.])
В целях оптимизации срезы создают не копии ''np.array'', а так называемые представления (''view''), поэтому меняя данные в срезе они будут меняться и в исходном массиве.
===== Операции =====
В отличии от обычных списков Python операции над массивами производятся поэлементно.
import numpy as np
b = np.linspace(0,5,6).reshape((3,2))
# b == array([[ 0., 1.],
# [ 2., 3.],
# [ 4., 5.]])
# Операция со скаляром
c = b + 2
# c == array([[ 2., 3.],
# [ 4., 5.],
# [ 6., 7.]])
# Операция с массивом того-же размера
c = b * b
# c == array([[ 0., 1.],
# [ 4., 9.],
# [ 16., 25.]])
В отличии от обычных списков Python операции над массивами производятся поэлементно.
Математические функции из ''math'' работать с ''np.array'' не умеют, но в модуле ''numpy'' есть их аналоги (''np.cos'', ''np.log10'', ...).
===== Конкатенация =====
b = np.linspace(0,5,6).reshape((3,2))
# b == array([[ 0., 1.],
# [ 2., 3.],
# [ 4., 5.]])
# Конкатенация по строкам
d = np.vstack((b,b))
# d == array([[0., 1.],
# [2., 3.],
# [4., 5.],
# [0., 1.],
# [2., 3.],
# [4., 5.]])
# Конкатенация по столбцам
d = np.hstack((b,b))
#d == array([[0., 1., 0., 1.],
# [2., 3., 2., 3.],
# [4., 5., 4., 5.]])
===== Итерация =====
NumPy предлагает альтернативный подход к итерации по всем элементам многомерного массива по сравнению с обычными вложенными циклами.
# Обычный обход. Получаем строки.
# Нужен вложенный цикл для доступа к элементам. Только чтение.
for x in b:
print(x, end=";")
# [ 0. 1.]; [ 2. 3.]; [ 4. 5.];
# Обход через np.nditer
for x in np.nditer(b):
print(x, end=" ")
# 0.0 1.0 2.0 3.0 4.0 5.0
# Обход через np.nditer с возможностью записи
for x in np.nditer(b, op_flags=['readwrite']):
x[...] = 2 * x + 2
# b == array([[ 2., 4.],
# [ 6., 8.],
# [ 10., 12.]])
# Многомерный аналог enumerate
for index, x in np.ndenumerate(b):
print(index, x, end="; ")
# (0, 0) 2.0; (0, 1) 4.0; (1, 0) 6.0; (1, 1) 8.0; (2, 0) 10.0; (2, 1) 12.0;
===== Операции сравнения =====
Применение операций сравнения (''>'', ''<'', ''>='', ''<='', ''=='', ''!='') к массивам ''np.array'' вызывает создание массива значений типа ''bool'' соответствующего размера.
b = np.linspace(0,5,6).reshape((3,2))
# b == array([[0., 1.],
# [2., 3.],
# [4., 5.]])
t = b>3
# t == array([[False, False],
# [False, False],
# [ True, True]])
Этот массив можно использовать далее для различных операций в качестве индекса.
q = b[b>3]
# q == array([4., 5.])
b[b>3] = 0
# b == array([[0., 1.],
# [2., 3.],
# [0., 0.]])
Маску можно использовать для любых массивов имеющих одинаковый shape. Их можно объединить в логические выражение с использованием операторов побитового «и» ''&'' и «или» ''|'', а также использовать кванторы ''np.any'' и ''np.all''.
===== Чтение и запись =====
^ Пример ^ Назначение ^ Справка ^
| ''np.save('file.npy', M)'' | Сохранение данных в формат Numpy | [[https://numpy.org/doc/stable/reference/generated/numpy.save.html|save]] |
| ''M = np.load('file.npy')'' | Чтение данных из формата Numpy | [[https://numpy.org/doc/stable/reference/generated/numpy.load.html|load]] |
| ''np.savetxt('file.txt',M)'' | Запись данных в текстовый файл | [[https://numpy.org/doc/stable/reference/generated/numpy.savetxt.html#numpy.savetxt|savetxt]] |
| ''M = np.loadtxt('file.txt')'' | Чтение данных из текстового файла | [[
https://numpy.org/doc/stable/reference/generated/numpy.loadtxt.html|loadtxt]] |
| ''M.tofile('file.dat')'' | Сохранение данных в бинарный файл | [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tofile.html|tofile]] |
| ''M = np.fromfile('file.dat')'' | Чтение данных из бинарного файла | [[https://numpy.org/doc/stable/reference/generated/numpy.fromfile.html|fromfile]] |
===== Что еще нужно знать о NumPy =====
* По NumPy есть подробная официальная документация: [[https://docs.scipy.org/doc/numpy/reference/index.html]]
* Для работы с данными содержащими инвалидные значения существует специальный класс ''numpy.ma.masked_array''
* Два числа с плавающий запятой могут быть равны только чудом, поэтому следует использовать ''numpy.isclose'' и явно указывать точность.
* Классы ''np.array'' переопределяют вывод на печать. Поэтому сделав ''print'' вы увидите только часть элементов.