import numpy as np
# Uwaga: b jest tablicą liczb zmiennoprzecinkowych, pomimo, że podaliśmy liczby całkowite
a = np.array([1,2,3])
print(a)
b = np.array([1,2,3],dtype=np.float32)
print(b)
[1 2 3] [1. 2. 3.]
Także możemy przekształcać list
z python na tablicę numpy:
l = [1,2,3] # także można użyć tuple
print(type(l))
a = np.array(l)
print(a)
b = np.array(l, dtype=np.float32)
print(b)
<class 'list'> [1 2 3] [1. 2. 3.]
Możemy też tworzyć tablice wielowymiarowe:
a = np.array([[1,2,3], [4,5,6]])
print("tablica a: ")
print(a)
print(type(a[0]))
#Uwaga: wymiary muszą się zgadzać, inaczej numpy potraktuje to jak tablicę python list:
b = np.array([[1,2,3], [4,5]])
print("tablica b: ")
print(b)
print(type(b[0]))
tablica a: [[1 2 3] [4 5 6]] <class 'numpy.ndarray'> tablica b: [list([1, 2, 3]) list([4, 5])] <class 'list'>
Są też funkcje: np.ones
, np.zeros
, np.eye
i np.arange
, których używamy do tworzenia tablic wypełnionych jedynkami, zerami, macierz identyczności i liczb z przedzału z odpowiednim krokiem odpowiednio:
a = np.zeros((4,5))
print(a, '\n')
b = np.eye(4)
print(b, '\n')
r = np.arange(2, 3, 0.2)
print(r, '\n')
c = np.ones((4,5,3)) # możemy tworzyć macierze o dowolnych wymiarach
print(c)
[[0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.]] [[1. 0. 0. 0.] [0. 1. 0. 0.] [0. 0. 1. 0.] [0. 0. 0. 1.]] [2. 2.2 2.4 2.6 2.8] [[[1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.]] [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.]] [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.]] [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.]]]
W numpy możemy wybierać elementy tablicy jak w zwykłej list
z python:
a = np.array([[1,2,3], [4,5,6]])
print(a[0]) # zwraca [1 2 3]
print(a[1, 1]) # zwraca 5
#równoważny zapis
# print(a[1][1])
[1 2 3] 5
Także możemy zrobić array slicing, która ogólnie wygląda tak a[l, r] i zwraca elementy z zakresu [l, r) :
a = np.array([[ 0, 1, 2, 3], # 0
[ 4, 5, 6, 7], # 1
[ 8, 9, 10, 11], # 2
[12, 13, 14, 15], # 3
[16, 17, 18, 19]]) # 4
#0 #1 #2 #3
print(a[2:]) #zwraca wiersze od indeksu 2 do końca
[[ 8 9 10 11] [12 13 14 15] [16 17 18 19]]
print(a[:2]) #zwraca wiersze od indeksu 0 do 2 wyłącznie
[[0 1 2 3] [4 5 6 7]]
print(a[2:4]) #zauważmy że żeby dostać jeszcze ostatni wiersz musimy podać a[2:5]
[[ 8 9 10 11] [12 13 14 15]]
print(a[2:4, 1:3])
#równoważne z
# b = a[2:4]
# print(b[:, 1:3]) # b[:] zwraca wszystkie wiersze
[[ 9 10] [13 14]]
print(a[:, 2]) #z każdego wiersza wybiera element o indeksie 2
[ 2 6 10 14 18]
Można także używać booli jako maskę dla wybierania elementów
mask = [True, True, True, False]
print(a[:, mask])
[[ 0 1 2] [ 4 5 6] [ 8 9 10] [12 13 14] [16 17 18]]
Żeby zobaczyć jakiego rozmiaru jest tablica używamy zmiennej shape, która zwraca tuple
z wymiarami:
print(a.shape)
print(a.shape[1]) #długość wiersza
(5, 4) 4
# macierz a jest z poprzedniago przykładu
print(a.reshape(-1), '\n') # a.flatten()
print(a.reshape(4,5), '\n')
print(a.reshape(2,10), '\n')
# print(a.reshape(2,8)) #ValueError: cannot reshape array of size 20 into shape (2,8)
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19]] [[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19]]
a = np.ones((300, 32, 32, 3))
print(a.shape,'\n')
print(a.reshape(300, -1).shape) # 32*32*3 = 3072
print(a.reshape(300, 32, -1, 3).shape)
(300, 32, 32, 3) (300, 3072) (300, 32, 32, 3)
To jest użyteczne do podobnych operacij (broadcasting, numpy functions):
a = np.random.randn(2,3,4) # numpy functions
print(a,'\n')
print(a<0,'\n') # broadcasting
a[a<0] = 0
print(a)
[[[-0.6448891 0.95988904 -0.79272592 -0.06970128] [-0.97003444 0.92054643 -0.65928497 -0.47546803] [-0.15718798 1.3399144 -0.71455218 0.01069881]] [[-1.08111028 0.73841436 0.28729008 -0.08504842] [-0.56170564 1.31879558 -0.54805955 -0.55464583] [ 0.59014749 -2.37489079 0.40210844 -0.41835986]]] [[[ True False True True] [ True False True True] [ True False True False]] [[ True False False True] [ True False True True] [False True False True]]] [[[0. 0.95988904 0. 0. ] [0. 0.92054643 0. 0. ] [0. 1.3399144 0. 0.01069881]] [[0. 0.73841436 0.28729008 0. ] [0. 1.31879558 0. 0. ] [0.59014749 0. 0.40210844 0. ]]]
Możemy wykorzystywać list
albo np.array
jak listę elementów do pobrania:
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])
b = np.array([0,2,3])
print(a[:, [0,1,3]],'\n') #pobiera z każdego wiersza elementy o indeksach 0,2,3
print(a[b, :]) # bierze wszystkie elementy z wierszów o indeksach 0,2,3
[[ 0 1 3] [ 4 5 7] [ 8 9 11] [12 13 15] [16 17 19]] [[ 0 1 2 3] [ 8 9 10 11] [12 13 14 15]]
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])
# elementy nie muszą być unikalne jak w przykładzie
b = a[:, [2,2,2]]
print(b, '\n')
# b jest nową tablicą więc wszystkie zmiany b nie wpłyną na a
b[0,0] = 55
print(b,'\n')
print(a)
[[ 2 2 2] [ 6 6 6] [10 10 10] [14 14 14] [18 18 18]] [[55 2 2] [ 6 6 6] [10 10 10] [14 14 14] [18 18 18]] [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15] [16 17 18 19]]
Uwaga operacja b = a
nie będzie operacją kopiowania, i b
zarowno jak i a
będą wskazywać na tą samą macierz
a = np.array(range(5)) # [0 1 2 3 4]
b = a
b[1] = 23
print("macierz b",'\n', b, '\n')
print("macierz a",'\n', a, '\n')
macierz b [ 0 23 2 3 4] macierz a [ 0 23 2 3 4]
Żeby tego uniknąć można użyć funkcji np.copy()
a = np.array(range(5)) # [0 1 2 3 4]
b = a.copy() # lub np.copy(a)
b[0]=10 # b = [10 1 2 3 4]
print(a)
[0 1 2 3 4]
Broadcasting jest potężnym mechanizmem w Numpy który pozwala wykonywać operacje na macierzach w szybki i zręczny sposób:
a = np.arange(20).reshape(5, 4) #[[ 0 1 2 3]
#[ 4 5 6 7]
#[ 8 9 10 11]
#[12 13 14 15]
#[16 17 18 19]]
b = np.array([1, 2, 3, 4])
a += b # a = a + b
print(a, '\n')
a -= b
print(a,'\n')
[[ 1 3 5 7] [ 5 7 9 11] [ 9 11 13 15] [13 15 17 19] [17 19 21 23]] [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15] [16 17 18 19]]
operacja a += b
może być jawnie zapisana w ten sposób:
for i in range(a.shape[0]):
for j in range(a.shape[1]):
a[i, j] += b[j]
print(a, '\n')
a -= b
[[ 1 3 5 7] [ 5 7 9 11] [ 9 11 13 15] [13 15 17 19] [17 19 21 23]]
a *= b
print(a, '\n')
a //= b
print(a, '\n')
# a /= b # Uwaga w numpy używanie /= dla np.int64(takiego typu są liczby całkowite defaultowo) spowoduje błąd
# funkcja np.astype przyjmuje jako argument typ i rzutuje(konwertuje) elementy tablicy na ten typ
a = np.arange(20).astype(np.float64).reshape(5, 4) #[[ 0 1 2 3]
#[ 4 5 6 7]
#[ 8 9 10 11]
#[12 13 14 15]
#[16 17 18 19]]
a /= b
print(a)
[[ 0 2 6 12] [ 4 10 18 28] [ 8 18 30 44] [12 26 42 60] [16 34 54 76]] [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15] [16 17 18 19]] [[ 0. 0.5 0.66666667 0.75 ] [ 4. 2.5 2. 1.75 ] [ 8. 4.5 3.33333333 2.75 ] [12. 6.5 4.66666667 3.75 ] [16. 8.5 6. 4.75 ]]
Można używać broadcast nie tylko dlo wierszy:
a = np.arange(20).reshape(5, 4) #[[ 0 1 2 3]
#[ 4 5 6 7]
#[ 8 9 10 11]
#[12 13 14 15]
#[16 17 18 19]]
b = np.arange(5).reshape(5,1) #[[0],
# [1],
# [2],
# [3],
# [4]]
a = a + b # do wszystkich elementów pirwszego wiersza(o indeksie 0) będzie dodane 0, do drugiego wiersza 1, ...
print(a)
[[ 0 1 2 3] [ 5 6 7 8] [10 11 12 13] [15 16 17 18] [20 21 22 23]]
Uwaga dla broadcast'u wymiary pierwszej i drugiej macierzy muszą się zgadzać dla osi według ktorej operacja będzie wykonana, np.:
a = np.arange(20).reshape(5, 4)
b = np.arange(5) # [0 1 2 3 4]
print(a.shape, '\n', b.shape)
a = a + b
(5, 4) (5,)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-388-cc14a7d6b83b> in <module> 3 print(a.shape, '\n', b.shape) 4 ----> 5 a = a + b ValueError: operands could not be broadcast together with shapes (5,4) (5,)
Często potrzebujemy dodać/pomnożyć/.. każdy element przez skalar, w numpy nie potrzebujemy dla tego żadnej pętli, również możemy użyć operatorów porównywania, jak w jednym z poprzednich przykładów (wróć do przykładu):
a = np.zeros((3,2)) #[[0. 0.]
#[0. 0.]
#[0. 0.]]
a += 2
print(a,'\n')
a /= 6
print(a,'\n')
a += [0,1] #także broadcasting działa na list'ach, ale polecam używać np.array, żeby nie dostać problemów z kastowaniem typów
print(a, '\n')
b = a > 1
print(b)
[[2. 2.] [2. 2.] [2. 2.]] [[0.33333333 0.33333333] [0.33333333 0.33333333] [0.33333333 0.33333333]] [[0.33333333 1.33333333] [0.33333333 1.33333333] [0.33333333 1.33333333]] [[False True] [False True] [False True]]
print(np.random.rand(3,4,2)) # randomowe liczby z zakresu 0..1
[[[0.95654898 0.51772985] [0.30443877 0.66300355] [0.5346792 0.42737866] [0.01869313 0.71600861]] [[0.58592717 0.25488915] [0.18962555 0.69029206] [0.65188959 0.87090875] [0.92873514 0.33361594]] [[0.8189228 0.62384474] [0.94660751 0.16215483] [0.92421993 0.29920311] [0.39280279 0.40642805]]]
print(np.random.randn(3, 4, 2)) # liczby z "standard normal distribution"
[[[ 0.12585249 -0.55134048] [ 0.25200496 -1.50630257] [-0.01470187 -0.05461041] [ 0.74866057 -0.83361785]] [[-0.27943147 0.0164368 ] [ 0.63081656 0.17151957] [-0.61570685 -0.15984513] [ 1.76090931 -0.36132852]] [[ 1.15908714 -0.60243523] [ 0.00741905 -0.80679109] [ 0.10368366 -0.41862015] [-0.35824732 0.10183025]]]
low, high = (-6, 1)
shape = (3,4,2)
print(np.random.uniform(low, high, shape)) # "uniform distribution" z zakresu od low do high, z podanym shape typu tuple
[[[-1.16558105 -3.53384009] [-5.48569627 -4.24534522] [-0.27918469 -0.25560944] [-5.48176218 0.70471729]] [[-3.39664205 -4.94766537] [-5.26482679 -2.45064224] [-3.69986093 -0.30885886] [ 0.53846226 -0.21632664]] [[-5.74475269 0.42878536] [-0.61880348 -0.66429102] [-5.15524829 -1.4232551 ] [-2.83124314 -4.03850157]]]
a = np.random.uniform(-1, 1, (3,2))
print(a,'\n')
print(np.mean(a),'\n') # a.mean()
print(np.mean(a, axis=0), '\n') # liczy śriednią dla każdej kolumny
print(np.mean(a, axis=1), '\n') # liczy śriednią dla każdego wiersza
# podobnie działa std
print(np.std(a), '\n')
print(np.std(a, axis=0), '\n')
print(np.std(a, axis=1),'\n')
[[-0.04117204 -0.99369902] [ 0.80944333 -0.80993573] [-0.64197097 0.47084097]] -0.2010822418751138 [ 0.04210011 -0.44426459] [-5.17435530e-01 -2.46196812e-04 -8.55649991e-02] 0.6697687179800678 [0.59545588 0.65141174] [0.47626349 0.80968953 0.55640597]
import numpy.linalg as linalg
a = np.array([[2,3],
[2,2]])
print(linalg.inv(a), '\n') # macierz odwrotna Uwaga: wyrzuca błąd kiedy det == 0
print(linalg.det(a), '\n') # wyznacznik
print(linalg.norm(a), '\n') # norma
print(linalg.norm(a, axis=0), '\n') # norma z kolumn jako wektorów
print(linalg.norm(a, axis=1), '\n') # norma z wierszy jako wektorów
[[-1. 1.5] [ 1. -1. ]] -2.0 4.58257569495584 [2.82842712 3.60555128] [3.60555128 2.82842712]
a = np.arange(6).reshape(2,3)
b = np.arange(6).reshape(3,2)
print(a,'\n\n', b, '\n')
print("------------------------")
print(np.dot(a,b), '\n') # mnożenie macierzowe
print(a.dot(b), '\n') # inna forma
print(a @ b, '\n') #inna forma
print("------------------------")
#Uwaga
print(a*a, '\n') # element-wise multiplication czyli
# result[i,j] = first_matrix[i,j] * second_matrix[i,j] (broadcasting mnożenia)
#-------------------------------
print(a.T, '\n') # transponowanie macierzy
[[0 1 2] [3 4 5]] [[0 1] [2 3] [4 5]] ------------------------ [[10 13] [28 40]] [[10 13] [28 40]] [[10 13] [28 40]] ------------------------ [[ 0 1 4] [ 9 16 25]] [[0 3] [1 4] [2 5]]
a = np.ones((256, 128, 3, 300))
print(a.shape, '\n')
print(a.transpose(3, 0, 1, 2).shape) # lub a.transpose(-1, 0, 1, 2).shape #Uwaga: "-1, -2.." to są indeksy od pońca
# czyli -1 == 3, -2 == 2, -3 == 1, -4 == 0
(256, 128, 3, 300) (300, 256, 128, 3)
a = np.arange(6).reshape(3,2)
print(a, '\n')
print(np.exp(a), '\n') # e^x dla każdego elementu z a
print(np.sin(a), '\n') # sin(x) dla każdego elementu z a
print(np.sum(a), '\n') # suma tablicy
print(np.sum(a, axis=1), '\n') # suma wierszy
print(np.sum(a, axis=0), '\n') # suma kolumn
[[0 1] [2 3] [4 5]] [[ 1. 2.71828183] [ 7.3890561 20.08553692] [ 54.59815003 148.4131591 ]] [[ 0. 0.84147098] [ 0.90929743 0.14112001] [-0.7568025 -0.95892427]] 15 [1 5 9] [6 9]
# problem tej funkcji polega na tym że kiedy zrobimy operacje np.exp na dużych liczbach ona może przyjąć
# wartość np.inf, czyli bardzo duża liczba np. np.exp(1000), lub np.nan(not a number) jak podzielimy coś przez np.inf
# print(np.exp(1000), '\n')
def softmax(a):
a_exp = np.exp(a)
softmax = a_exp / np.sum(a_exp, axis=1, keepdims=True) # lub
# softmax = a_exp / np.sum(a_exp, axis=1).reshape(-1, 1)
print(softmax, '\n')
def stable_softmax(a):
a_exp = np.exp( a - np.max(a, axis=1, keepdims=True) ) # lub
# a_exp = np.exp( a - np.max(a, axis=1).reshape(-1,1))
softmax = a_exp / np.sum(a_exp, axis=1, keepdims=True) #lub
# softmax = a_exp / np.sum(a_exp, axis=1).reshape(-1, 1)
print(softmax, '\n')
a = np.array([[1000, 1001], [1,2]])
softmax(a)
stable_softmax(a)
[[ nan nan] [0.26894142 0.73105858]] [[0.26894142 0.73105858] [0.26894142 0.73105858]]
/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:5: RuntimeWarning: overflow encountered in exp """ /anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:6: RuntimeWarning: invalid value encountered in true_divide