12

Pandas: transformace

Ještě než se pustíme do poslední lekce Pandasu, čeká nás představení jednoho velmi důležitého konceptu z jazyka Python, který posune naše programování zcela na novou úroveň. Naučíme se totiž psát vlastní funkce.

Vlastní funkce

Během našeho výletu Pythonem jste již potkali mnoho funkcí. Pomocí funkce round jsme zaokrouhlovali, pomocí funkce random jsme generovali náhodná čísla, pomocí funkce open jsme otvírali soubory a tak dále, a tak dále. Každá funkce je vlastně takový malý prográmek, který řeší nějakou často opakovanou činnost. Jakmile začneme psát delší programy, často narazíme na potřebu vyrobit si vlastní funkci, abychom měli zkratku pro nějakou vlastní činnost, kterou v našem programu často opakujeme.

Můžeme si například představit program, který se zabývá výměrou pozemků a často se nám stává, že chceme spočítat plochu pozemku tvaru trojúhelníka jako na tomto obrázku.

Pozemek

Na vzoreček pro výpočet plochy trojúhelníka si vzpomeneme ze základní školy.

area = width * height / 2

Pokud ale tento výpočet chceme v našem programu často opakovat, nemá smysl ho pořád dokola kopírovat. Lepší je si napsat funkci, která dostane šířku a výšku trojúhelníka jako vstup a vrátí nám spočítanou plochu. Takovou funkci bychom mohli pojmenovat například triangleArea a v Pythonu bychom ji vytvořili takto.

def triangleArea(w, h):
  return w * h / 2

Takovou funkci pak můžeme zavolat jako každou jinou se zcela konkrétními vstupními hodnotami, které pro náš příkladový pozemek činí 6 a 3.

>>> area = triangleArea(6, 3)
>>> area
9.0

Pojďme si blíže vysvětlit, jak jsme funkci vlastně vyrobili. Funkce se v Pythonu vytváří použitím kličového slova def. Za ním následuje název funkce, který si můžeme zvolit dle libosti. Za názvem funkce v závorkách uvedeme takzvané vstupní parametry. Parametry jsou speciální proměnné, do kterých nám sám Python uloží vstupy funkce ve chvíli, kdy ji zavoláme. Za dvojtečkou následuje blok kódu, který říká, co daná funkce dělá. Chceme-li z funkce vrátit výsledek, musíme použít klíčové slovo return.

V našem případě má funkce dva vstupní parametry jménem w a h. Ve chvíli, kdy jsme funkci zavolali, do těchto parametrů Python uložil hodnoty 6 a 3. Poté se provedl kód funkce, který spočítal výsledek, a ten se nám po navrácení z funkce uložil do proměnné area.

Složitější funkce

Naše funkce pro výpočet obsahu byla velmi jednoduchá. Kód funkce může být samozřejmě mnohem obsáhlejší. Napišme například funkci, která jako vstup obdrží seznam a vrátí součet všech kladných čísel z tohoto seznamu.

def sumPositive(nums):
  result = 0
  for n in nums:
    if n > 0:
      result += n
  return result

Cvičení

1

Obsah elipsy

Tentokrát chceme spočítat plochu pozemku ve tvaru elipsy jako na obrázku.

Elipsa

Z matematiky víme, že známe li šířku a výšku elipsy, její obsah je polovina šířky krát polovina výšky krát číslo pí. Napište funkci elipseArea, která spočítá plochu pozemku dle zadané šířky a výšky. Číslo pí najdete v modulu math jako math.pi.

2

Větší ze dvou čísel

Napište funkci jménem max2, který vrátí větší ze dvou zadaných čísel.

3

Geometrický průměr

Napište funkci jménem gmean, která spočítá takzvaný geometrický průměr ze zadaného seznamu čísel. Geometrický průměr n čísel se spočítá tak, že se všechny hodnoty navzájem vynásobí z výsledného součinu se spočítá n-tá odmocnina.

Bonusy

4

Větší ze tří čísel

Napište funkci jménem max3, který vrátí největší ze tří zadaných čísel.

Transformace dat v Pandasu

Vytváření vlastních funkcí jsme si nevysvětlovali jen tak nazdařbůh. Naše nové schopnosti ihned využijeme při práci s daty. Často se nám totiž stane, že data nejsou v tak úplně dokonalém formátu, jak by se nám hodilo a musíme si je trošku pomasírovat, nebo-li odborně řečeno transformovat.

