Mikuláš je tu! Získaj 90 % extra kreditov ZADARMO s promo kódom CERTIK90 pri nákupe od 1 199 kreditov. Len do nedele 7. 12. 2025! Zisti viac:
NOVINKA: Najžiadanejšie rekvalifikačné kurzy teraz s 50% zľavou + kurz AI ZADARMO. Nečakaj, táto ponuka dlho nevydrží! Zisti viac:

3. diel - NumPy - Tvorba polí

V predchádzajúcej lekcii, NumPy - Dátové typy, sme sa pozreli na základné dátové typy knižnice NumPy.

V dnešnom tutoriáli knižnice NumPy v Pythone sa pozrieme na polia v knižnici NumPy a naučíme sa ich vytvárať. Vysvetlíme si, aké je dôležité udržiavať pole homogénne.

Úvod do polí ndarray v NumPy

Polia, ktoré poskytuje knižnica NumPy, sa do značnej miery správajú podobne ako bežné zoznamy v Pythone. Je možné do nich ukladať najrôznejšie typy premenných – čísla, reťazce i všeobecné objekty a potom k prvkom pristupovať, prípadne ich meniť. Oproti bežným zoznamom v Pythone ale nie je možné do nich prvky pridávať a odoberať. Polia v NumPy majú pevne daný počet prvkov, podobne ako to majú polia v jazykoch C alebo Java.

Rozdiel medzi poľom a zoznamom

Ešte raz si zopakujme veľmi dôležitý poznatok, ktorý sme spomenuli v lekcii NumPy - Predstavenie knižnice:

  • Zoznamy v Pythone sú dynamické. Zodpovedajú tomu, čo v iných jazykoch označujeme ako list - zoznam.
  • Oproti zoznamom sa v NumPy jedná o skutočné polia - array, ako ich poznáme z jazykov C či Java. V NumPy ich označujeme aj ich technickým názvom ako ndarray.

Natívne zoznamy Pythonu a polí NumPy budeme v tutoriáli striktne odlišovať.

Ukážme si ešte pre istotu kód zoznamu a poľa:

my_list = [1, 2, 3]    # list
print(type(my_list))

my_array = np.array([1, 2, 3])    # ndarray
print(type(my_array))

Vo výstupe konzoly dostaneme:

Difference between list and array
<class 'list'>
<class 'numpy.ndarray'>

Kedy použiť pole, kedy zoznam?

Prečo by sme mali používať NumPy ndarray, keď nám oproti pythonovskému zoznamu neumožňuje pridávať a odoberať prvky? Odpovede sú dve: čas a priestor v pamäti. S poľom sa počítaču zaobchádza jednoduchšie. Akonáhle sa v pamäti pole vytvorí, jeho dĺžka zostáva až do zmazania Garbage Collectorom konštantná. Samozrejme sa občas nejaká hodnota v ňom zmení, ale to je všetko.

Preto ak pracujeme s veľkým množstvom hodnôt alebo operácií, je všeobecne rýchlejšie aj úspornejšie použiť ndarray. Inak samozrejme môžeme použiť aj základný Python zoznam.

Tvorba ndarray polí v NumPy

Existuje viacero spôsobov, ako vytvoriť ndarray. Najčastejšie použijeme "premenu" zoznamu pomocou metódy np.array(). NumPy však obsahuje mnoho ďalších zaujímavých funkcií a metód, ktoré poskytujú na výstupe ndarray. Pozrime sa na niektoré z nich.

Vytvorenie NumPy poľa z Python zoznamu

Najprv si ukážeme už spomínanú metódu np.array():

natural_numbers_array = np.array([1, 2, 3])
animals_array = np.array(['dog', 'cat', 'turtle'])
date_array = np.array([np.datetime64('2023-07-20T17:23:10.42'), np.datetime64('2023-07-20'), np.datetime64('2023-07')])

Pri vytváraní polí z textov (np.array([...])) používa NumPy dátový typ np.str_ s pevnou dĺžkou, v predchádzajúcej ukážke je pole animals_array typu dtype='<U6'. Ak do neho budeme chcieť neskôr vložiť dlhší reťazec, bude automaticky skrátený na šesť znakov.

Aby sme tomu predišli, môžeme pri vytváraní poľa vopred definovať dostatočnú dĺžku:

