Pakiet NumPy - tutorial#
Wstep#
Czym jest pakiet numpy? Pakiet NumPy jest pakietem wykorzystywanym do operacji numerycznych przeprowadzanych na wielowymiarowych obiektach zwanych arrayami. Pakiet ten jest bardzo wydajny i często wykorzystywany - zarówno przez początkujących jak i tych bardziej zaawansowanych. Dobrze współpracuje z pakietami takimi jak pandas, scipy, matplotlib, scikit-learn i wieloma innymi pakietami bazującymi na danych numerycznych.
Czym jest array? Array jest obiektem, na którym można wywoływać złożone operacje numeryczne. Z uwagi na wydajność obliczeń array’e sa często wykorzystywane. Swoją strukturą przypominają listę, lecz między tymi obiektami są znaczące różnice. Poniżej zostały przedstawione niektóre z nich.
Różnica między działaniami na listach, a numpy array#
Standardowo Python nie potrafi robić obliczeń na listach:
height = [1.82, 2.01, 1.68]
weight = [100, 89, 53]
# weight / height ** 2 - to nie zadziala
Jest na to sposób, który też jest powszechnie stosowany - List Comprehension.
Należy jednak pamiętać, że nie w każdym przypadku List Comprehension zastąpi nam funckje wbudowane w pakiet numpy. Ponadto nadmiar tej struktury wpływa negatywnie na wydajność kodu.
[weight[i] / height[i] ** 2 for i in range(len(height))]
[30.189590629151066, 22.02915769411649, 18.77834467120182]
Szybszym (i wydaje się, że bardziej przyjaznym) rozwiązaniem jest użycie pakietu Numpy. Dzięki temu obiektowi możemy definiować działania na całej array’i.
import numpy as np
np_height = np.array(height)
np_height
array([1.82, 2.01, 1.68])
np_weight = np.array(weight)
np_weight
array([100, 89, 53])
bmi = np_weight / np_height ** 2
bmi
array([30.18959063, 22.02915769, 18.77834467])
W numpy array można przechowywać dane tylko jednego typu (w listach można mieszać typy danych).
[0.2, 'll', True]
[0.2, 'll', True]
np.array([0.2, 'll', True])
array(['0.2', 'll', 'True'], dtype='<U32')
Uwaga! Dodajac standardowe listy w Pythonie uzyskamy listę elementów wartości z obu list. Robiąc to samo na arrayach dodajemy odpowiednie elementy do siebie. Trzeba więc uważać na to co się robi.
python_list = [1, 2, 3]
numpy_array = np.array(python_list)
python_list + python_list
[1, 2, 3, 1, 2, 3]
numpy_array + numpy_array
array([2, 4, 6])
# Chcac uzyskac efekt powiekszenia listy (jak na standardowych listach) wystarczy uzyc funkcji np.append
np.append(numpy_array, numpy_array)
array([1, 2, 3, 1, 2, 3])
Operacje na arrayach#
Tworzenie Numpy Array#
# Definiowanie array'a na podstawie listy
np.array([1,2,3,4,5])
array([1, 2, 3, 4, 5])
# Funkcja np.ones tworzy array skladajacy sie z samych 1
np.ones(16)
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
# Funkcja np.zeros tworzy array skladajacy sie z samych 0
np.zeros(5)
array([0., 0., 0., 0., 0.])
# Tworzenie pustej array'i. Powinna dzialac najszybciej bo nie wymaga zmieniania wartosci liczb
# (wartosci sa wybierane na podstawie miejsca w pamieci gdzie sie znajduja)
np.empty(15, dtype=int)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
# Tworzenie macierzy jednostkowej
np.eye(3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
# Definiowanie array'a z zadanego zakresu o okreslonej dlugosci
np.linspace(start=0, stop=5, num=21)
array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. , 2.25, 2.5 ,
2.75, 3. , 3.25, 3.5 , 3.75, 4. , 4.25, 4.5 , 4.75, 5. ])
# Definiowanie array'a z zadanego zakresu o okreslonej roznicy miedzy wyrazami
# Zauwaz ze wyraz 5.25 nie zostal juz dopisany
np.arange(start=0, stop=5.25, step=0.25)
array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. , 2.25, 2.5 ,
2.75, 3. , 3.25, 3.5 , 3.75, 4. , 4.25, 4.5 , 4.75, 5. ])
# Jesli nie poda sie parametru stop to array budowany jest od 0 do podanej liczby z zadanym krokiem
np.arange(5.25, step=0.25)
array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. , 2.25, 2.5 ,
2.75, 3. , 3.25, 3.5 , 3.75, 4. , 4.25, 4.5 , 4.75, 5. ])
# Mozna zdefiniowac typ danych przechowywanych w array'u
# Zwroc uwage ze nie definiujac parametru step jest on zdefiniowany jako 1
np.arange(10, dtype=complex)
array([0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j, 7.+0.j,
8.+0.j, 9.+0.j])
Filtrowanie elementow#
# Chcac stworzyc dwuwymiarowy array tworzymy array z zagniezdzonych list
np_2d = np.array([[1.1, 5, 3],
[4, 5, 6]])
np_2d
array([[1.1, 5. , 3. ],
[4. , 5. , 6. ]])
# Wybieranie pierwszego wiersza
np_2d[0]
array([1.1, 5. , 3. ])
# Wybieranie drugiej kolumny
np_2d[:, 1]
array([5., 5.])
# Wybranie wszystkich kolumn dla pierwszego wiersza (od 0 do 1 (bez 1))
np_2d[0:1, :]
array([[1.1, 5. , 3. ]])
# pierwsza i druga kolumna od drugiego wiersza wlacznie
np_2d[1:,[0,1]]
array([[4., 5.]])
# Wybranie elementow spelniajacych okreslony warunek
np_2d[np_2d<5]
array([1.1, 3. , 4. ])
# Czy ktorykolwiek element array'a jest rowny 5
np.any(np_2d==5)
True
# Czy wszystkie elementy array'a sa rowne 5
np.all(np_2d==5)
False
np_2d
array([[1.1, 5. , 3. ],
[4. , 5. , 6. ]])
np_2d[np.nonzero(np_2d)]
array([1.1, 5. , 3. , 4. , 5. , 6. ])
np_2d[np.where(np_2d<5)]
array([1.1, 3. , 4. ])
Wywoływanie funkcji opisujących array#
W celu lepszego poznania obiektu, z którym pracujemy warto wywołać podstawowe funkcje mówiące nam o ilości danych, liczbie wymiarów, itp.
funkcja |
opis |
---|---|
ndim |
zwraca liczbę wymiarów array’a |
shape |
zwraca wymiar array’a |
size |
zwraca liczbę elementów w array’u |
dtype |
określenie typu danych w array’u |
unique |
zwraca unikalne elementy w array’u |
# Utworzmy 16elementowy array. Zauwaz, ze w pakiecie numpy mamy zdefiniowana liczbe pi.
pi_array = np.linspace(start=-2*np.pi, stop=2*np.pi, num=16)
pi_array
array([-6.28318531, -5.44542727, -4.60766923, -3.76991118, -2.93215314,
-2.0943951 , -1.25663706, -0.41887902, 0.41887902, 1.25663706,
2.0943951 , 2.93215314, 3.76991118, 4.60766923, 5.44542727,
6.28318531])
pi_array.ndim
1
# Sprawdzanie wymiaru arraya
pi_array.shape
(16,)
# Sprawdzanie liczby elementow w array'u
pi_array.size
16
# Sprawdzenie typu danych w array'u
pi_array.dtype.name
'float64'
# Wartosci unikalne w array'u
np.unique(pi_array)
array([-6.28318531, -5.44542727, -4.60766923, -3.76991118, -2.93215314,
-2.0943951 , -1.25663706, -0.41887902, 0.41887902, 1.25663706,
2.0943951 , 2.93215314, 3.76991118, 4.60766923, 5.44542727,
6.28318531])
Wywoływanie określonych operacji matematycznych na arrayach#
funkcja |
opis |
---|---|
reshape |
zmienia wymiar array’a |
ravel |
spłaszczanie array’a |
T |
transponuje array |
@ lub dot |
mnożenie macierzowe arrayi |
* , / , +,- |
mnożenie/dzielenie/dodawanie/odejmowanie poszczególnych elementów między sobą |
sin (lub inne funkcje trygonometryczne)/exp/sqrt/log |
działanie na array odpowiednia funkcja |
Stałe matematyczne:
funkcja |
stała |
---|---|
np.pi |
liczba pi |
np.e |
liczba e (zobacz także funkcję np.exp()) |
np.inf |
nieskonczoność |
np.nan |
not a number |
np.ninf |
minus nieskonczoność |
np.log() |
funkcja logarytmiczna, np np.log(0)==-inf |
# Wywolywanie funkcji na array'u. Zauwaz, ze w pakiecie numpy wystepuja funkcje trygonometryczne
np.sin(pi_array)
array([ 2.44929360e-16, 7.43144825e-01, 9.94521895e-01, 5.87785252e-01,
-2.07911691e-01, -8.66025404e-01, -9.51056516e-01, -4.06736643e-01,
4.06736643e-01, 9.51056516e-01, 8.66025404e-01, 2.07911691e-01,
-5.87785252e-01, -9.94521895e-01, -7.43144825e-01, -2.44929360e-16])
np.arcsin(np.sin(pi_array))
array([ 2.44929360e-16, 8.37758041e-01, 1.46607657e+00, 6.28318531e-01,
-2.09439510e-01, -1.04719755e+00, -1.25663706e+00, -4.18879020e-01,
4.18879020e-01, 1.25663706e+00, 1.04719755e+00, 2.09439510e-01,
-6.28318531e-01, -1.46607657e+00, -8.37758041e-01, -2.44929360e-16])
# Funkcja reshape przeksztalca wymiar array'a do zadanego
# W tym przypadku array o wymiarze (16,) przeksztalcamy na array o wymiarze 4,4
A = np.ones(16).reshape(4,4)
A
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
B = np.arange(16).reshape(4,4)
B
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
# Funkcja splaszczajaca (zauwaz ze B nie zostalo nadpisane)
B.ravel()
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
B.ndim
2
# Porownywanie poszczegolnych elementow w arrayach
A==B
array([[False, True, False, False],
[False, False, False, False],
[False, False, False, False],
[False, False, False, False]])
# Mnozenie odpowiadajacych sobie wyrazow
A*B
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.]])
# Mnozenie macierzowe
A.dot(B)
array([[24., 28., 32., 36.],
[24., 28., 32., 36.],
[24., 28., 32., 36.],
[24., 28., 32., 36.]])
# Inny sposob mnozenia macierzowego
A@B
array([[24., 28., 32., 36.],
[24., 28., 32., 36.],
[24., 28., 32., 36.],
[24., 28., 32., 36.]])
# Transponowanie
B.T
array([[ 0, 4, 8, 12],
[ 1, 5, 9, 13],
[ 2, 6, 10, 14],
[ 3, 7, 11, 15]])
# Funkcja e^x. Chcac uzyskac liczbe e wystarczy podac np.exp(1)
np.exp(B)
array([[1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01],
[5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03],
[2.98095799e+03, 8.10308393e+03, 2.20264658e+04, 5.98741417e+04],
[1.62754791e+05, 4.42413392e+05, 1.20260428e+06, 3.26901737e+06]])
# Pierwiastkowanie
np.sqrt(B)
array([[0. , 1. , 1.41421356, 1.73205081],
[2. , 2.23606798, 2.44948974, 2.64575131],
[2.82842712, 3. , 3.16227766, 3.31662479],
[3.46410162, 3.60555128, 3.74165739, 3.87298335]])
Sortowanie array’a#
# Stworzenie arrayi z 10 losowych liczb calkowitych z przedzialu [1,10]
to_sort = np.random.randint(1, 10, 10)
to_sort
array([4, 6, 6, 7, 8, 1, 7, 5, 4, 4])
np.sort(to_sort)
array([1, 4, 4, 4, 5, 6, 6, 7, 7, 8])
Sortowanie malejaco
-np.sort(-to_sort)
array([8, 7, 7, 6, 6, 5, 4, 4, 4, 1])
# Funckja flip odwraca kolejnosc elementow w array'i
np.flip(np.sort(to_sort))
array([8, 7, 7, 6, 6, 5, 4, 4, 4, 1])
Łączenie i dzielenie array’i, dodawanie i usuwanie elementów#
funkcja |
opis |
---|---|
vstack/hstack |
wierszowe/kolumnowe skladanie dwoch arrayi |
vspli/hsplit |
wierszowe/kolumnowe rozdzielanie dwoch arrayi |
append |
dodawanie elementu na koniec arrayi |
insert |
umieszczanie nowego elementu w arrayi w okreslonym miejscu |
delete |
usuwanie elementow arrayi |
# Wertykalne laczanie arrayi
np.vstack((A, B))
array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.]])
# Horyzontalne laczanie array'i
np.hstack((A, B))
array([[ 1., 1., 1., 1., 0., 1., 2., 3.],
[ 1., 1., 1., 1., 4., 5., 6., 7.],
[ 1., 1., 1., 1., 8., 9., 10., 11.],
[ 1., 1., 1., 1., 12., 13., 14., 15.]])
# Podzial arrayi B na dwie mniejsze
np.hsplit(B, 2)
[array([[ 0, 1],
[ 4, 5],
[ 8, 9],
[12, 13]]),
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])]
# Dodawanie liczby do array'a
np.append(to_sort, 10)
array([ 4, 6, 6, 7, 8, 1, 7, 5, 4, 4, 10])
# Dodawanie listy do array'a
np.append(to_sort, [10, 15, 13])
array([ 4, 6, 6, 7, 8, 1, 7, 5, 4, 4, 10, 15, 13])
# Dodawanie arraya do array'a
np.append(to_sort, np.array([10, 15, 13]))
array([ 4, 6, 6, 7, 8, 1, 7, 5, 4, 4, 10, 15, 13])
# Wrzucenie elementow listy na dana pozycje w array'u
np.insert(to_sort, 4, [1, 23])
array([ 4, 6, 6, 7, 1, 23, 8, 1, 7, 5, 4, 4])
# Usuniecie drugiej kolumny w array'u
np.delete(B, 1, axis=1)
array([[ 0, 2, 3],
[ 4, 6, 7],
[ 8, 10, 11],
[12, 14, 15]])
# Usuniecie drugiego i trzecicego wiersza
np.delete(B, [1, 2], axis=0)
array([[ 0, 1, 2, 3],
[12, 13, 14, 15]])
Podstawowe statystyki#
funkcja |
opis |
---|---|
sum |
zsumowanie elementow |
max |
element maksymalny |
min |
element minimalny |
mean |
srednia |
sum |
zsumowanie elementow |
std |
odchylenie standardowe |
var |
wariancja |
cov |
macierz kowariancji |
cumsum |
suma skumulowana |
argmax |
zwraca indeks najwiekszego elementu |
argmin |
zwraca indeks najmniejszego elementu |
# Zsumowanie wszystkich elementow
B.sum()
120
# Suma poszczegolnych kolumn
B.sum(axis=0)
array([24, 28, 32, 36])
# Suma poszczegolnych wierszy
B.sum(axis=1)
array([ 6, 22, 38, 54])
np.cumsum(B)
array([ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78,
91, 105, 120])
to_sort
array([4, 6, 6, 7, 8, 1, 7, 5, 4, 4])
np.argmax(to_sort)
4
Zdjęcie jako wielowymiarowy array#
# Wczytajmy przykladowe zdjecie i sprawdzmy jego typ
from scipy import misc
raccoon = misc.face()
type(raccoon)
C:\Users\knajmajer\AppData\Local\Temp\ipykernel_23736\461371906.py:3: DeprecationWarning: scipy.misc.face has been deprecated in SciPy v1.10.0; and will be completely removed in SciPy v1.12.0. Dataset methods have moved into the scipy.datasets module. Use scipy.datasets.face instead.
raccoon = misc.face()
numpy.ndarray
Wczytując kolorowe zdjęcie (kolory w RGB) otrzymamy array wielkości (X, Y, 3). X odpowiada za wysokość obrazu (liczba wierszy w array’i), Y odpowiada za szerokość (liczba kolumn w array’i), a 3 odpowiada za składowe RGB.
# Sprawdzmy liczbe wymiarow
raccoon.ndim
3
# Sprawdzmy rozmiary (zdjecie ma rozmiar 768x1024 pixeli)
raccoon.shape
(768, 1024, 3)
# Liczba elementow (768*1024*3)
raccoon.size
2359296
raccoon.dtype
dtype('uint8')
# Wyswietlenie pierwszego pixela
raccoon[0][0]
array([121, 112, 131], dtype=uint8)
# Wybranie drugiego wiersza.
# Analogicznie: raccoon[1, :, :] lub raccoon[1]
raccoon[1, ...]
array([[ 89, 82, 100],
[110, 103, 121],
[130, 122, 143],
...,
[118, 125, 71],
[134, 141, 87],
[146, 153, 99]], dtype=uint8)
# Iterowanie po pikselach
for column in raccoon[:5]:
for rgb in column[:5]:
print(rgb)
[121 112 131]
[138 129 148]
[153 144 165]
[155 146 167]
[155 146 167]
[ 89 82 100]
[110 103 121]
[130 122 143]
[137 129 150]
[141 133 154]
[73 66 84]
[ 94 87 105]
[115 108 126]
[123 115 136]
[127 119 140]
[81 77 94]
[ 97 93 110]
[113 109 126]
[120 115 135]
[125 120 140]
[103 99 114]
[113 109 126]
[123 119 136]
[132 127 147]
[142 137 157]
# Sprawdzenie poprawnosci petli (wyciagniecie 5 pierwszych wierszy i 5 pierwszych kolumn)
raccoon[:5,:5]
array([[[121, 112, 131],
[138, 129, 148],
[153, 144, 165],
[155, 146, 167],
[155, 146, 167]],
[[ 89, 82, 100],
[110, 103, 121],
[130, 122, 143],
[137, 129, 150],
[141, 133, 154]],
[[ 73, 66, 84],
[ 94, 87, 105],
[115, 108, 126],
[123, 115, 136],
[127, 119, 140]],
[[ 81, 77, 94],
[ 97, 93, 110],
[113, 109, 126],
[120, 115, 135],
[125, 120, 140]],
[[103, 99, 114],
[113, 109, 126],
[123, 119, 136],
[132, 127, 147],
[142, 137, 157]]], dtype=uint8)
# Wyswietlenie arraya przy pomocy pakietu matplotlib
# Na jednym z kolejnych zajec bedzie wiecej o tym pakiecie i nie tylko :)
import matplotlib.pyplot as plt
plt.imshow(raccoon)
<matplotlib.image.AxesImage at 0x240caf13740>

