Lekce 3 - Tvorba polí
V předchozí lekci, Datové typy, jsme se podívali na to, jaké základní datové typy používá knihovna NumPy a jak se s nimi pracuje.
V tomto 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í.
Pole v NumPy - ndarray
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.
Pole vs. seznam - rozlišení pojmů
Ještě jednou si zopakujme velmi důležitý poznatek,
který jsme zmínili v lekci NumPy - Představení knihovny v
kapitole Seznamy
v Pythonu vs NumPy arrays. Seznamy v Pythonu jsou
dynamické. Odpovídají tomu, co v jiných jazycích označujeme jako
list
- seznam. Oproti tomu v NumPy se jedná o skutečná pole -
array
, jako je známe z jazyků C či Java. V NumPy je budeme
označovat také jejich technickým názvem jako ndarray
. 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'
Pole vs. seznam - kdy použít co?
Proč bychom měli použít 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.
Jak se vytváří pole 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í ndarray
na výstupu. 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í.
No a pokud budeme opravdu kreativní a do jednoho pole namixujeme tolik
typů, že NumPy nedokáže najít jeden, který 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
Vytváření 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 č. 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
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 kvůli zjednodušení kódu používat v
tutoriálu:
import numpy.random as random
Nyní už můžeme proměnnou random
používat přímo.
Metoda randint()
funguje jednoduše a známe ji už z kurzu
Základní konstrukce jazyka Pythonu z lekce Knihovny
math a random. 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ím kvízu, Kvíz - Datové typy a pole v NumPy, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.