Uvažme jakéhosi Kristiána, jenž se snaží o zhubnutí do svého obleku, který má ještě z tanečních na střední škole. Náš Kristián se rozhodl po 14 dní zdravěji jíst a chodit pravidelně běhat. Své úsilí si poctivě zaznamenával do následující tabulky.

den váha běh
pá 13. 75,6 kg 3 km
so 15. 75,3 kh pauza
ne 16. 75,9kg pauza
po 17. 76,1 kg 2 km
út 18. 75,4 kg paza
st 19. 75 kg pauza
čt 20. 74,9 kg 3
pá 21. 74,8 k pauza
so 22. 74,3kg 3 km
ne 23. 75,2 kg 4 km
po 24. 74,5 kg
út 25. 74,2 kg pauza
st 26. 74,1 kg 3 km
čt 27. 73,8 kg 3km

Tabulku si můžete stáhnout jako soubor vaha.txt. Bohužel Kristián není ten úplně nejvíc nejdůslednější člověk na planetě, takže hodnoty v druhém a třetím sloupečku nejsou vždy úplně konzistentní, hemží se to zde překlepy i občasnou chybějící hodnotou. Váha je řetězec, který nejen obsahuje i jednotky, ale navíc jsou desetinná čísla zapsána pomocí čárky. Navíc jsou hodnoty v tomto souboru jsou odděleny tabulátory, což svědčí o tom, že je Kritián asi vykopíroval přímo z Excelu nebo Google docs.

V dnešní lekci už nebudeme pracovat v příkazové řádce, ale napíšeme si regulérní program. Nejprve načteme naše data do DataFrame. Pozor na to, že oddělovače jsou tabulátory.

import pandas
vaha = pandas.read_csv('vaha.txt', encoding='utf-8', sep='\t')

Můžeme si všimnout, že ani v prvních sloupečku, kde naštěstí žádné překlepy nemáme, nejsou data úplně v šikovném formátu. Datumy máme jako jméno a číslo dne. To je první věc, kterou se pokusíme zpravit.

Ty nejužitečnější operace pro transformaci dat nejdeme na sériích. Vezměme si první sloupeček naší tabulky jako sérii. Pomocí vlastnosti .str můžeme pracovat se sérií řetězců úplně stejně, jako pracujeme s jedním řetězcem. Můžeme se tak například zbavit zbytečných názvů dní.

cisloDne = vaha['den'].str[3:]
print(cisloDne)

0     13.
1     15.
2     16.
3     17.
4     18.
5     19.
6     20.
7     21.
8     22.
9     23.
10    24.
11    25.
12    26.
13    27.
Name: den, dtype: object

Můžeme taky smazat otravnou tečku na konci tak, že ji prostě nahradíme prázdným řetězcem.

cisloDne = vaha['den'].str[3:].str.replace('.', '')
print(cisloDne)

0     13
1     15
2     16
3     17
4     18
5     19
6     20
7     21
8     22
9     23
10    24
11    25
12    26
13    27
Name: den, dtype: object

Všimněte si ale, že hodnoty v sérii cisloDne jsou pořád řetězce. Chceme-li je převést na čísla, musíme použít Pandas funkci to_numeric.

cisloDne = vaha['den'].str[3:].str.replace('.', '')
cisloDne = pandas.to_numeric(cislaDne)
print(cisloDne)

0     13
1     15
2     16
3     17
4     18
5     19
6     20
7     21
8     22
9     23
10    24
11    25
12    26
13    27
Name: den, dtype: int64

Takto máme sloupeček hezky vyčištěný. Zapojme ho jako nový sloupeček do naší tabulky. Zároveň ještě nahradíme sloupeček se dny pouze jejich názvy. Tím bude tabulka mnohem přehlednější.

den = vaha['den'].str[:3]
vaha['číslo dne'] = cisloDne
vaha['den'] = den
print(vaha)

    den     váha    běh  číslo dne
0   pá   75,6 kg   3 km         13
1   so   75,3 kh  pauza         15
2   ne    75,9kg  pauza         16
3   po   76,1 kg   2 km         17
4   út   75,4 kg   paza         18
5   st     75 kg  pauza         19
6   čt   74,9 kg      3         20
7   pá    74,8 k  pauza         21
8   so    74,3kg   3 km         22
9   ne   75,2 kg   4 km         23
10  po   74,5 kg    NaN         24
11  út   74,2 kg  pauza         25
12  st   74,1 kg   3 km         26
13  čt   73,8 kg    3km         27

Složitější transformace

První sloupeček byl ještě relativně snadný. S druhým to bude o dost těžší. Data nejsou moc konzistentní a dostat je do rozumné podoby znamená řešit různé speciální případy pomocí různých podmínek.