animals_array = np.array(['dog', 'cat', 'turtle'], dtype='<U20')
animals_array[1] = "Bengal cat"
print(animals_array[1])

Existuje tiež možnosť vytvoriť pole s parametrom dtype=object, kde sú prvky bežné Python reťazce (str). Také pole síce umožňuje ukladať reťazce ľubovoľnej dĺžky, ale stráca sa rýchlosť a pamäťová efektivita NumPy. Pracuje sa s ním podobne pomaly ako s bežným Python zoznamom. Preto sa dtype=object používa len vtedy, keď je nutné uchovať reťazce rôznej dĺžky a nechceme riskovať orezanie.

Všeobecne je nevhodné miešať rôzne typy v jednom poli. Dátový typ NumPy poľa pri jeho vytvorení buď explicitne špecifikujeme alebo je automaticky určený na základe hodnôt, ktoré do poľa vložíme. Ak vložíme do jedného poľa rôzne typy dát, NumPy ich všetky prevedie na jednotný typ. Vyberie pritom taký, ktorý dokáže reprezentovať všetky vložené hodnoty.

Uveďme si príklad. Ak vytvoríme pole s celými číslami a jedno z nich bude desatinné, všetky čísla budú prevedené na desatinné. Podobne, ak pridáme reťazec do poľa s celými číslami, všetky čísla budú prevedené na reťazce. Je zrejmé, ako nepekne toto dokáže ovplyvniť výsledky matematických operácií.

Pretypovanie v poli je nákladné z hľadiska výkonu a ide o veľmi jednoduchý spôsob, ako si prirobiť naozaj nepríjemné problémy s neočakávaným správaním programu. Preto sa snažíme pole udržiavať homogénne.

Ak budeme naozaj kreatívni a do jedného poľa namixujeme toľko typov, že NumPy nedokáže nájsť jeden, ktorým by ich zastrešil, vznikne nám pole prvkov object:

mix_array = np.array([1, 'dog', np.datetime64('2023-07-20T17:23:10.42')])
print(mix_array.dtype)

Vo výstupe uvidíme:

Array element type
object

Tvorba viacrozmerných polí

Poďme si ukázať, ako s pomocou metódy np.array() vytvoríme viacrozmerné polia:

array_2D = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
array_3D = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
Prístup k prvkom v 3D poli

Naše array_3D má formát 2 x (2 x 3). Predstavme si ho ako kinosálu. Formát 2 x (2 x 3) nám hovorí, že máme prízemie a poschodie a v každom z nich dva rady po troch sedadlách. Rozložme si zápis poľa tak, aby bol intuitívnejší:

array_3D = np.array([
[[1, 2, 3],    # ground floor, first row
[4, 5, 6]],    # ground floor, second row
[[7, 8, 9],    # floor, first row
[10, 11, 12]]  # floor, second row
])

# Access to seat number 12 on the floor:
seat = array_3D[1, 1, 2]  # in the variable seat there will be the number 12

Zaujímavosťou je, že môžeme vytvoriť aj polia s "dimenziou 0":

array_0D = np.array(10)

V podstate tým do poľa uložíme iba jednu hodnotu. Ak vytlačíme obsah premennej array_0D, dostaneme hodnotu 10. Ak však zistíme typ premennej, dostaneme ndarray, nie integer:

print(array_0D)
print(type(array_0D))

V konzole uvidíme:

Arrays with dimension 0
10
<class 'numpy.ndarray'>

Náhodné celé číslo np.random.randint()

Metóda randint() v module random knižnice NumPy je základná funkcia na generovanie náhodných čísel typu integer. Ako pre vygenerovanie jednej náhodnej hodnoty, tak aj celého (aj viacrozmerného) poľa náhodných hodnôt.

Funkcie a metódy v module random máme k dispozícii buď priamo z knižnice np.random, alebo si modul pridáme priamo ako premennú. To je spôsob, aký budeme používať v tutoriáli kvôli zjednodušeniu kódu:

import numpy.random as random

Teraz už môžeme premennú random používať priamo.

Metódu randint() už poznáme z kurzu Základné konštrukcie jazyka Python. Nechajme si teda vygenerovať náhodné číslo od nuly do devätnásť:

random_number = random.randint(20)

Je potrebné si uvedomiť, že ako mnoho ďalších knižníc a funkcií v Pythone, aj NumPy používa spodnú definovanú hranicu intervalu "vrátane" (inkluzívne) a hornú hranicu "bez" (exkluzívne). V našom prípade kód random.randint(20) znamená v reči matematiky interval <0; 20).

