Lekce 3 - NumPy - Tvorba polí
V předchozí lekci, NumPy - Datové typy, jsme se podívali na základní datové typy knihovny NumPy.
V dnešním tutoriálu knihovny NumPy v Pythonu se podíváme na pole v knihovně NumPy a naučíme se je vytvářet. Vysvětlíme si, jak je důležité udržovat pole homogenní.
Úvod do polí ndarray
v
NumPy
Pole, která poskytuje knihovna NumPy, se do značné míry chovají podobně jako běžné seznamy v Pythonu. Lze do nich ukládat nejrůznější typy proměnných - čísla, řetězce i obecné objekty a poté k prvkům přistupovat, případně je měnit. Oproti běžným seznamům v Pythonu ale není možné do nich prvky přidávat a odebírat. Pole v NumPy mají pevně daný počet prvků, podobně jako to mají pole v jazycích C či Java.
Rozdíl mezi polem a seznamem
Ještě jednou si zopakujme velmi důležitý poznatek, který jsme zmínili v lekci NumPy - Představení knihovny:
- Seznamy v Pythonu jsou dynamické.
Odpovídají tomu, co v jiných jazycích označujeme jako
list
- seznam. - Oproti seznamům se v NumPy jedná o skutečná pole -
array
, jako je známe z jazyků C či Java. V NumPy je označujeme také jejich technickým názvem jakondarray
.
Nativní seznamy Pythonu a pole NumPy budeme v tutoriálu striktně odlišovat.
Ukažme si ještě pro jistotu kód seznamu a pole:
seznam = [1, 2, 3] # seznam print(type(seznam)) pole = np.array([1, 2, 3]) # ndarray print(type(pole))
Ve výstupu konzole dostaneme:
Rozdíl mezi seznamem a polem:
class 'list'
class 'numpy.ndarray'
Kdy použít pole, kdy seznam?
Proč bychom měli používat NumPy ndarray
, když nám oproti
Pythonovskému seznamu neumožňuje přidávat a odebírat prvky? Odpovědi jsou
dvě: čas a prostor v paměti. S polem se
počítači zachází jednodušeji. Jakmile se jednou pole vytvoří v paměti,
jeho délka zůstává až do smazání Garbage Collectorem konstantní.
Samozřejmě se občas nějaká hodnota v něm změní, ale to je vše.
Proto pokud pracujeme s velkým množstvím hodnot nebo
operací, je obecně rychlejší i úspornější použít ndarray
.
Jinak samozřejmě můžeme použít i základní Python seznam.
Tvorba ndarray
polí v
NumPy
Existuje více způsobů, jak ndarray
vytvořit. Nejčastěji
použijeme "přeměnu" seznamu pomocí metody np.array()
. NumPy
nicméně obsahuje mnoho dalších zajímavých funkcí a metod, které
poskytují na výstupu ndarray
. Podívejme se na některé z
nich.
Vytvoření NumPy pole z Python seznamu
Nejprve si ukážeme již zmíněnou metodu np.array()
:
prirozena_cisla_pole = np.array([1, 2, 3]) zvirata_pole = np.array(['pes', 'kočka', 'pterodaktyl']) datum_pole = np.array([np.datetime64('2023-07-20T17:23:10.42'), np.datetime64('2023-07-20'), np.datetime64('2023-07')])
Obecně je nevhodné míchat různé typy v jednom poli. Datový typ NumPy pole, při jeho vytvoření, buď explicitně specifikujeme, nebo je automaticky určen na základě hodnot, které do pole vložíme. Pokud vložíme do jednoho pole různé typy dat, NumPy je všechny převede na jednotný typ. Vybere přitom takový, který dokáže reprezentovat všechny vložené hodnoty.
Uveďme si příklad. Pokud vytvoříme pole s celými čísly a jedno z nich bude desetinné, všechna čísla budou převedena na desetinná. Podobně, pokud přidáme řetězec do pole s celými čísly, všechna čísla budou převedena na řetězce. Je zřejmé, jak nepěkně toto dokáže ovlivnit výsledky matematických operací.
Přetypování v poli je nákladné z hlediska výkonu a jde o velmi snadný způsob, jak si přidělat opravdu nepříjemné problémy s neočekávaným chováním programu. Proto se snažíme pole udržovat homogenní.
Pokud budeme opravdu kreativní a do jednoho pole namixujeme tolik typů, že
NumPy nedokáže najít jeden, kterým by je zastřešil, vznikne nám pole
prvků object
:
mix_pole = np.array([1, 'pes', np.datetime64('2023-07-20T17:23:10.42')]) print(mix_pole.dtype)
Ve výstupu uvidíme:
Typ prvků pole:
object
Tvorba vícerozměrných polí
Pojďme si ukázat, jak s pomocí metody np.array()
vytvoříme
vícerozměrná pole:
pole_2D = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) pole_3D = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
Přístup k prvkům v 3D poli
Naše pole_3D
má formát 2 x (2 x 3)
. Představme
si ho jako kinosál. Formát 2 x (2 x 3)
nám říká, že máme
přízemí a patro a v každém z nich dvě řady po třech sedadlech. Rozložme
si zápis pole tak, aby byl intuitivnější:
pole_3D = np.array([ [[1, 2, 3], # přízemí, první řada [4, 5, 6]], # přízemí, druhá řada [[7, 8, 9], # patro, první řada [10, 11, 12]] # patro, druhá řada ]) # Přístup k sedadlu číslo 12 v patře: sedadlo = pole_3D[1, 1, 2] # v proměnné sedadlo bude číslo 12
Zajímavostí je, že můžeme vytvořit i pole s "dimenzí 0":
pole_0D = np.array(10)
V podstatě tím do pole uložíme jen jednu hodnotu. Pokud vytiskneme obsah
proměnné pole_0D
, dostaneme hodnotu 10
. Pokud však
zjistíme typ proměnné, dostaneme ndarray
, nikoli
integer
:
print(pole_0D) print(type(pole_0D))
V konzoli uvidíme:
Pole s dimenzí 0:
10
<class 'numpy.ndarray'>
Náhodné celé číslo
np.random.randint()
Metoda randint()
v modulu random
knihovny NumPy je
základní funkce pro generování náhodných čísel typu
integer
. Jak pro vygenerování jedné náhodné hodnoty, tak i
celého (i vícerozměrného) pole náhodných hodnot.
Funkce a metody v modulu random
máme k dispozici buď přímo z
knihovny np.random
, nebo si modul přidáme přímo jako
proměnnou. To je způsob, jaký budeme používat v tutoriálu kvůli
zjednodušení kódu:
import numpy.random as random
Nyní už můžeme proměnnou random
používat přímo.
Metodu randint()
již známe z kurzu Základní konstrukce jazyka Python. Nechme si tedy
vygenerovat náhodné číslo od nuly do devatenácti:
nahodne_cislo = random.randint(20)
Je potřeba si uvědomit, že jako mnoho dalších knihoven a
funkcí v Pythonu, i NumPy používá spodní definovanou hranici intervalu
"včetně" (inkluzivní) a horní hranici "bez" (exkluzivní). V našem
případě kód random.randint(20)
znamená v řeči matematiky
interval <0; 20)
.
Pole náhod 
Pokud chceme vytvořit pole náhodných hodnot, vyplníme metodě argument
požadované délky pole size
:
pole_nahodnych_cisel = random.randint(3, 20, size=(5)) # znamená naplň pole o délce 5 čísel čísly od 3 do 19 print(pole_nahodnych_cisel) print() pole_2D_nahodnych_cisel = random.randint(3, 20, size=(5, 4)) # naplň pole o pěti řádcích a čtyřech sloupcích čísly od 3 do 19 print(pole_2D_nahodnych_cisel)
Výstup v konzoli:
Konzolová aplikace
[ 8 7 7 5 17]
[[19 8 14 8]
[14 5 5 3]
[ 4 6 17 10]
[16 14 5 6]
[19 18 8 9]]
Náhodný double
-
np.random.rand()
Metoda rand()
má trochu odlišnou syntaxi. Tato metoda generuje
náhodné číslo mezi 0
a 1
. Opět v half-open
intervalu, ovšem tentokrát <0; 1)
. Pokud chceme získat
desetinné číslo v jiném intervalu, musíme funkci rand()
přenásobit a posunout. Řekněme, že chceme náhodné číslo v intervalu
<10, 30)
:
nahodne_desetinne_cislo = random.rand() * 20 + 10
Více si o náhodných číslech řekneme v dalších lekcích. Teď se
přesuneme k vytváření polí. NumPy ndarray
pomocí metody
rand()
vytvoříme tak, že do argumentu dáme číslo
označující rozměr pole, které chceme:
# Pole náhodných čísel délky 3 nahodne_1D_pole = random.rand(3)
Získali jsme pole tří čísel z intervalu <0,1)
. Podobně
vytvoříme i vícerozměrná pole - prostě jen přidáme číslo označující
další rozměr:
# Matice náhodných čísel o rozměrech 3×4 nahodne_2D_pole = random.rand(3, 4)
Pro čísla v jiném intervalu než <0,1)
opět
použijeme posunutí násobením a přičtením čísla.
To je pro tuto lekci vše.
V následující lekci, NumPy - Základní operace s poli, si vysvětlíme základní operace s NumPy poli. Konkrétně si ukážeme indexaci a procházení polí.