Введение в научный Python-1. Введение в научный Python
Скачать 6.2 Mb.
|
Векторные и матричные операции над массивами. Одномерные и двумерные массивы интерпретируются как вектора и матрицы, если они используются в качестве аргументов функций, которые вычисляют скалярное или векторное произведение. Функция dot() вычисляет скалярное произведение одномерных массивов одинаковой длины. >>> np.dot(3, 4) # скалярное умножение чисел 12 >>> a=np.array([1,2,3,4]) >>> b=np.array([ -3,-2,-1,0]) >>> np.dot(a,b) # скалярное умножение «векторов» -10 Для двумерных массивов функция dot() выполняет матричное умножение. >>> a=np.array([[1,2,3],[4,5,6]]) >>> print(a) [[1 2 3] [4 5 6]] >>> b=np.array([[1,2],[3,4],[5,6]]) >>> print(b) [[1 2] [3 4] [5 6]] >>> np.dot(a,b) # матричное умножение массивов array([[22, 28], [49, 64]]) 77 Массивы имеют метод dot( ), работающий аналогично функции dot( ). >>> a=np.array([[1,2],[3, -4]]) >>> b=np.array([1,3]) >>> a.dot(b) array([ 7, -9]) Это удобно, когда нужно выполнить подряд несколько операций скалярного произведения, например, >>> a.dot(b).dot(b) -20 Функции dot( ) можно передавать аргументы – списки, а не только массивы. >>> a=[[-1,3],[4,1]] >>> b=[[1,2],[3,4]] >>> print(np.dot(a,b)) [[ 8 10] [ 7 12]] Однако, списки не имеют метода dot( ). Матричное произведение двумерных массивов выполняет также функция matmul() , которой можно передавать аргументы – списки. Она появилась в пакете numpy в версии 1.10, и в более ранних версиях вам следует использовать функцию dot. >>> np.matmul(a, b) array([[ 8, 10], [ 7, 12]]) Функцию matmul()можно использовать и для вычисления произведения матрицы на вектор. >>> b=[1,2] >>> np.matmul(a, b) array([5, 6]) >>> np.matmul(b, a) array([7, 5]) Функция matmul() с аргументами векторами вычисляет скалярное произведение. >>> np.matmul([1,2,3],[4,5,6]) 32 При этом компоненты вектора могут быть комплексными числами. >>> np.matmul([2 -1j,3+2j],[5+2j,2-3j]) (24-6j) Функция matmul() отличается от dot() тем, что не вычисляет произведение чисел. Но она умеет вычислять произведение трехмерных массивов, интерпретируя каждый слой таких массивов как матрицу, и вычисляя послойное матричное произведение. Для вычисления скалярного произведения векторов можно использовать функцию vdot(). >>> np.vdot([1,2,3],[4,5,6]) 32 78 В отличие от matmul, функция vdot выполняет комплексное сопряжение первого вектора, если его компоненты являются комплексными числами. >>> np.vdot([2 -1j,3+2j],[5+2j,2-3j]) (8-4j) Если аргументы функция vdot() являются массивами большей размерности, чем единица, то они (массивы) выравниваются до одномерных. >>> a=[[1,2],[3,4]] >>> b=[[1, -1],[2,3]] >>> np.vdot(a,b) 17 Функция cross() вычисляет векторное произведение векторов (одномерных массивов или списков, содержащих три или два элемента) >>> x = [1, 2, 3] >>> y = [4, 5, 6] >>> np.cross(x, y) array([-3, 6, -3]) >>> x = [1, 2] >>> y = [4, 5] >>> np.cross(x, y) array(-3) Внешнее произведение векторов (двух одномерных массивов) j i j i v u a вычисляет функция outer(). >>> u=np.array([2,3]); >>> v=np.array([5,10,15]) >>> a=np.outer(u,v);print(a) [[10 20 30] [15 30 45]] Тот же результат можно получить с помощью матричного умножения, но для этого вместо одномерных массивов u и v следует использовать двумерные массивы с теми же данными. >>> U=np.array([[2,3]]).T; print(U) # array.Т – транспонирование массива [[2] [3]] >>> V=np.array([[5,10,15]]);V array([[ 5, 10, 15]]) >>> np.dot(U, V) array([[10, 20, 30], [15, 30, 45]]) Здесь, чтобы матричное умножение сработало, мы создали матрицу U, состоящую из одного столбца, и матрицу V, состоящую из одной строки. Реорганизация массивов. Функция и метод reshape() реорганизовывают массив. Число элементов новой матрицы должно совпадать с числом элементов исходной. При этом данные массива не меняются. >>> a=np.arange(0,1,0.25) 79 >>> a array([ 0. , 0.25, 0.5 , 0.75]) >>> b=a.reshape(2,2) >>> b array([[ 0. , 0.25], [ 0.5 , 0.75]]) >>> d = np.arange(6).reshape((2,3)) >>> print(d) [[0 1 2] [3 4 5]] Можно изменить структуру массива, если атрибуту shape присвоить кортеж новых размеров. В этом случае изменится форма массива, но его данные останутся без изменений. >>> b=np.arange(4);b array([0, 1, 2, 3]) >>> b.shape (4,) >>> b.shape=2,2 >>> b array([[0, 1], [2, 3]]) Точно также двумерный массив можно сделать одномерным. >>> A=np.array([[1,2,3],[4,5,6]]); A array([[1, 2, 3], [4, 5, 6]]) >>> A.shape (2, 3) >>> A.shape=(6,); A array([1, 2, 3, 4, 5, 6]) Как видите, присваивание значений атрибуту shape делает то же самое, что и метод reshape(). Имеется функция resize(a[,new_size]), которая возвращает новый массив, элементами которого являются элементы массива a. Если количество элементов нового массива больше, чем исходного, то новый массив заполняется копиями элементов исходного. Элементы повторяются в том порядке, в котором они хранятся в памяти. >>> a=np.array([3,5,7, -2,4]) >>> np.resize(a,(9,)) array([ 3, 5, 7, -2, 4, 3, 5, 7, -2]) >>> a=np.array([[0,1],[2,3]]) >>> np.resize(a,(2,3)) array([[0, 1, 2], [3, 0, 1]]) Метод resize(new_size) отличается от функции тем, что новый массив заполняется нулями, а не копиями данных исходного массива. >>> b=np.array([8,9,7,5,3]) 80 >>> b.resize(9) >>> b array([8, 9, 7, 5, 3, 0, 0, 0, 0]) Если на массив имеется другая ссылка, то изменение размера невозможно! >>> b=np.array([8,9,7,5,3]) >>> c=b >>> b.resize(9) Ошибка! Изменение размера массива невозможно также в случае, если до этого он подвергался реорганизации с помощью функции или метода reshape(). Преобразовать одноэлементный массив в скаляр можно с помощью функции asscalar(). >>> x=np.array([123]); x array([123]) >>> y=np.asscalar(x);y 123 Метод diagonal() возвращает вектор из диагональных элементов двумерного массива. >>> a = np.arange(9).reshape((3,3)) >>> print(a) [[0 1 2] [3 4 5] [6 7 8]] >>> a.diagonal() array([0, 4, 8]) >>> a = np.arange(6).reshape((2,3)) >>> print(a) [[0 1 2] [3 4 5]] >>> a.diagonal() array([0, 4]) Функция transpose() транспонирует массив. >>> x=np.array([[1,2,3],[4,5,6],[7,8,9]]) >>> print(x) [[1 2 3] [4 5 6] [7 8 9]] >>> print(np.transpose(x)) [[1 4 7] [2 5 8] [3 6 9]] У массивов имеется также метод transpose(). >>> x = np.array([[1,2,3]]) # двойные квадратные скобки >>> x.transpose() 81 array([[1], [2], [3]]) Транспонирование выполняется над массивами размерности больше или равной 2, иначе массив оставляется без изменений. В предыдущем примере мы использовали двойные квадратные скобки для того, чтобы массив x был двумерным. Вместо функции и метода transpose() можно использовать атрибут T, который также возвращает транспонированный массив. >>> a=np.arange(9).reshape((3,3)) >>> a.T array([[0, 3, 6], [1, 4, 7], [2, 5, 8]]) >>> a = np.array([[1],[2],[3]]) >>> a array([[1], [2], [3]]) >>> a.T array([[1, 2, 3]]) >>> a.T.T array([[1], [2], [3]]) Создание массивов из других массивов. Массивы можно конструировать путем объединения уже имеющихся массивов. Функция append() «пристраивает» справа к одному массиву другой >>> a=np.array([1,2,3]) >>> a=np.append(a,[10,20,30]) >>> a array([ 1, 2, 3, 10, 20, 30]) Функции delete, insert и append не меняют массив, а возвращают новый, в котором удалены, вставлены в середину или добавлены в конец какие-то элементы. >>> a=np.arange(2,9,1);print(a) [2 3 4 5 6 7 8] >>> b=np.delete(a,[2,4]);print(b) # [2,4] – индексы удаляемых элементов [2 3 5 7 8] >>> c=np.insert(a,2,23) # вставить на 2-е место число 23 >>> c array([2,3,23,4,5,6,7,8]) >>> a=np.arange(2,9,1);print(a) [2 3 4 5 6 7 8] >>> c=np.insert(a,2,[5,10,15]); print(c) # с 2–го индекса вставить 5,10,15 [ 2 3 5 10 15 4 5 6 7 8] 82 >>> np.insert(a,[0,2,5],[11,22,33]) # [0,2,5] – индексы мест, # [11,22,33]) – значения вставляемых элементов array([11, 2, 3, 22, 4, 5, 6, 33, 7, 8]) По умолчанию многомерные массивы выравниваются до одномерных, а затем выполняется вставка элементов. >>> a = np.array([[1, 2], [3, 4], [5, 6]]) >>> a array([[1, 2], [3, 4], [5, 6]]) >>> np.insert(a, 1, 5) array([1, 5, 2, 3, 4, 5, 6]) Функция hstack() соединяет массивы по горизонтали. >>> a=np.arange(2,6,1);print(a) [2 3 4 5] >>> b=np.arange(10,15,1);print(b) [10 11 12 13 14] >>> np.hstack((a,b)) array([ 2, 3, 4, 5, 10, 11, 12, 13, 14]) >>> a = np.array([[1],[2],[3]]) >>> b = np.array([[5],[6],[7]]) >>> np.hstack((a,b)) array([[1, 5], [2, 6], [3, 7]]) Функция vstack() соединяет массивы по вертикали. >>> a = np.array([1, 2, 3]) >>> b = np.array([5, 6, 7]) >>> np.vstack((a,b)) array([[1, 2, 3], [5, 6, 7]]) >>> a=np.arange(1,7,1).reshape((2,3)); print(a) [[1 2 3] [4 5 6]] >>> b=np.arange(10,70,10).reshape((2,3));print(b) [[10 20 30] [40 50 60]] >>> np.vstack((a,b)) array([[ 1, 2, 3], [ 4, 5, 6], [10, 20, 30], [40, 50, 60]]) В пакете numpy имеется «оператор» numpy.r_ , предназначенный для конкатенации массивов. Кроме того, его можно использовать для их создания. Это не функция, его аргументы следует заключать в квадратные скобки. 83 >>> np.r_ [1:10:1] # создание массива целых чисел 1≤X<10 array([1, 2, 3, 4, 5, 6, 7, 8, 9]) Ессли в команде np.r_ [start:stop:step] параметр step содержит мнимую единицу, т.е. имеет вид Nj, то N представляет количество точек на отрезке [start,stop] (а не шаг!), и начальная и конечная точки включаются в массив. >>> z=np.r_ [1:4:7j]; z # 7 - это количество точек array([1., 1.5, 2., 2.5, 3.,3.5, 4.]) Если операнды являются массивами (или числами, разделенными запятыми), то они объединяются (по первой оси) в новый массив. В следующей команде мы создаем массив из двух массивов, между которыми вставляем два числа (нули). >>> np.r_[np.array([1,2,3]), 0, 0, np.array([4,5,6])] array([1, 2, 3, 0, 0, 4, 5, 6]) В следующем примере умножение списка на число интерпретируется как повторение списка. >>> np.r_[-1:1:5j, [0]*3, 5, 6] array([-1. ,-0.5, 0., 0.5, 1., 0., 0., 0., 5., 6.]) Если первым аргументом является строка, состоящая из одного символа – цифры, то он интерпретируется как номер оси, по которой будет выполняться конкатенация. >>> a = np.array([[ 0, 1, 2], [3, 4, 5]]) >>> np.r_ ['0', a, a] # соединение по 0–ой оси array([[0, 1, 2], [3, 4, 5], [0, 1, 2], [3, 4, 5]]) >>> np.r_ ['1', a, a] # соединение по 1–ой оси array([[0, 1, 2, 0, 1, 2], [3, 4, 5, 3, 4, 5]]) Если первым аргументом является строка „r‟ или „c‟, то результатом конкатенации по строкам (row) или столбцам (column) является матрица. >>> np.r_['r',[1,2,3], [4,5,6]] matrix([[1, 2, 3, 4, 5, 6]]) >>> np.r_['c',[1,2], [5,6]] matrix([[1], [2], [5], [6]]) Массивы можно расщеплять. Функция hsplit() разделяет массив по горизонтали на несколько подмассивов. Номера столбцов, по которым происходит разрыв, указываются в виде списка вторым аргументом этой функции. >>> a=np.arange(9);print(a) [0 1 2 3 4 5 6 7 8] >>> x,y,z=np.hsplit(a,[2,5]) >>> print(x); print(y); print(z) 84 [0 1] [2 3 4] [5 6 7 8] Если второй аргумент функции hsplit является числом N, то массив делится на N одинаковых по размеру подмассивов. При этом размеры массива и подмассивов должны быть согласованы. >>> a = np.arange(8).reshape(2, 4); print(a) [[0 1 2 3] [4 5 6 7]] >>> x,y=np.hsplit(a, 2) # разбиение на два подмассива одинакового размера >>> print(x) [[0 1] [4 5]] >>> print(y) [[2 3] [6 7]] Функция vsplit() разделяет массив по вертикали на несколько подмассивов. Если второй аргумент функции является числом N, то массив делится на N одинаковых по размеру массивов. >>> a = np.array([[1,2],[3,4],[5,6],[7,8]]) >>> print(a) [[1 2] [3 4] [5 6] [7 8]] >>> x,y=np.vsplit(a,2) # разбиение на два подмассива одинакового размера >>> print(x) [[1 2] [3 4]] >>> print(y) [[5 6] [7 8]] Если массивы должны иметь различные размеры, то номера строк, по которым будет происходить разбиение, указываются вторым аргументом в квадратных скобках. >>> a = np.array([[1,2],[3,4],[5,6]]) ; print(a) [[1 2] [3 4] [5 6]] >>> x,y=np.vsplit(a,[1]) >>> print(x) [[1 2]] >>> print(y) [[3 4] [5 6]] >>> x,y,z=np.vsplit(a,[1,2]) 85 >>> print(z) [[5 6]] Имеются также функции, перемещающие элементы в пределах массива, точнее возвращающие новые массивы, содержащие прежние данные, но в другом порядке. Например, функция roll(x,n) прокручивает вправо элементы массива x на n позиций. >>> x = np.arange(9) >>> print(x) [0 1 2 3 4 5 6 7 8] >>> x2=np.roll(x,2) >>> print(x2) [7 8 0 1 2 3 4 5 6] Многомерный массив перед прокручиванием преобразуется в одномерный (в двумерном массиве выравнивание выполняется построчно), а затем его форма восстанавливается. >>> x=np.array([[1,2,3],[4,5,6],[7,8,9]]) >>> print(np.roll(x,2)) [[8 9 1] [2 3 4] [5 6 7]] Функция sort() возвращает отсортированную копию массива >>> a=np.array([5,1, -2,6,0,9]) >>> b=np.sort(a); print(b) [-2 0 1 5 6 9] Метод sort() сортирует текущий массив. >>> a=np.array([5,1, -2,6,0,9]) >>> a.sort(); print(a) [-2 0 1 5 6 9] В двумерных массивах данные сортируются в каждой строке >>> b=np.array([[2,5,0],[4,1,3]]) >>> print(np.sort(b)) [[0 2 5] [1 3 4]] Логические массивы. Логические (булевы) массивы состоят из логических элементов True и False. >>> a = np.array([True, False, True, False]) >>> a array([True,False, True,False],dtype=bool) >>> b = np.array([1, 1, 0, 0], dtype=bool) >>> b array([True, True, False, False], dtype=bool) Обычно логические массивы создаются при выполнении операций сравнения. >>> x=np.array([ -3.5, 2.2, -5.1, 4.3, 6.8, -8.0, 4.0]) >>> y=np.array([4.5, 2.2, -2.1, 4.3, 7.8, -1.0, 4.0]) >>> Z=x==y 86 >>> Z array([False, True, False, True, False, False, True],dtype=bool) >>> X=x<0 >>> X array([True, False, True, False, False, True, False],dtype=bool) Двойное неравенство для массивов не работает. >>> -3 Для сложных условий следует использовать функции пакета numpy, которые реализуют операции логической алгебры для массивов: logical_and(), logical_or(), logical_xor(), logical_not(). Например, последнее неравенство можно записать в виде >>> np.logical_and(x>=-3,x<6) array([False,True,False,True,False,False,True],dtype=bool) Вот более сложное логическое условие >>> np.logical_not(np.logical_or(x>0,y>2)) array([False,False,True,False,False,True,False],dtype=bool) Функция any(логический_массив) пакета numpy возвращает True, если хотя бы один из элементов логического_массива равен True. >>> np.any(X) True Функция all(логический_массив) возвращает True, если все элементы логического_массива равны True. >>> np.all(X) False >>> a = np.array([1, -2, 3, -1]) >>> b = np.array([2, 2, 3, 2]) >>> c = np.array([6, 4, 7, 5]) >>> ((a <= b) & (b <= c)).all() True Функция array_equal(arr1, arr2) возвращает True или False в зависимости от того совпадают ли значения элементов массива arr1 с соответствующими элементами массива arr2. >>> np.array_equal(a,b) False >>> d=np.array([4,1,6,2]) -3 >>> np.array_equal(a,d) True Работа с элементами массивов. Доступ к элементам массива осуществляется с использованием индексации. При этом можно использовать список индексов. >>> a = np.arange(6) >>> print(a[[1,3,5]]) [ 1. 3. 5.] Имеется возможность логической индексации. Для этого используются булевы массивы. 87 >>> a=np.array([2,-1,3,4,-5,7,-2,6,-7]); print(a) [ 2 -1 3 4 -5 7 -2 6 -7] >>> b=a>0; print(b) # массив b является логическим [True False True True False True False True False] Логическая индексация имеет следующий формат: основной_массив[логический_массив], где логический массив должен иметь тот же размер, что и основной. Результатом этой операции является массив, составленный из элементов основного массива, для которых в логическом массиве соответствующие элементы равны True. >>> c=a[b]; print(c) [2 3 4 7 6] Пусть из двумерного массива B требуется выбрать все отрицательные элементы и записать их в одномерный массив F. >>> B=np.array([[4, -3, -1],[ 2, 7, 0],[ -5, 1, -2]]); print(B) [[ 4 -3 -1] [ 2 7 0] [-5 1 -2]] Сначала создадим логический массив. >>> ind=B<0; print(ind) [[False True True] [False False False] [ True False True]] Использование логического массива ind в качестве индекса исходного массива B позволяет решить поставленную задачу >>> F=B[ind]; print(F) [-3 -1 -5 -2] Можно было обойтись и без вспомогательного массива ind, написав сразу >>> F = B[B<0]; print(F) [-3 -1 -5 -2] Аналогично >>> A=np.arange( -3,6).reshape(3,3);print(A) [[-3 -2 -1] [ 0 1 2] [ 3 4 5]] >>> C=A[A>B]; print(C) [-2 2 3 4 5] Логическое индексирование позволяет выбрать из массива элементы, удовлетворяющие определенным условиям, которые заданы логическим выражением. Если требуется присвоить новое значение элементам массива, удовлетворяющим определенному условию, то массив с логическим индексом должен войти в левую часть оператора присваивания. >>> A=np.arange(1,10).reshape(3,3);print(A) |