Академический Документы
Профессиональный Документы
Культура Документы
numpy
с = [] # 1D
for i in range(len(a)):
c.append(a[i] * b[i]
python
python python python
3
Писать много – не питонично
c = [] # 2D
for i in range(len(a)):
c.append([])
c1 = c[-1]
много for j in range(len(a[0])):
c1.append(a[i][j] * b[i][j]
много
много
4
Писать много – не питонично
c = [] # nD
# Fire me please
# Kill me please
5
Математично, питонично, понятно
c = a * b # numpy
6
Numpy предлагает
● Работу с массивами
● с элементами определённого типа (типа C)
● Предопределённые поэлементные операции
● Математическую лаконичность
● Околосветовыесишные скорости
7
Пример: точки синуса в две строки
8
Взгляд на ndarray
10
Массив и его поля
a.data
a.ndim == 3
a.shape == (5, 4, 3)
a.size == 60 == 5*4*3
1 3 22 4 6
4 эл.
3 8 1 7 2 a.dtype ==
a=
9 5 3 8 4 dtype('int64')
6 1 2 7 9 a.itemsize == 8
.
эл
3
5 элементов
>>> a = np.array([2,3,4])
>>> a
array([2, 3, 4]) # тип - автоматически
>>> a.dtype
dtype('int64')
14
Явное задание типа
>>> c = np.array( [ [1,2], [3,4] ],
dtype=complex )
>>> c
array([[ 1.+0.j, 2.+0.j],
[ 3.+0.j, 4.+0.j]])
15
Создание из ничего
>>> np.zeros( (3,4) ) # тип - float64
array([[ 0., 0., 0., 0.], shape
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
17
Создание на интервале (2)
# минимум максимум количество
>>> np.linspace( 0, 2, 9 )
18
Создание случайного массива
>>> np.random.rand(3, 2) # равномерное на [0; 1)
0.6102079062175166 # число
>>> b**2
array([0, 1, 4, 9])
>>> a *= 3
>>> a
array([[3, 3, 3],
[3, 3, 3]])
>>> b += a
>>> b
array([[ 3.417022 , 3.72032449, 3.00011437],
[ 3.30233257, 3.14675589, 3.09233859]])
24
Функции, работающие
поэлементно
>>> xs = np.arange(0, 5., 1)
>>> ys = np.sin(xs)
>>> zs = np.exp(xs)
>>> xs
array([ 0., 1., 2., 3., 4.])
>>> ys
array([ 0. , 0.84147098, 0.90929743, 0.14112001,
-0.7568025 ])
>>> zs
array([ 1. , 2.71828183, 7.3890561 ,
20.08553692, 54.59815003])
ys = np.sin(xs)
zs = ys ** 2 + xs / 3
ts = np.exp(zs)
Визуализация зависимости xs, ys, zs, ts от xs.
xs, ys, zs, ts - массивы 26
Upcasting: каст в более широкое
(по битам/диапазону) или точное число
>>> a = np.ones((2,3), dtype=int)
>>> b = np.random.random((2,3))
Вспомним C/C++: там тоже всё узкое и неточное кастится в широкое и точное, когда участвует в выражениях. 27
Ручной каст
>>> a = np.ones((2,3), dtype=int)
>>> b = np.random.random((2,3))
>>> b.astype(int)
array([[3, 3, 3],
[3, 3, 3]])
>>> a += b.astype(int)
28
Подмассивы,
доступ к элементам
>>> a[0][1]
2
31
Подмассив: пропуск измерений
>>> a = np.array([[[1000, 1001],
[1010, 1011]],
[[1100, 1101],
[1110, 1111]]])
>>> a[...]
# получим содержимое массива a
32
Выборка из массива
>>> a = np.array([0,10,20,30,40,50,60,70])
33
Выборка из массива: nD
>>> a = np.array([[[1000, 1001],
[1010, 1011]],
[[1100, 1101],
[1110, 1111]]])
a[1,0,0]
a[1,1,0]
a[1,0,1] [[a[0,1,0], a[1,0,0]],
[a[1,1,0], a[1,0,1]]]
>>> a[i,j,k]
array([[1010, 1100],
[1110, 1101]])
34
Выборка из массива: nD (2)
>>> idx = (i, j, k) >>> idx = [i, j, k]
>>> a[idx] >>> a[idx]
array([[1010, 1100], array([[1010, 1100],
[1110, 1100]]) [1110, 1100]])
[[1100, 1101],
[1110, 1111]],
[[1000, 1001],
[1010, 1011]]])
# a[idx] эквивалентно
# a[np.array(idx)]
35
Итерация по массиву
>>> for x in a: print(x)
[[1000 1001]
[1010 1011]]
[[1100 1101]
[1110 1111]]
38
Индекс в ndarray
● Значение или кортеж значений, где значение
● Целое число 0..shape[i]-1
● одномерный/многомерный ndarray целых
индексов
● одномерный ndarray булевых значений
● python slice objects (a:b или a:b:c)
● python ellipsis (...)
● numpy.newaxis (равно None, но нагляднее)
● Что-то ещё?
39
Функции над массивами,
статистики
>>> a.sum()
2.5718191614547998
>>> a.min()
0.1862602113776709
>>> a.max()
0.6852195003967595
41
Агрегатные функции: по осям
>>> a = np.array([[1, 2, 3],
[4, 5, 6]])
>>> a.sum()
21
>>> a.sum(axis=1)
array([ 6, 15])
>>> a[np.nonzero(a)]
array([1, 3, 4])
>>> np.array([[True],[False]]).all(axis=0)
array([ True, False], dtype=bool)
43
Индексы минимумов/максимумов
>>> a = np.array([3, 3, 2])
>>> a.argmin()
2
>>> b.argmax(axis=0)
array([1, 0, 1])
44
Манипуляции
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1., 8.],
[ 0., 4.]])
>>> a
array([1, 2, 3])
>>> a[np.newaxis, :]
array([[1, 2, 3]])
47
Разделение массивов на части
>>> a = np.array([[1,2,3,4,5,6],[7,8,9,1,2,3]])
>>> a
array([[1, 2, 3, 4, 5, 6],
[7, 8, 9, 1, 2, 3]])
>>> np.hsplit(a, 3)
[array([[1, 2], делим на три части
[7, 8]]), array([[3, 4], array([[1, 2, 3, 4, 5, 6],
[9, 1]]), array([[5, 6], [7, 8, 9, 1, 2, 3]])
[2, 3]])]
50
Broadcasting
>>> a + b # 2D + 1D
array([[11, 22, 33], b само расширилось
[14, 25, 36]]) до двух измерений
52
Правила броадкастинга
● Для вызова функций, работающих
поэлементно
1.В начала всех форм "маломерных" массивов
добавятся единицы.
2.Все массивы с размером 1 по некоторому
измерению будут вести себя как массивы с
размером, равным максимальному размеру
входного массива по этому измерению, причём
будут выдавать один и тот же элемент по этому
измерению.
53
Правила броадкастинга: пример
● Хотим сложить A и B с формами (2,3,4,5) и
(4,5) соответственно
1.B перейдёт в B' с формой (1, 1, 4, 5)
2.B' перейдёт в B'' с формой (2, 3, 4, 5),
• причём B''[i, j, k, l] == B''[i', j', k, l]
• для всех i,i' из 0..1; j,j' из 0..2; k из 0..3 и l из 0..4
54
Broadcasting
(математические выкладки)
>>> a = np.array([[1, 2, 3], # (2, 3)
[4, 5, 6]])
>>> b = np.array([10, 20, 30]) # (3,)
>>> a + b2 # эквивалентно a + b
array([[11, 22, 33],
[14, 25, 36]])
55
Из файла и в файл
ndarray.tofile(файл_или_имя_файла,
sep=разделитель, # '' для бин.файла
format=строка_формата)
57
Запись и чтение массива
>>> a = np.array([[2., 3.1, 4], [0, 3, 9]]);
>>> f = open('array.txt')
>>> f.read()
'2.0,3.1,4.0,0.0,3.0,9.0'
>>> f.close()
>>> a1 = a1.reshape((2,3))
58
Строение ndarray,
ссылки и значения,
работа над формами
60
Массив и его строение
buffer
buffer+offset shape == (5, 4, 3)
strides == (5*4*8,
4*8,
1 3 22 4 6
4 эл.
8)
3 8 1 7 2
a=
9 5 3 8 4
itemsize == 8
6 1 2 7 9 offset == 0
.
эл
3
5 элементов
61
strides (на примере 3D)
strides[0]: strides[1]:
1 3 22 4 6 6
3 8 1 7 2 размер 2 размер
4 эл.
* элемента * элемента
4 эл.
9 5 3 8 4 4
6 1 2 7 9 9
5 элементов
В массиве (M, N, K)
M*N*e – толщина слоя
N*e – размер одномерной “колбасы”
e – размер элемента
Доказательство:
b[i,j,k] = a + z + i*d1 + j*d2 + k*d3 = a[i,j,k]
63
strides + slice (на примере 3D)
Пусть a.strides = (d1, d2, d3),
a.shape = (S1, S2, S3)
a.offset = z
a[i,j,k] = a + z + i*d1 + j*d2 + k*d3
Доказательство:
b[i,j,k] = a + z + d1*m + d2*n + d3*p + i*d1 + j*d2 + k*d3
= a + z + d1*(m+i) + d2*(n+j) + d3*(p+k) = a[i+m, j+n, p+k]
64
strides + slice (на примере 3D)
Любой слайс можно описать последовательностью
односторонних слайсов, например
a[m1:m2, n1:n2, k1:k2] =
a[m1:, n1:, k1:][:m2-m1, :n2-n1, :k2-k1]
>>> a.strides
(12, 4) # порядок как в C: a[i,j+1] следует за a[i,j]
>>> a.T.strides
(4, 12)
>>> b.strides
(4, 8) # порядок Fortran: a[i+1,j] следует за a[i,j]
>>> b.T.strides
(8, 4)
66
Когда numpy копирует?
● numpy не копирует
● когда простые присваивания
– b = a # python assignment, b – ссылка на a
● когда массив передают в функцию
– f(a) # в python объекты передаются по ссылке
67
Когда numpy копирует?
● numpy возвращает view
● когда явно вызывают "view"
– c = a.view() # c – view: у c свои shape, strides, offset, но
c ссылается на тот же буфер, что и a
● когда берут подмассивы
– s = a[:,1:3] # s – view
● иногда, когда вызывают reshape/ravel/T
68
Когда numpy копирует?
● numpy копирует
● когда явно вызывают copy
– d = a.copy() # у d всё своё, даже буфер
● иногда, когда вызывают reshape/ravel/T
69
пара слов о scipy
https://ru.wikipedia.org/wiki/SciPy 71
numpy + scipy = друзья
>>> import numpy; import scipy; import scipy.stats
>>> a = np.array([1,3,9])
72
Заключение
75
Do's & don'ts (3)
● Проход по массиву
● b = np.sin(a) # нормально
● b = np.array([math.sin(x)
for x in a]) # тормозит
● b = []
for x in a:
b.append(math.sin(x)) # тормозит
● Оставайтесь в рамках numpy.ndarray
● Индексация
● a[2,3,4] # элемент (2,3,4)
● a[(2,3,4)] # элемент (2,3,4)
● a[[2,3,4]] # три элем. с инд. 2, 3 и 4 76
Послесловие от любителя C++
● Не стоит думать, что это какое-то python-
специфичное волшебство
● C++ позволяет
● перегружать операторы [] и ()
● использовать наследование и динамический
полиморфизм
● использовать шаблоны и статический
полиморфизм
● компилировать быстрый код
● написать аналог numpy, который будет
ненамного более громоздким
77
C++: библиотека xtensor
#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
xt::xarray<double> arr1
{{1.0, 2.0, 3.0},
{2.0, 5.0, 7.0},
{2.0, 5.0, 7.0}};
xt::xarray<double> arr2
{5.0, 6.0, 7.0};
Материалы: http://inp.nsk.su/~grozin/python/
python * numpy * matplotlib * pandas * mpmath * SymPy * cython * plumbum * Iminuit * PyQt5
80
Благодарности
● Автор выражает благодарность
Соседкину А. П. за помощь.
81
.