NumPy (Numerical Python) е основният модул за числени изчисления в Python. NumPy съдържа бърза и ефективна по отношение на паметта реализация на подобна на списък масив структура от данни и съдържа полезни функции за линейна алгебра и произволни числа. Голяма част от NumPy всъщност е написана на езика за програмиране C
.
Масивът NumPy е подобен на структурата на данните list
на Python. Списъкът на Python може да съдържа произволна комбинация от типове елементи: цели числа, плаващи числа, низове, функции, обекти и т.н. Масивът NumPy, от друга страна, трябва да съдържа само един тип елемент в даден момент. По този начин масивите NumPy могат да бъдат много по-бързи и по-ефективни от паметта.
Както модулът Pandas
(за анализ на данни), така и модулът Scikit-Learn
(за машинно обучение) са изградени върху модула NumPy. Модулът Matplotlib
(за чертане) също работи добре с NumPy. Тези четири модула плюс базовия Python са практически всичко, от което се нуждаете за основно до средно ниво на машинно обучение.
Два други основни модула на Python, тясно свързани с машинното обучение, са както следва - въпреки че няма да ги покрием в нашите уроци:
SciPy
: Този модул е за числени изчисления, включително интеграция, диференциация, оптимизация, вероятностни разпределения и паралелно програмиране.StatsModels
: Този модул предоставя класове и функции за оценка на много различни статистически модели, както и за провеждане на статистически тестове и изследване на статистически данни.
Нека импортираме numpy
с обичайната конвенция за np
.
import numpy as np
Създаване на масиви с NumPy
Класът масив на NumPy се нарича ndarray (n-измерен масив). Известен е и под името масив.
- В масив NumPy всяко измерение се нарича ос, а броят на осите се нарича ранг.
- Например, матрица 3x4 е масив от ранг 2 (тя е двумерна).
- Първата ос има дължина 3, втората има дължина 4.
- Списъкът с дължини на оси на масив се нарича форма на масива.
- Например, формата на матрица 3x4 е
(3, 4)
. - Рангът е равен на дължината на формата.
- Размерът на масив е общият брой елементи, който е произведение на всички дължини на осите (напр. 3*4=12)
np.array
Най-лесният начин да създадете масив е да използвате функцията array
. Това приема всеки подобен на последователност обект (включително други масиви) и създава нов масив NumPy, съдържащ предадените данни.
arr1 = np.array([2, 10.2, 5.4, 80, 0]) arr1 array([ 2. , 10.2, 5.4, 80. , 0. ])
Вложените последователности, като списък от списъци с еднаква дължина, ще бъдат преобразувани в многоизмерен масив:
data = [[1, 2, 3, 4], [5, 6, 7, 8]] arr2 = np.array(data) arr2 array([[1, 2, 3, 4], [5, 6, 7, 8]]) arr2.shape (2, 4) arr2.ndim # equal to len(a.shape) 2 arr2.size 8
Други функции за създаване на масиви
Има няколко други удобни функции NumPy за създаване на масиви.
np.zeros
Създава масив, съдържащ произволен брой нули.
np.zeros(5) array([0., 0., 0., 0., 0.])
Също толкова лесно е да създадете 2-D масив (т.е. матрица), като предоставите кортеж с желания брой редове и колони. Например, ето една матрица 3x4:
np.zeros((2, 3)) # notice the double parantheses array([[0., 0., 0.], [0., 0., 0.]])
np.ones
Произвежда масив от всички единици.
np.ones((2, 3)) array([[1., 1., 1.], [1., 1., 1.]])
Как да създадете масив със същите стойности:
(np.pi * np.ones((3,4))).round(2) array([[3.14, 3.14, 3.14, 3.14], [3.14, 3.14, 3.14, 3.14], [3.14, 3.14, 3.14, 3.14]])
np.arange
Това е подобно на вградената range
функция на Python, но много по-бързо.
np.arange(5) array([0, 1, 2, 3, 4]) np.arange(1, 5) array([1, 2, 3, 4])
Работи и с плувки:
np.arange(1.0, 5.0) array([1., 2., 3., 4.])
Разбира се, можете да предоставите параметър на стъпка:
np.arange(1, 5, step = 0.5) array([1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])
np.linspace
Това е подобно на seq()
в R. Неговите входове са (начало, спиране, брой елементи) и връща равномерно разпределени числа за определен интервал. По подразбиране стоп стойността е включена.
np.linspace(0, 10, 6) array([ 0., 2., 4., 6., 8., 10.])
np.quantile
Изчислява q-тия квантил на своя вход. Играе добре с np.linspace
.
a = np.arange(1, 21) print('a =', a) quartiles = np.linspace(0, 1, 5) print('quartiles =', quartiles) a = [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20] quartiles = [0. 0.25 0.5 0.75 1. ] np.quantile(a, 0.5) # how to compute the median 10.5 np.quantile(a, quartiles) array([ 1. , 5.75, 10.5 , 15.25, 20. ])
np.rand
и np.randn
Редица функции са налични в модула random
на NumPy за създаване на масиви, инициализирани с произволни стойности. Например, тук е матрица, инициализирана с произволни плаващи числа между 0 и 1 (равномерно разпределение):
np.random.rand(2,3).round(3) array([[0.033, 0.139, 0.315], [0.985, 0.061, 0.478]])
Ето матрица, съдържаща произволни плаващи стойности, взети от едномерно „нормално разпределение“ (разпределение на Гаус) със средна стойност 0 и дисперсия 1:
np.random.randn(2,3).round(3) array([[-0.719, 0.3 , -1.702], [ 0.684, -0.227, -0.385]])
Типове данни за масиви
TypeDescriptionint1616-bit integer typesint3232-bit integer typesint6464-bit integer typesfloat16Плаваща запетая с половин точност float32Стандартна плаваща запетая с единична точност float64Стандартна плаваща запетая с двойна точностboolBoolean (True или False)string_StringobjectA стойност може да бъде всеки обект на Python
np.array.dtype
Масивите на NumPy също са ефективни отчасти, защото всичките им елементи трябва да имат един и същи тип (обикновено числа). Можете да проверите какъв е типът данни, като погледнете атрибута dtype
.
arr1 = np.array([1, 2, 3], dtype = np.float64) print("Data type name:", arr1.dtype.name) Data type name: float64 arr2 = np.array([1, 2, 3], dtype = np.int32) print(arr2.dtype, arr2) int32 [1 2 3]
np.array.astype
Можете изрично да конвертирате или преобразувате масив от един dtype
в друг, като използвате метода astype
.
arr2.dtype dtype('int32') arr2 = arr2.astype(np.float64) arr2.dtype # integers are now cast to floating point dtype('float64')
Ако имате масив от низове, представляващи числа, можете да използвате astype
, за да ги конвертирате в числова форма.
numeric_strings = np.array(['1.25', '-9.6', '42'], dtype = np.string_) numeric_strings array([b'1.25', b'-9.6', b'42'], dtype='|S4') numeric_strings.astype(float) # this will not take effect unless you do set it to a new variable! array([ 1.25, -9.6 , 42. ]) numeric_strings.dtype dtype('S4')
Аритметични операции с масиви
Всички обичайни аритметични оператори (+
, -
, *
, /
, //
, **
и т.н.) могат да се използват с масиви. Те се прилагат елементно.
a = np.array([14, 23, 32, 41]) b = np.array([5, 4, 3, 2]) print("a + b =", a + b) print("a - b =", a - b) print("a * b =", a * b) print("a / b =", a / b) print("a // b =", a // b) print("a % b =", a % b) print("a ** b =", a ** b) a + b = [19 27 35 43] a - b = [ 9 19 29 39] a * b = [70 92 96 82] a / b = [ 2.8 5.75 10.66666667 20.5 ] a // b = [ 2 5 10 20] a % b = [4 3 2 1] a ** b = [537824 279841 32768 1681]
Имайте предвид, че умножениетоне е матрично умножение.
Масивите трябва да имат еднаква форма. Ако не го направят, NumPy ще приложи правилата за излъчване, които са разгледани по-долу.
Преоформяне на масиви
В много случаи можете да конвертирате масив от една форма в друга, без да копирате данни.
np.array.shape
Промяната на формата на масив е толкова проста, колкото задаване на неговия атрибут shape
. Размерът на масива обаче трябва да остане същият.
g = np.arange(12) print(g) print("Rank:", g.ndim) [ 0 1 2 3 4 5 6 7 8 9 10 11] Rank: 1 g.shape = (6, 2) print(g) print("Rank:", g.ndim) [[ 0 1] [ 2 3] [ 4 5] [ 6 7] [ 8 9] [10 11]] Rank: 2
np.array.reshape
Друг начин да промените формата на масив е да използвате метода reshape()
, който връща нов обект на масив.
g2 = g.reshape(4,3) # you need to set this to a new variable to take effect! print(g2) print("Rank:", g2.ndim) [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]] Rank: 2
Какво ще кажете да станем мързеливи и да оставим NumPy да разбере подробностите?
g2 = g.reshape(4, -1) print(g2) [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]]
Как да конвертирате многоизмерен масив обратно в 1-измерен (известен още като сплескване на масив): можете да използвате метода flatten
.
f = np.arange(6).reshape(3,2) print(f) f = f.flatten() # you need to set this to a new variable to take effect! print(f) print(f.shape) [[0 1] [2 3] [4 5]] [0 1 2 3 4 5] (6,)
Добавяне и премахване на елементи
np.append
и np.insert
a = np.arange(6) print('original array:\n', a) b = np.append(a, 111) print('appending an element to the end:\n', b) c = np.insert(a, 0, 111) print('inserting an element at a specific position:\n', c) # watch out: these will NOT work: a.append(111), a.insert(0, 111) original array: [0 1 2 3 4 5] appending an element to the end: [ 0 1 2 3 4 5 111] inserting an element at a specific position: [111 0 1 2 3 4 5]
np.delete
a = np.arange(6) a c = np.delete(a, [0,1]) print('deleting the first two elements:\n', c) a.resize(2,3) print('a after resize():\n', a) e = np.delete(a, 0, axis=1) # you can delete an entire column by specifying axis=1 print('first column deleted:\n', e) f = np.delete(a, 0, axis=0) # or you can delete an entire row by specifying axis=0 print('first row deleted:\n', f) deleting the first two elements: [2 3 4 5] a after resize(): [[0 1 2] [3 4 5]] first column deleted: [[1 2] [4 5]] first row deleted: [[3 4 5]]
Копиране на масиви
NumPy обикновено не прави копия за ефективност. Повечето задания са само изгледи, а не копия. Ако искате копие, трябва да го кажете.
Можете да използвате np.array.copy
или np.copy
.
b = a = np.arange(6) a_copy = a.copy() # alternatively, a_copy = np.copy(a) print(a == a_copy) # element-wise comparison print(a is a_copy) # this is False print(a is b) # this is True a[0] = -111 # changing a has no effect on a_copy print(a_copy) [ True True True True True True] False True array([0, 1, 2, 3, 4, 5])
Излъчване
Излъчването описва как NumPy третира масиви с различни форми по време на аритметични операции. Излъчването може да се усложни, така че ви препоръчваме да го избягвате напълно, ако можете, и да направите едно от двете неща по-долу:
- Излъчване само на скалар с масив
- Излъчване на масиви с еднаква форма
In [40]:
A = np.arange(6).reshape(3,2) B = np.arange(6, 12).reshape(3,2) print(B) array([[ 6, 7], [ 8, 9], [10, 11]]) A + B array([[ 6, 8], [10, 12], [14, 16]]) 3 * A array([[ 0, 3], [ 6, 9], [12, 15]]) (A / 3).round(2) # float division array([[0. , 0.33], [0.67, 1. ], [1.33, 1.67]]) A // 3 # integer division array([[0, 0], [0, 1], [1, 1]], dtype=int32) 11 + A array([[11, 12], [13, 14], [15, 16]])
Поелементното умножение на матрицата се извършва от *
.
A * B array([[ 0, 7], [16, 27], [40, 55]])
За обикновено матрично умножение трябва да използвате np.dot
.
B_new = B.reshape(2,-1) B_new np.dot(A, B_new) array([[ 9, 10, 11], [39, 44, 49], [69, 78, 87]])
Условни изрази с масиви
x = np.array([10,20,30,40,50]) x >= 30 array([False, False, True, True, True]) x[x >= 30] array([30, 40, 50])
np.where
Връща индексите на елементи във входен масив, където даденото условие е изпълнено.
y = np.arange(10) print(y) print(np.where(y < 5)) [0 1 2 3 4 5 6 7 8 9] (array([0, 1, 2, 3, 4], dtype=int64),)
Изключително полезно: Можете да използвате where
за векторизирани изрази if-else.
compared_to_5 = list(np.where(y < 5, 'smaller', 'bigger')) print(compared_to_5) ['smaller', 'smaller', 'smaller', 'smaller', 'smaller', 'bigger', 'bigger', 'bigger', 'bigger', 'bigger']
Математически и статистически функции
Набор от математически функции, които изчисляват статистика за цял масив или за данните по дадена ос, са достъпни като методи на класа масив.
a = np.array([[-2.5, 3.1, 7], [10, 11, 12]]) print(a) [[-2.5 3.1 7. ] [10. 11. 12. ]] np.max(a) 12.0 np.min(a) -2.5 np.mean(a) 6.766666666666667 np.prod(a) -71610.0 np.std(a) 5.084835843520964 np.var(a) 25.855555555555554 np.sum(a) 40.6
Тези функции приемат незадължителен аргумент axis
, който ви позволява да поискате операцията да бъде извършена върху елементи по дадената ос. Например:
b = np.arange(12).reshape(2,-1) print(b) array([[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]]) print(b.sum(axis=0)) # sum across columns array([ 6, 8, 10, 12, 14, 16]) print(b.sum(axis=1)) # sum across rows array([15, 51])
Универсални функции
Универсална функция, или ufunc, е функция, която извършва поелементни операции върху данни в ndarrays. Можете да мислите за тях като за бързи векторизирани обвивки за прости функции, които приемат една или повече скаларни стойности и произвеждат един или повече скаларни резултати.
Много ufunc са прости трансформации по елементи, като sqrt или exp. Те се наричат унарни ufuncs.
z = np.array([[-2.5, 3.1, 7], [10, 11, 12]])
np.square
Квадрат по елементи на входа.
print(np.square(z)) array([[ 6.25, 9.61, 49. ], [100. , 121. , 144. ]])
np.exp
Изчислете експоненциала на всички елементи във входния масив.
print(np.exp(z)) array([[8.20849986e-02, 2.21979513e+01, 1.09663316e+03], [2.20264658e+04, 5.98741417e+04, 1.62754791e+05]])
Двоични универсални функции
Други, като add или maximum, вземат два масива (по този начин, двоичен ufunc) и връщат единичен масив като резултат:
x = np.array([3, 6, 1]) y = np.array([4, 2, 9]) print(x) print(y) [3 6 1] [4 2 9]
np.maximum
Максимум от елементи на масива по елементи — не бъркайте с np.max
, който намира максималния елемент в масива.
print(np.maximum(x,y)) array([4, 6, 9])
np.minimum
Минимум от елементи на масива по елементи — не бъркайте с np.min
, който намира минималния елемент в масива.
print(np.minimum(x,y)) array([3, 2, 1])
np.power
Първите елементи на масива са повдигнати на степени от втория масив, по елементи.
print(np.power(x,y)) array([81, 36, 1], dtype=int32)
Индексиране и нарязване на масиви
Едномерни масиви
Едномерните масиви NumPy могат да бъдат достъпни повече или по-малко като обикновените масиви на Python:
a = np.array([1, 5, 3, 19, 13, 7, 3]) print(a[3]) 19 print(a[2:5]) array([ 3, 19, 13]) print(a[2:-1]) array([ 3, 19, 13, 7]) print(a[:2]) array([1, 5]) print(a[2::2]) array([ 3, 13, 3]) print(a[::-1]) array([ 3, 7, 13, 19, 3, 5, 1])
Разбира се, можете да променяте елементи:
a[3]=999 print(a) array([ 1, 5, 3, 999, 13, 7, 3])
Можете също да промените срез от масив:
a[2:5] = [997, 998, 999] print(a) array([ 1, 5, 997, 998, 999, 7, 3])
Многомерни масиви
Многоизмерните масиви могат да бъдат достъпни по подобен начин чрез предоставяне на индекс или срез за всяка ос, разделени със запетаи:
b = np.arange(12).reshape(4, 3) print(b) array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]]) print(b[1, 1]) # row 2, col 2 (recall that Python slices starting at index 0) 4 print(b[0, :]) # row 1, all columns array([0, 1, 2]) b[:, 0] # all rows, column 1 array([0, 3, 6, 9])
Внимание: Обърнете внимание на фината разлика между тези два израза:
c = b[0, :] print(c) print(c.shape) [0 1 2] (3,) d = b[0:1, :] print(d) print(d.shape) [[0 1 2]] (1, 3)
Първият израз връща ред 1 като 1D масив с форма (3,)
, докато вторият връща същия ред като 2D масив с форма (1, 3)
.
Транспониране на масиви
Методът transpose()
на масив транспонира масива.
a = np.arange(10).reshape(5,-1) print(a) array([[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]) a = a.transpose() # notice the assignment for this method to work! print(a) array([[0, 2, 4, 6, 8], [1, 3, 5, 7, 9]])
Комбиниране на масиви
np.vstack
: подреждане на масиви вертикално
a = 1 + np.arange(3) b = -1 * a c = 10 + a print(a) print(b) print(c) d = np.vstack((a, b, c)) # notice the double parantheses print('stack vertically:\n', d) [1 2 3] [-1 -2 -3] [11 12 13] stack vertically: [[ 1 2 3] [-1 -2 -3] [11 12 13]]
np.hstack
: подрежда масивите хоризонтално
d = np.hstack((a, b, c)) # notice the double parantheses print('stack horizontally:\n', d) stack horizontally: [ 1 2 3 -1 -2 -3 11 12 13]
Сортиране на масиви
Можете да използвате метода sort
на масив, но обърнете внимание, тъй като сортирането се извършва на място!
a = np.array([3, 5, -1, 0, 11]) print(a) sort_output = a.sort() print('a has been sorted in place:\n', a) print(sort_output) # tricky: this will print None! [ 3 5 -1 0 11] a has been sorted in place: [-1 0 3 5 11] None
Ако не искате да сортирате на място, трябва да използвате np.sort
.
a = np.array([3, 5, -1, 0, 11]) print(a) b = np.sort(a) print(b) print('Notice a is not changed:\n', a) [ 3 5 -1 0 11] [-1 0 3 5 11] Notice a is not changed: [ 3 5 -1 0 11]
Ако искате обратно сортиране, трябва да го направите индиректно, тъй като няма директна опция за това в методите sort
.
a_reverse_sorted = np.sort(a)[::-1] print(a_reverse_sorted) [11 5 3 0 -1]
Упражнения
1- Инициализирайте 2D масив 5 × × 3 с всички числа, делими на 3 между 3 и 48. СЪВЕТ: Аргументът на np.arange
step
. Например, можете да създадете масив от 0, 2, 4, 6, 8, като извикате np.arange(0, 10, step = 2)
. След това изрежете последната колона от масива.
2- Създайте масив, кажете a = np.random.uniform(1, 10, 10)
. Намерете местоположението или индекса на максималната стойност в a
. Какво ще кажете за местоположението на минималната стойност? СЪВЕТ: използвайте методите argmax
и argmin
3- Създайте следния масив и намерете максималните стойности във всеки ред. Какво ще кажете за максималните стойности по колони? СЪВЕТ: използвайте np.amax
.
𝐴=[12374−1]A=[13427−1]
4- Липсващи стойности като NA
и nan
не са необичайни в науката за данни (технически nan
не е липсваща стойност. То означава не-число.) Създайте следната матрица, която съдържа едно nan
, като използвате np.nan
.
𝐵=[1237nan−1]B=[13nan27−1]
5- Намерете максималните стойности по колони и по ред в B
, създадени в предишния въпрос. np.amax
връща ли някаква стойност? СЪВЕТ: Опитайте метода np.nanmax
.
Възможни решения
1- Инициализиране и нарязване на масиви
import numpy as np # Create and reshape the array myarray = np.arange(3, 48, step = 3) myarray.shape = (5, 3) # Slice the last column myarray[:,2]
2- Индексиране на максимума и минимума
import numpy as np a = np.random.uniform(1, 10, 10) a a.argmax() # Find the maximum index a.argmin() # Find the minimum index
3- Максимални и минимални стойности по колони и редове.
import numpy as np A = np.array([[1, 3, 4],[2, 7, -1]]) np.amax(A, axis = 0) # Column-wise np.amax(A, axis = 1) # Row-wise
4- Създаване на nan
с numpy
.
import numpy as np B = np.array([[1, 3, np.nan],[2, 7, -1]])
5- Максимални и минимални стойности по колони и редове при наличие на nan
стойности.
import numpy as np B = np.array([[1, 3, np.nan],[2, 7, -1]]) np.nanmax(B, axis = 0) # Column-wise np.nanmax(B, axis = 1) # Row-wise