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 ifu, 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()