Pole náhod :-)

Ak chceme vytvoriť pole náhodných hodnôt, vyplníme metóde argument požadovanej dĺžky poľa size:

random_number_array = random.randint(3, 20, size=(5)) # fills the array of 5 numbers with numbers from 3 to 19
print(random_number_array)
print()
random_number_2Darray = random.randint(3, 20, size=(5, 4)) # fills the array of 5 rows and 4 columns
print(random_number_2Darray)

Výstup v konzole:

Arrays with random numbers
[ 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ý float - np.random.rand()

Metóda rand() má trochu odlišnú syntax. Táto metóda generuje náhodné číslo medzi 0 a 1. Opäť v half-open intervale, avšak tentoraz <0; 1). Ak chceme získať desatinné číslo v inom intervale, musíme funkciu rand() prenásobiť a posunúť. Povedzme, že chceme náhodné číslo v intervale <10, 30):

random_decimal_number = random.rand() * 20 + 10

Teraz sa presunieme na vytváranie polí. NumPy ndarray pomocou metódy rand() vytvoríme tak, že do argumentu dáme číslo označujúce rozmer poľa, ktoré chceme:

# Array of random numbers of length 3
random_1D_array = random.rand(3)

Získali sme polia troch čísel z intervalu <0,1). Podobne vytvoríme aj viacrozmerné polia – jednoducho len pridáme číslo označujúce ďalší rozmer:

# Random number matrix with dimensions 3×4
random_2D_array = random.rand(3, 4)

Pre čísla v inom intervale ako <0,1) opäť použijeme posunutie násobením a pripočítaním čísla.

Moderná tvorba náhodných čísel

Od verzie NumPy 1.17 knižnica odporúča používať nové rozhranie na generovanie náhodných čísel. Využíva sa objekt Generator, ktorý nahrádza staršie funkcie z modulu np.random. Nový prístup je bezpečnejší, lepšie reprodukovateľný a prehľadnejší.

Vytvorenie nového generátora

Najprv vytvoríme inštanciu generátora pomocou funkcie default_rng():

from numpy.random import default_rng

rng = default_rng()

Generovanie náhodných čísel

Generator ponúka mnoho metód pre rôzne typy náhodných hodnôt. Najčastejšie používame metódy random() a integers().

Metóda random()

Metóda random() vracia desatinné čísla v intervale <0,1):

random_float_number = rng.random()
print("Random number (float) <0,1): ", random_float_number)

Ak ju zavoláme bez argumentu, získame jedno číslo. Ak jej odovzdáme tvar výsledného poľa (napríklad (3, 4)), vráti viac náhodných čísel usporiadaných do poľa daného rozmeru.

Ak chceme čísla v inom intervale ako <0,1), opäť ich jednoducho vynásobíme a posunieme. Takto napríklad získame číslo v intervale <10,30):

random_float_number = rng.random() * 20 + 10
print("Random number in an interval <10,30): ", random_float_number)
Metóda integers()

Metóda integers() generuje náhodné celé čísla. Ak zadáme iba hornú hranicu, získame jedno náhodné číslo od 0 do zadanej hodnoty mínus jedna:

random_int_number = rng.integers(10)
print("Random integer <0,10): ", random_int_number)

Keď zadáme aj spodnú hranicu a parameter size, získame viac čísel v poli zvoleného rozmeru. Ak chceme vytvoriť 2D pole náhodných čísel od 3 do 19 s rozmermi 5×4, použijeme zápis:

int_array = rng.integers(3, 20, size=(5, 4))
print("2D array of random integers in the interval <3,20):\n", int_array)

V nasledujúcej lekcii, NumPy - Operácie s poľami, si vysvetlíme základné operácie s NumPy poľami. Konkrétne si ukážeme indexáciu a prechádzanie polí.


 

Predchádzajúci článok
NumPy - Dátové typy
Všetky články v sekcii
NumPy - Matematika v Pythone
Preskočiť článok
(neodporúčame)
NumPy - Operácie s poľami
Článok pre vás napísal Miloš Halda
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje především bioinformatice a s ní souvisejícím tématům. Nevyhýbá se OOP jazykům, statistice a nástrojům pro analýzu dat.
Aktivity