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