Nejdříve si nainstaluj tyhle knihovny:
Tenhle typ knihoven je nechvalně známý tím, že je docela složité je nainstalovat. I proto, že jsou zčásti napsané v jiných jazycích než je Python. V poslední době se ale jejich instalace zjednodušuje, tak to snad zvládneš i ty!
Numpy jde nainstalovat z; www.lfd.uci.edu/~gohlke/pythonlibs/#numpy (stáhnout a soubor nainstalovat pip install soubor.whl
); ostatní by snad měly jít pomocí pip install jméno-knihovny
, tedy:
> pip install pillow matplotlib ipython[notebook] pandas pygments
Nejdřív je potřeba nainstalovat závislosti. Pro Fedoru:
$ sudo yum install python3-devel zlib-devel Cython atlas-devel lapack-devel gcc gcc-gfortran zlib-devel tk-devel libpng-devel freetype-devel libjpeg-turbo-devel
A pak, ve virtuálním prostředí:
$ pip install numpy pillow matplotlib ipython[notebook] pandas pygments
IPython je nástroj, který zjednodušuje interaktivní práci v Pythonu, zvlášť výpočty a experimenty. Dá se spustit z příkazové řádky jako ipython
– pak se chová podobně jako python
, jen s barvičkama a různýma vychytávkama navíc. My si ale vyzkoušíme IPython Notebook, který běží v prohlížeči, a umožní nám se vracet k předchozím příkazům, zobrazovat obrázky, nebo třeba prokládat kód hezky formátovanými poznámkami (jako je ta, kterou právě čteš). Zadej:
$ ipython notebook
IPython Notebook se ovládá docela intuitivně: do políčka napíšeš kód, zmáčkneš Shift+Enter. Kód se provede, zobrazí se výsledek, a objeví se políčko pro další kousek kódu. Když je kód špatně, dá se opravit a pomocí Shift+Enter spustit znovu.
Základní knihovna, používaná na výpočty, je Numpy, která definuje typ array
(pole). Takové pole se chová v mnoha ohledech podobně jako seznam. Když budeme chtít pole vytvořit, použijeme nejčastěji právě seznam (nebo jiný objekt který se dá použít pro for
cyklus):
import numpy
pole = numpy.array([0, 1, 2, 3, 4, 5, 6])
pole
pole[0]
pole[1:-2]
pole[0] = 9
pole
V jiných ohledech se ale chová jinak. Například takovému poli nejde měnit velikost – nemá metody jako append
:
pole.append(1)
Další zajímavost je, že pole má daný typ prvků. Když uděláme pole celých čísel, nejdou do něj pak vkládat třeba řetězce:
pole[0] = 'ahoj'
... a pokud do takového pole přiřadíme desetinné číslo, zaokrouhlí se dolů (pomocí funkce int
):
pole[0] = 3.9
pole
Typ pole se dá zadat při jeho vytváření, pomocí argumentu dtype
.
pole_float = numpy.array([0, 1, 2, 3, 4, 5, 6], dtype=float)
pole_float
pole_str = numpy.array([0, 1, 2, 3, 4, 5, 6], dtype=str)
pole_str
Aritmetické operace, jako součet, se provádí na jednotlivých prvcích – není to jako u seznamů. Přičteme-li k poli číslo, přičte se ke všem prvkům pole; stejně u násobení atd. Přičteme-li k poli pole, sečtou se odpovídající prvky.
pole1 = numpy.array(range(10))
pole1
pole1 + 5
pole1 / 4
pole2 = numpy.array(range(-10, 20, 3))
pole2
pole1 + pole2
pole1 * pole2
A jak spojit dvě pole dohromady, tak jak to dělá +
u seznamů? Na to má Numpy speciální funkci hstack
.
numpy.hstack([pole1, pole2])
Všechny funkce Numpy se dají najít v domkumentaci, i když nezasvěcení v ní můžou ze začátku docela tápat. Základní matematické funkce jso popsány pod "Available ufuncs".
numpy.sin(pole)
Ještě pozor na to, že i porovnávací operátory pracují na jednotlivých prvcích. Vznikle pole bool
ů.
pole1 < pole2
Pokud budeš chtít takové pole použít v if
u, použij funkci any
(vrací True
pokud jsou některé z prvků true) nebo all
(vrací True
pokud jsou všechny prvky true)
if any(pole1 < pole2):
print('Některé prvky pole1 jsou menší než odpovídající prvky pole2')
if all(pole1 < pole2):
print('Všechny prvky pole1 jsou menší než odpovídající prvky pole2')
V praxi jsou pole, se kterými se dělá nějaké to zpracovávání dat, obrovská. Udělej si pole s milionem prvků, pomocí funkce linspace
která bere minimální a maximální hodnotu, a délku pole. (Zabere to několik MB paměti; pokud máš menší počítač, zkus třeba jen 10 tisíc.)
x = numpy.linspace(-10, 10, 1000000)
x
Pak na všechna tahle čísla zároveň zavolej funkci sin
. Všimni si jak je to rychlé – kdybys na to použila pythoní cyklus for
, trvalo by to mnohem déle. Obecně je dobré, když děláš výpočty pomocí Numpy, používat pole a vestavěné funkce místo Pythoních cyklů.
sin_x = numpy.sin(x)
sin_x
A teď si ten sinus výstup nakresli pomocí knihovny matplotlib
. Nejdřív nějaké ty importy a nastavení: řádek začínající %
je IPythonová vychytávka, která způsobí že se grafy vykreslí pr'imo v prohlížeči.
from matplotlib import pyplot
%matplotlib inline
Teď stačí použít funkci matplotlib.pyplot.plot
, která bere (v základu) dva argumenty – pole hodnot pro x
-ovou osu, a pole hodnot pro osu y
-ovou.
pyplot.plot(x, sin_x)
Pole z Numpy ale nemusí být jen řady čísel. Můžou to být i tabulky čísel! Dvou- (a více-)rozměrným polím se říká matice, a chovají se trochu jako seznamy seznamů.
matice = numpy.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
matice
matice[0]
matice[0:-1]
matice[0][2]
Na rozdíl od seznamů seznamů se matice dají indexovat dvojicí čísel (nebo složitějších indexů), oddělených čárkou. Dají se tak jednoduše vybírat celé řádky nebo sloupce.
matice[0, 2]
matice[0, 1:]
matice[:, :2]
matice[::2, ::2]
A do vybraných řádků nebo sloupců se dají i přiřazovat hodnoty.
matice2 = numpy.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
matice2
matice2[1, 1] = 0
matice2
matice2[::2, ::2] = 0
matice2
matice2[::2, ::2] = numpy.array([[-1, -3], [-7, -9]])
matice2[1, 1] = -5
matice2
Pomocí knihovny matplotlib
si můžeme matici "nakreslit" jako orázek.
pyplot.imshow(matice2, interpolation='none', cmap='gray')
# Pojmenované argumenty specifikují jak přesně se graf vykreslí:
# interpolation='none' má smysl pro malé matice, a
# cmap='gray' vykresluje v odstínech šedi:
# černá je nejmenší číslo, bílá největší
Když každému číslu přiřadit barvu,
obrazek = numpy.zeros([100, 100])
for x in range(-50, 50):
for y in range(-50, 50):
obrazek[x+50, y+50] = x ** 2 + y ** 2
pyplot.imshow(obrazek)
Jak je napsáno výše, je lepší nepoužívat for
-cykly, protože s velkou spoustou čísel by to bylo pomalé. Numpy má spoustu funkcí, které dokážou cykly nahradit – jen je potřeba je znát, či umět najít v dokumentaci.
n = numpy.linspace(-50, 50, 1000)
x, y = numpy.meshgrid(n, n)
z = x ** 2 + y ** 2
pyplot.imshow(obrazek)
Pole v Numpy mohou mít i více rozměrů než dva. Trojrozměrná matice je jako seznam seznamů seznamů. Každý pixel v obrázku, který jsme kreslily před chvílí, by sám obsahoval několik hodnot.
Příklad trojrozměrné matice je počítačový obrázek – pro každý pixel se zapisuje intenzita červené, zelené a modré barvy (angl. red, green, blue; RGB) – tedy 3 hodnoty.
from PIL import Image
# Obrázek © Mokele, http://en.wikipedia.org/wiki/User:HCA
# http://commons.wikimedia.org/wiki/File:Ball_python_lucy.JPG
krajta = numpy.array(Image.open('krajta.jpg'))
krajta
Funkce imshow
si s takovou věcí poradí, a nakreslí obrázek správných barev. (Jen si pořád myslí že to je graf, tak automaticky doplní osy.)
pyplot.imshow(krajta)
Indexování funguje stejně jako u dvourozměrných polí. V případě obrázků představuje první číslo řádek, druhé sloupec, ...
pyplot.imshow(krajta[400:-200, 0:300])
... a třetí index je barva. Můžeš třeba snížit intenzitu modré, aby obrázek zežloutl:
krajta[:, :, 2] = krajta[:, :, 2] / 2
pyplot.imshow(krajta)
(A aby se nemuselo při indexování vícerozměrných polí opakovat :, :, :, :
, dá se to nahradit třemi tečkami:)
krajta[..., 0] = krajta[..., 0] / 2
pyplot.imshow(krajta)
Tak, teď když něco víš o tom, jak funguje Numpy a jeho pole, můžeš se (s trochou googlení) směle pustit do analýzy obrazů, zvuků, matematických funkcí nebo fyzikálních simulací. Pokud znáš např. Matlab, zkus googlit "Python" + jméno funkce v Matlabu; často zjistíš že Numpy (nebo jiná knihovna, třeba Scipy) podobnou funkci obsahuje taky. A že na její použití nepotřebuješ drahou licenci :)
Většina dat, které se analyzují, nebudou matice jako takové, ale budou to tabulky se sloupečky. My zkusíme zpracovat data ze sčítání lidu, domů a bytů, které zveřejňuje Český statistický úřad. Nejvhodnější formát pro stažení je pro nás je CSV; pro porozumění si stáhni i "Popis dat" v PDF.
Koukni se stažený soubor v textovém editoru, ať vidíš o co jde. Pak ho načti pomocí Pandas:
import pandas
data = pandas.read_csv('SLDB_ZV.CSV', encoding='cp1250')
data.head() # metoda head() nám ukáže jen prvních pár řádků, aby výpis nebyl tak dlouhý.
Názvy sloupců od ČSÚ nedávají moc smysl, tak si je přejmenuj:
data.columns = ['typ', 'název', 'číslo', 'kód',
'obyvatel', 'muži', 'ženy', 'obyv_0', 'obyv_15', 'obyv_65',
'aktivní', 'zamestnaní', 'domy', 'byty', 'domácnosti']
data.head()
Data v Pandas tabulce se dají indexovat, ale na rozdíl od matic, indexy vybírají sloupce. Pokud chceš vybrat řádek musíš napsat:
data.loc[10]
Sloupce se pak indexují jménem (řetězcem), nikoli číslem:
data['obyvatel']
Možná sis v předchozím výstupu všimla levého sloupce, který obsahuje čísla řádků. Pandas mu říká index. Je to speciální sloupec, který "pojmenovává" celou řádku, a vypisuje se na příhodných místech. Bylo by fajn tam mít místo čísla jméno. Index se dá se změnit, ale musíš si dávat pozor, aby neobsahoval duplikáty. V ČR je 15 obcí jménem 'Nová Ves'
, takže nemůžeme jednoznačně pojmenovat jenom jménem. Naštěstí Pandas umí takzvané složené indexy, takže můžeme zkombinovat číslo řádku (kvůli unikátnosti) a jméno (pro přehlednost):
data.set_index([data.index, 'název'], drop=False, inplace=True)
data.head()
data['obyvatel']
Při aritmetických operacích se sloupečky chovají podobně jako matice: operace se provede nad odpovídajícími hodnotami. Dá se třeba jednoduše zjistit poměr počtů mužů a žen:
data['ženy'] / data['muži']
Nebo zjistit, který z řádků představuje kraj:
data['typ'] == 'kraj'
A speciální vychytávka: sloupcem typu bool
se dá indexovat, a vybrat tak příslušné řádky tabulky. Takhle se tvoří tabulka, ve které jsou jenom kraje:
kraje = data[data['typ'] == 'kraj']
kraje
Když budeš chtít vybrat víc sloupců, dá se indexovat i seznamem řetězců:
kraje[['název', 'číslo']]
Následující kód přidá do tabulky nový sloupec.
Tabulka kraje
je automaticky "svázaná" s původní data
– změny v jedné z nich se promítnou v té druhé a naopak. Nejde tedy přidat sloupec jen do krajů, protože by chyběly informace pro ostatní řádky. Nejjednoduší řešení je udělat kopii tabulky kraje
, která svázaná
nebude.
Přidání nového sloupce je pak celkem přímočaré.
kraje = kraje.copy()
kraje['poměr'] = kraje['ženy'] / kraje['muži']
kraje[['muži', 'ženy', 'poměr']]
No a nakonec si ukážeme nějaké ty grafy a obrázky.
kraje[['muži', 'ženy']].plot(kind='bar')
kraje.plot(kind='scatter', x='domy', y='byty', s=kraje['domácnosti']/1000)
a další viz dokumentace k funkci plot()