# Znormalizowanie RGB (zmiana z przedzialu [0, 255] na [0, 1])
raccoon = raccoon / 255
# Chac uzyskac zdjecie w skali szarosci trzeba pomnozyc macierzowo nasza macierz przez podana ponizej
raccoon_gray = raccoon @ [0.2126, 0.7152, 0.0722]
raccoon_gray.shape
(768, 1024)
plt.imshow(raccoon_gray, cmap="gray")
<matplotlib.image.AxesImage at 0x240cb011130>

Pandas DataFrame vs Numpy Array#
Chcąc wykonywać obliczenia szybciej warto zamienić DataFrame na Array. Wtedy każdy wiersz arraya będzie odpowiadać za każdy wiersz ramki danych.
import pandas as pd
df = pd.read_csv(filepath_or_buffer='mpg.csv', # sciezka do pliku
sep=',', # separator
header=0, # naglowek (nazwy kolumn)
index_col=0 # kolumna z indeksem
)
df.head()
cylinders | displacement | horsepower | weight | acceleration | model_year | origin | name | |
---|---|---|---|---|---|---|---|---|
mpg | ||||||||
18.0 | 8 | 307.0 | 130 | 3504 | 12.0 | 70 | 1 | chevrolet chevelle malibu |
15.0 | 8 | 350.0 | 165 | 3693 | 11.5 | 70 | 1 | buick skylark 320 |
18.0 | 8 | 318.0 | 150 | 3436 | 11.0 | 70 | 1 | plymouth satellite |
16.0 | 8 | 304.0 | 150 | 3433 | 12.0 | 70 | 1 | amc rebel sst |
17.0 | 8 | 302.0 | 140 | 3449 | 10.5 | 70 | 1 | ford torino |
df_array = df.to_numpy()
df_array[:5]
array([[8, 307.0, '130', 3504, 12.0, 70, 1, 'chevrolet chevelle malibu'],
[8, 350.0, '165', 3693, 11.5, 70, 1, 'buick skylark 320'],
[8, 318.0, '150', 3436, 11.0, 70, 1, 'plymouth satellite'],
[8, 304.0, '150', 3433, 12.0, 70, 1, 'amc rebel sst'],
[8, 302.0, '140', 3449, 10.5, 70, 1, 'ford torino']], dtype=object)
df.shape
(398, 8)
df_array.shape
(398, 8)
Porównajmy czas wykonania działań na trzech kolumnach.
from time import time
start = time()
fun = lambda row:row['cylinders']*row['weight']**row['acceleration']
df.apply(fun, axis=1)
print(f'Czas wykonania: {time()-start}')
Czas wykonania: 0.0022246837615966797
start = time()
df_array[:,1]*df_array[:,4]**df_array[:,5]
print(f'Czas wykonania: {time()-start}')
Czas wykonania: 0.0003380775451660156
Jak widać już na niewielkim zbiorze danych proste obliczenia na array’ach wykonują się szybciej. Warto o tym pamiętać i to wykorzystywać.
Podsumowanie#
Jak widać NumPy jest pakietem, ktory zdecydowanie potrafi ułatwić życie. Jest często wykorzystywany do złożonych obliczeń. Ma zastosowania przy pracy z różnymi danymi - obrazami, ramkami danych, czy po prostu dużymi listami.