Nauč se Python > Kurzy > Datový kurz PyLadies > Představení, Jupyter notebook, základy pandas > Knihovna pandas a základní manipulace s tabulkovými daty

Knihovna pandas a základní manipulace s tabulkovými daty

Před jakoukoli složitější analýzou je třeba naučit se základy práce se zpracovávanými daty. Data můžou být různého charakteru - jednorozměrná, dvourozměrná, strukturovaná, nestrukturovaná, obrazová, zvuková... V lekcích explorativní datové analýzy budeme pracovat s daty převážně tabulkovými - tedy takovými, které jistě znáš ze svého oblíbeného (nebo neoblíbeného) tabulkového procesoru ("spreadsheetu"). Obvykle každý řádek takové tabulky odpovídá nějaké věci, exempláři čehosi, případně nějakému pozorování. V jednotlivých sloupcích se pak nacházejí jednotlivé vlastnosti či měřené veličiny pro tyto věci charakteristické.

Ve světě Pythonu se pro zpracování tabulkových dat nejčastěji používá knihovna pandas. Ta umožňuje načítat data z mnohých formátů (včetně sešitů XLS(x)), různě je upravovat, velice efektivně počítat se sloupci, přímo zkoumat některé statistické ukazatele a v neposlední řadě výsledky pěkně vizualizovat. Tato lekce tě seznámí se základními používanými koncepty a naučí tě přistupovat k jednotlivým sloupcům, řádkům a buňkám.

Více o knihovně pandas najdeš na jejích domovských stránkách: https://pandas.pydata.org/

Import knihovny pandas

In [1]:
import pandas as pd     # K pandas budeme přistupovat pomocí aliasu pd

💡 Tento příkaz sice importuje modul (resp. knihovnu) pandas, ale ta nebude dostupná pod svým obvyklým jménem, nýbrž pod aliasem pd. Naopak jméno pandas nebude nadále definováno. Při běžném programování se snažíme aliasům vyhýbat, protože snižují čitelnost kódu pro další programátory. U datové analytiky je to jinak, protože použití jednoho aliasu, který je navíc velmi běžný, nám ušetří spoustu psaní.

In [2]:
# pandas   -> Vyvolalo by NameError

Načtení tabulky s daty

Skočíme do pandas rovnýma nohama a ukážeme si typický příklad dat, která s touto knihovnou budeme zpracovávat.

Pro čtení dat má Pandas celou řadu funkcí read_*, díky kterým si poradí s mnoha různými formáty. Poměrně častým je formát CSV ("comma-separated values" - wiki), ve kterém každému záznamu odpovídá jeden řádek, jednotlivé vlastnosti záznamu pak jsou odděleny čárkami (případně jiným znakem).

Pro práci s tímto notebookem si nejdříve stáhni soubor s daty z tohoto odkazu. Data pro pokusy jsou vytvořena z komplexního Pokedexu na Githubu.

In [3]:
tabulka_pokemonu = pd.read_csv("static/pokemon.csv")

Data (ať už to je cokoliv) jsou nyní načtená v paměti, odkazuje na ně proměnná tabulka_pokemonu. Pojďme se podívat, co se v nich ukrývá.

In [4]:
tabulka_pokemonu
Out[4]:
id name height weight color shape is baby type 1 type 2 hp attack defense speed
0 1 bulbasaur 0.7 6.9 green quadruped False Grass Poison 45 49 49 45
1 2 ivysaur 1.0 13.0 green quadruped False Grass Poison 60 62 63 60
2 3 venusaur 2.0 100.0 green quadruped False Grass Poison 80 82 83 80
3 4 charmander 0.6 8.5 red upright False Fire NaN 39 52 43 65
4 5 charmeleon 1.1 19.0 red upright False Fire NaN 58 64 58 80
... ... ... ... ... ... ... ... ... ... ... ... ... ...
802 803 poipole 0.6 1.8 purple upright False Poison NaN 67 73 67 73
803 804 naganadel 3.6 150.0 purple wings False Poison Dragon 73 73 73 121
804 805 stakataka 5.5 820.0 gray quadruped False Rock Steel 61 131 211 13
805 806 blacephalon 1.8 13.0 white humanoid False Fire Ghost 53 127 53 107
806 807 zeraora 1.5 44.5 yellow humanoid False Electric NaN 88 112 75 143

807 rows × 13 columns

Pokud vše zafungovalo, jak má, měl(a) bys před sebou mít relativně pěkně naformátovanou tabulku. Základní zobrazení v notebooku ti ukáže prvních pět a posledních pět řádků (kdo by riskoval, že mu tisíce řádků zahltí okno prohlížeče?) v tabulce společně s informací o celkovém počtu řádků a sloupců. V tomto případě tabulka obsahuje celkem 13 vlastností (pojmenovaných sloupců) pro 807 různých pokémonů (očíslovaných řádků).

⚠️ Varování: V tomto jednoduchém případě se tabulka načetla správně hned na první pokus, bez jakýchkoliv specifických parametrů, všechny sloupce se zdají obsahovat použitelné hodnoty. To je (zejména u formátu CSV) vlastně z pekla štěstí. Obvykle jsou se vstupními daty problémy - kupříkladu nemají popsané sloupce (případně je mají popsané podivně), používají zvláštní oddělovače záznamů nebo desetinné části čísel, v mnoha řádcích chybějí hodnoty (nebo jsou špatně opsané), ... Čištění dat se budeme věnovat někdy příště.

Co je ten objekt uložený v proměnné tabulka_pokemonu vlastně zač? Jaké je třídy?

Základní třídy v pandas - DataFrame, Series, Index

In [5]:
type(tabulka_pokemonu)
Out[5]:
pandas.core.frame.DataFrame

Odpověď zní DataFrame. Tento termín (používaný i v dalších oblíbených statistických jazycích, například R) je nespíš bez českého ekvivalentu, a tak nadále budeme mluvit o tabulkách či o instancích třídy DataFrame. Pokusme se nyní náš první DataFrame rozpitvat.

⚠️ Varování: Jistě si brzy všimneš, že se DataFrame svými funkcemi hodně podobná sešitu v tabulkovém procesoru, ale je potřeba si uvědomit, kde tato paralela končí. Na rozdíl od sešitů v Excelu nebo LibreOffice Calcu DataFrame obsahuje "pouze" suchá data, neukládá žádné formátování a nenabízí žádný "editor". Pěkná vizuální reprezentace je jen otázkou souhry pandas s Jupyter notebookem, případně si pro to můžeš napsat vlastní kód.

In [6]:
vysky = tabulka_pokemonu["height"]
vysky
Out[6]:
0      0.7
1      1.0
2      2.0
3      0.6
4      1.1
      ... 
802    0.6
803    3.6
804    5.5
805    1.8
806    1.5
Name: height, Length: 807, dtype: float64

💡 DataFrame se mimo jiné chová podobně jako slovník (dict) - když do hranatých závorek vložíš nějaký klíč, získáš takto pojmenovaný sloupec. Ve skutečnosti hranaté závorky (square brackets) umožňují vybírat z tabulek na základě různých dalších kritérií, ale k tomu se ještě dostaneme.

Naše pitva pokračuje zjištěním, co je zač proměnná vysky.

In [7]:
type(vysky)
Out[7]:
pandas.core.series.Series

Series

Sloupce jsou typu Series (česky řada, ale ani toto slovo nebudeme používat). Tento typ vypadá jako seznam (list). Ověříme si, jestli se tak i chová:

In [8]:
vysky[0]     # První výška? ✓
Out[8]:
0.7
In [9]:
vysky[-5:]   # Posledních pět výšek? ✓
Out[9]:
802    0.6
803    3.6
804    5.5
805    1.8
806    1.5
Name: height, dtype: float64

Úkol: Zkus ještě nějaké další operace se seznamy, které už umíš, aplikovat i na vysky. Někdy to půjde, někdy ne.

Není také žádný problém mezi seznamy a Series převádět. Vůbec nejjednodušší způsob, jak můžeš vytvořit vlastní Series (pomni, že mimo kontext tabulky), je vytvořit instanci této třídy s nějakým seznamem jako argumentem:

In [10]:
cisla = pd.Series([1, 2, 3])
cisla
Out[10]:
0    1
1    2
2    3
dtype: int64

A jde to i naopak:

In [11]:
cisla.tolist()     # Varianta 1 (preferovaná, rychlejší)
list(cisla)        # Varianta 2
Out[11]:
[1, 2, 3]

Čím se tedy Series od seznamu liší a v čem spočívá jeho výhoda?

Každý sloupec má především následujících pět základních vlastností:

1) hodnoty

In [12]:
vysky[:50].values    # Z estetických důvodů si sloupec trochu zkrátíme
Out[12]:
array([0.7, 1. , 2. , 0.6, 1.1, 1.7, 0.5, 1. , 1.6, 0.3, 0.7, 1.1, 0.3,
       0.6, 1. , 0.3, 1.1, 1.5, 0.3, 0.7, 0.3, 1.2, 2. , 3.5, 0.4, 0.8,
       0.6, 1. , 0.4, 0.8, 1.3, 0.5, 0.9, 1.4, 0.6, 1.3, 0.6, 1.1, 0.5,
       1. , 0.8, 1.6, 0.5, 0.8, 1.2, 0.3, 1. , 1. , 1.5, 0.2])
In [13]:
type(vysky.values)
Out[13]:
numpy.ndarray

💡 Hodnoty jsou v Series uloženy ve speciálním formátu postaveném na typu ndarray z knihovny numpy. Té se věnovat nebudeme, ale zejména v případě numerických hodnot ušetří místo v paměti a zrychlí matematické operace (například sečíst všechny hodnoty v Series je výrazně rychlejší než v seznamu).

2) typ hodnot

In [14]:
vysky.dtype
Out[14]:
dtype('float64')

💡 Na rozdíl od seznamů by všechny prvky Series měly být stejného typu (pokud nejsou, zvolí se nejbližší společný nadtyp). pandas má vlastní sadu typů, tzv. dtypes, která částečně kopíruje výchozí datové typy v Pythonu, ale (především u numerických typů) se blíží více k tomu, jak s nimi pracuje procesor. A nehledejte u nich dědičnost (dobrá zpráva?). Nejběžnější typy si představíme příště - společně s operacemi, které se se sloupci dají dělat.

3) index

In [15]:
vysky.index
Out[15]:
RangeIndex(start=0, stop=807, step=1)

💡 K prvkům seznamu přistupuješ pomocí číselného pořadí (0 - první prvek, 1 - druhý, ...), ze slovníku vybíráš podle klíče, pandas zavádí zobecněný index, který může být číselný, řetězcový, ale třeba i postavený na datu/času. O různých indexech viz níže.

4) jméno

In [16]:
vysky.name
Out[16]:
'height'

💡 Series může, ale nemusí mít jméno. Pozor, je to hodnota uložená uvnitř samotného objektu, nijak nesouvisí se jménem proměnné, do které ho uložíš (ale u sloupce v tabulce se použije pro přístup k němu).

5) velikost

In [17]:
vysky.size
Out[17]:
807

💡 Tato vlastnost říká, kolik je v Series prvků. Není nijak magická, chová se jako len u seznamu (a ostatně len lze použít i na Series). Pro úplnost uvádíme, že na rozdíl od ostatních vlastností je tato jen pro čtení.

Úkol: Zjisti hodnoty atributů .name, .index, .dtype, .values a size u objektu cisla. Všimneš si něčeho zajímavého? Případně se podívej na totéž u některých dalších sloupců z tabulka_pokemonu.

Při vytváření objektů Series lze tyto atributy (až na size a v omezené míře dtype) explicitně uvést:

In [18]:
vek = pd.Series(
    [27, 65, 14],
    name="Věk",
    index=["Karla", "Martina", "Žofie"],
    dtype=float,
)
vek
Out[18]:
Karla      27.0
Martina    65.0
Žofie      14.0
Name: Věk, dtype: float64

Úkol: Vytvoř objekt Series, který bude obsahovat seznam barev, zvířat, čísel nebo nějaké jiné kategorie věcí, které máš rád/a.

Index

Ve výchozím stavu je u sloupců i tabulek použit bezejmenný číselný index, který řadí prvky po sobě od nuly výše:

In [19]:
vysky.index
Out[19]:
RangeIndex(start=0, stop=807, step=1)

Nicméně existují i další typy indexů (většinou výčtové):

In [20]:
vek.index
Out[20]:
Index(['Karla', 'Martina', 'Žofie'], dtype='object')
In [21]:
udalosti = pd.Series(
    ["Nezávislost Československa", "Konec druhé světové války", "Sametová revoluce"],
    index = pd.Index([1918, 1945, 1989], name="rok")   # Index může mít i jméno
)
udalosti
Out[21]:
rok
1918    Nezávislost Československa
1945     Konec druhé světové války
1989             Sametová revoluce
dtype: object
In [22]:
udalosti.index
Out[22]:
Int64Index([1918, 1945, 1989], dtype='int64', name='rok')

Tento index je číselný, ale hodnoty nejsou (resp. jsou, ale nemusely by být) srovnané a jsou "děravé".

In [23]:
udalosti_presne = pd.Series(
    ["Nezávislost Československa", "Konec druhé světové války", "Sametová revoluce"],
    index = pd.DatetimeIndex(['1918-10-28', '1945-05-08', '1989-11-17'])
)
udalosti_presne.index
Out[23]:
DatetimeIndex(['1918-10-28', '1945-05-08', '1989-11-17'], dtype='datetime64[ns]', freq=None)

Hodnoty indexu potom lze použít v hranatých závorkách pro přístup k prvkům Series, podobně jako u slovníku. Možnosti jsou ale mnohem širší, ukážeme si je za chvíli v kontextu DataFrame.

In [24]:
vek["Martina"]
Out[24]:
65.0

Úkol: Jaký index má tabulka pokémonů?

DataFrame

Když už jsme se seznámili se sloupci a indexy, můžeme se vrátit k tabulce, resp. DataFrame.

Podobně jako je Series kontejnerem hodnot spojených s indexem, DataFrame je dvojrozměrným kontejnerem, který kromě hodnot (.values) obsahuje hned dva indexy - jeden pro řádky a jeden pro sloupce:

In [25]:
tabulka_pokemonu.columns    # Seznam sloupců
Out[25]:
Index(['id', 'name', 'height', 'weight', 'color', 'shape', 'is baby', 'type 1',
       'type 2', 'hp', 'attack', 'defense', 'speed'],
      dtype='object')
In [26]:
tabulka_pokemonu.index      # Index (seznam řádků)
Out[26]:
RangeIndex(start=0, stop=807, step=1)
In [27]:
tabulka_pokemonu.values
Out[27]:
array([[1, 'bulbasaur', 0.7, ..., 49, 49, 45],
       [2, 'ivysaur', 1.0, ..., 62, 63, 60],
       [3, 'venusaur', 2.0, ..., 82, 83, 80],
       ...,
       [805, 'stakataka', 5.5, ..., 131, 211, 13],
       [806, 'blacephalon', 1.8, ..., 127, 53, 107],
       [807, 'zeraora', 1.5, ..., 112, 75, 143]], dtype=object)
In [28]:
tabulka_pokemonu.shape       # Velikost (počet řádků x počet sloupců)
Out[28]:
(807, 13)

Zkonstruovat novou tabulku lze (kromě načtení dat z externího souboru) několika způsoby, z nichž asi nejčastější jsou ze seznamu slovníků nebo slovníku seznamů. Podobně jako u Series, některé atributy lze dodat jako další argumenty.

In [29]:
pd.DataFrame({
    "cislo": [1, 2, 3],
    "pismeno": ["a", "b", "c"]
})
Out[29]:
cislo pismeno
0 1 a
1 2 b
2 3 c
In [30]:
pd.DataFrame([
        {"jmeno": "máslo", "cena": 42.90},
        {"jmeno": "sýr", "cena": 31.90},
        {"jmeno": "kečup", "cena": 49.90 },   
    ],
    index=["artikl1", "artikl2", "artikl3"]
)
Out[30]:
jmeno cena
artikl1 máslo 42.9
artikl2 sýr 31.9
artikl3 kečup 49.9

Úkol: Vytvoř tabulku (DataFrame), která bude obsahovat sloupce "jméno", "příjmení" a "věk" pro postavy z nějakého tvého oblíbeného románu či filmu. Můžeš, ale nemusíš v ní použít index.

Indexování

Řádky, sloupce, číselné pořadí, klíče, rozsahy... Objekty pandas se někdy chovají jako seznamy, někdy jako slovníky. Jak z nich tedy dostat hodnoty? Je toho hodně, a proto přistupování k částem tabulky nevystačíme s prostými hranatými závorkami [].

Pro začátek si upravíme naši tabulku s pokémony tak, aby měla zajímavý a snadno uchopitelný index. Použijeme k tomu dvě metody třídy DataFrame (obě vrací novou instanci DataFrame, odvozenou od instance, na které je voláme):

  • set_index vrací tabulku, ve které je některý ze sloupců použit jako index

  • sort_index vrací tabulku, která obsahuje stejný index, ale seřazený

In [31]:
pokemoni = tabulka_pokemonu.set_index("name").sort_index()
pokemoni
Out[31]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
abomasnow 460 2.2 135.5 white upright False Grass Ice 90 92 75 60
abra 63 0.9 19.5 brown upright False Psychic NaN 25 20 15 90
absol 359 1.2 47.0 white quadruped False Dark NaN 65 130 60 75
accelgor 617 0.8 25.3 red arms False Bug NaN 80 70 40 145
aegislash 681 1.7 53.0 brown blob False Steel Ghost 60 50 150 60
... ... ... ... ... ... ... ... ... ... ... ... ...
zoroark 571 1.6 81.1 gray upright False Dark NaN 60 105 60 105
zorua 570 0.7 12.5 gray quadruped False Dark NaN 40 65 40 65
zubat 41 0.8 7.5 purple wings False Poison Flying 40 45 35 55
zweilous 634 1.4 50.0 blue quadruped False Dark Dragon 72 85 70 58
zygarde 718 5.0 305.0 green squiggle False Dragon Ground 108 100 121 95

807 rows × 12 columns

In [32]:
pokemoni.index
Out[32]:
Index(['abomasnow', 'abra', 'absol', 'accelgor', 'aegislash', 'aerodactyl',
       'aggron', 'aipom', 'alakazam', 'alomomola',
       ...
       'zapdos', 'zebstrika', 'zekrom', 'zeraora', 'zigzagoon', 'zoroark',
       'zorua', 'zubat', 'zweilous', 'zygarde'],
      dtype='object', name='name', length=807)

[]

Začneme s hranatými závorkami:

  • U Series vrací hodnotu, jíž přináleží příslušný klíč v indexu (to jsme si ukázali výše).
  • U DataFrame vrací sloupec s příslušným jménem
In [33]:
pokemoni["height"]
Out[33]:
name
abomasnow    2.2
abra         0.9
absol        1.2
accelgor     0.8
aegislash    1.7
            ... 
zoroark      1.6
zorua        0.7
zubat        0.8
zweilous     1.4
zygarde      5.0
Name: height, Length: 807, dtype: float64

Pokud do závorek u DataFrame vložíš několik hodnot v seznamu, dostaneš více sloupců (a tedy DataFrame!):

In [34]:
pokemoni[["height", "weight"]]
Out[34]:
height weight
name
abomasnow 2.2 135.5
abra 0.9 19.5
absol 1.2 47.0
accelgor 0.8 25.3
aegislash 1.7 53.0
... ... ...
zoroark 1.6 81.1
zorua 0.7 12.5
zubat 0.8 7.5
zweilous 1.4 50.0
zygarde 5.0 305.0

807 rows × 2 columns

Úkol: Co se stane, když totéž učiníš se Series?

Úkol: Který z posledních 5 pokemonů (abecedně seřazených) je nejrychlejší?

.loc[]

Když chceme získat nějaký řádek, použijeme atribut loc, takzvaný indexer. Pozor, není to metoda a nepoužívají se závorky kulaté, nýbrž hranaté. (Má to své důvody - jedině tak můžeme elegantně používat zkrácený dvojtečkový zápis pro rozsahy).

In [35]:
pokemoni.loc["abra"]
Out[35]:
id              63
height         0.9
weight        19.5
color        brown
shape      upright
is baby      False
type 1     Psychic
type 2         NaN
hp              25
attack          20
defense         15
speed           90
Name: abra, dtype: object

Zajímal nás řádek s indexem "abra" a dostali jsme očekávaný výsledek - Series, kde každá hodnota je oindexovaná jménem sloupce.

Ovšem situace začne být zajímavá, když začneme v indexu používat rozsahy (pomni, že něco takového slovníky neumí):

In [36]:
pokemoni.loc["z":]
Out[36]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
zangoose 335 1.3 40.3 white upright False Normal NaN 73 115 60 90
zapdos 145 1.6 52.6 yellow wings False Electric Flying 90 90 85 100
zebstrika 523 1.6 79.5 black quadruped False Electric NaN 75 100 63 116
zekrom 644 2.9 345.0 black upright False Dragon Electric 100 150 120 90
zeraora 807 1.5 44.5 yellow humanoid False Electric NaN 88 112 75 143
zigzagoon 263 0.4 17.5 brown quadruped False Normal NaN 38 30 41 60
zoroark 571 1.6 81.1 gray upright False Dark NaN 60 105 60 105
zorua 570 0.7 12.5 gray quadruped False Dark NaN 40 65 40 65
zubat 41 0.8 7.5 purple wings False Poison Flying 40 45 35 55
zweilous 634 1.4 50.0 blue quadruped False Dark Dragon 72 85 70 58
zygarde 718 5.0 305.0 green squiggle False Dragon Ground 108 100 121 95

Pandas inteligentně pochopil, že chceme všechny klíče v nějakém rozsahu, dokonce aniž by byly v indexu přítomny.

⚠️ Toto ovšem lze učinit jen se seřazeným indexem. Pokud index seřazený není, vybírá se rozsah existujících klíčů v pořadí, jak jdou za sebou, včetně obou krajních hodnot, tedy takto:

In [37]:
pokemoni.loc["zangoose":"zygarde"]
Out[37]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
zangoose 335 1.3 40.3 white upright False Normal NaN 73 115 60 90
zapdos 145 1.6 52.6 yellow wings False Electric Flying 90 90 85 100
zebstrika 523 1.6 79.5 black quadruped False Electric NaN 75 100 63 116
zekrom 644 2.9 345.0 black upright False Dragon Electric 100 150 120 90
zeraora 807 1.5 44.5 yellow humanoid False Electric NaN 88 112 75 143
zigzagoon 263 0.4 17.5 brown quadruped False Normal NaN 38 30 41 60
zoroark 571 1.6 81.1 gray upright False Dark NaN 60 105 60 105
zorua 570 0.7 12.5 gray quadruped False Dark NaN 40 65 40 65
zubat 41 0.8 7.5 purple wings False Poison Flying 40 45 35 55
zweilous 634 1.4 50.0 blue quadruped False Dark Dragon 72 85 70 58
zygarde 718 5.0 305.0 green squiggle False Dragon Ground 108 100 121 95

Když se chceš dostat ke konkrétní hodnotě, použiješ v hranatých závorkách dva klíče v pořadí řádek, sloupec.

In [38]:
pokemoni.loc["zorua", "color"]
Out[38]:
'gray'

Pozor ale na počet závorek. Pokud se v závorce vyskytne seznam s klíči, vyberou se v dané dimenzi všechny odpovídající řádky či hodnoty:

In [39]:
pokemoni.loc[["zorua", "zubat"]]
Out[39]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
zorua 570 0.7 12.5 gray quadruped False Dark NaN 40 65 40 65
zubat 41 0.8 7.5 purple wings False Poison Flying 40 45 35 55

Přístupy lze samozřejmě (nebo ne tak samozřejmě?) kombinovat, a tak můžeš vybírat rozsahy i seznamy v řádcích a sloupcích nezávisle na sobě:

In [40]:
pokemoni.loc["j":"k", ["color", "attack"]]
Out[40]:
color attack
name
jangmo-o gray 55
jellicent white 60
jigglypuff pink 45
jirachi yellow 100
jolteon yellow 65
joltik yellow 47
jumpluff blue 55
jynx red 50

Úkol: Jakou barvu mají (všichni) pokémoni, jejichž jméno začíná na "z"?

Úkol: Kolik pokemonů existuje se jménem mezi písmeny "d" a "f"?

Úkol: Ze seznamu všech pokemonů jsi vyber 5 s jménem tobě sympatickým (vyhni se prvním a posledním pěti). Jakého jsou typu? Který je nejvyšší a který nejtěžší?

.iloc[]

Když chceme na chvíli zapomenout na to, jaký index je u tabulky nebo sloupce použit, můžeme k prvkům přistupovat přímo přes jejich pořadí (čísla řádku nebo sloupce). Toto je v zásadě intuitivní a odpovídá indexování, na které jsi zvyklá/ý z práce se seznamy.

In [41]:
pokemoni.iloc[44]
Out[41]:
id                15
height             1
weight          29.5
color         yellow
shape      bug-wings
is baby        False
type 1           Bug
type 2        Poison
hp                65
attack            90
defense           40
speed             75
Name: beedrill, dtype: object
In [42]:
pokemoni.iloc[-10:]
Out[42]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
zapdos 145 1.6 52.6 yellow wings False Electric Flying 90 90 85 100
zebstrika 523 1.6 79.5 black quadruped False Electric NaN 75 100 63 116
zekrom 644 2.9 345.0 black upright False Dragon Electric 100 150 120 90
zeraora 807 1.5 44.5 yellow humanoid False Electric NaN 88 112 75 143
zigzagoon 263 0.4 17.5 brown quadruped False Normal NaN 38 30 41 60
zoroark 571 1.6 81.1 gray upright False Dark NaN 60 105 60 105
zorua 570 0.7 12.5 gray quadruped False Dark NaN 40 65 40 65
zubat 41 0.8 7.5 purple wings False Poison Flying 40 45 35 55
zweilous 634 1.4 50.0 blue quadruped False Dark Dragon 72 85 70 58
zygarde 718 5.0 305.0 green squiggle False Dragon Ground 108 100 121 95

I tady jde kombinovat. A tak, až tě někdo požádá o hodnotu, která se nachází "vlevo dole", můžeš zkusit:

In [43]:
pokemoni.iloc[-1,0]
Out[43]:
718

Na závěr, jen pro úplnost si představíme tři pohodlné funkce, které vybírají první, poslední nebo náhodné řádky z tabulky (všechny tři mají nepovinný parametr udávající počet požadovaných řádků):

In [44]:
pokemoni.head()    # Prvních několik řádků
Out[44]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
abomasnow 460 2.2 135.5 white upright False Grass Ice 90 92 75 60
abra 63 0.9 19.5 brown upright False Psychic NaN 25 20 15 90
absol 359 1.2 47.0 white quadruped False Dark NaN 65 130 60 75
accelgor 617 0.8 25.3 red arms False Bug NaN 80 70 40 145
aegislash 681 1.7 53.0 brown blob False Steel Ghost 60 50 150 60
In [45]:
pokemoni.tail()     # Posledních několik řádků
Out[45]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
zoroark 571 1.6 81.1 gray upright False Dark NaN 60 105 60 105
zorua 570 0.7 12.5 gray quadruped False Dark NaN 40 65 40 65
zubat 41 0.8 7.5 purple wings False Poison Flying 40 45 35 55
zweilous 634 1.4 50.0 blue quadruped False Dark Dragon 72 85 70 58
zygarde 718 5.0 305.0 green squiggle False Dragon Ground 108 100 121 95

Úkol: Umíš napsat obdobu funkce .tail() pomocí indexování?

In [46]:
pokemoni.sample()    # Náhodný řádek
Out[46]:
id height weight color shape is baby type 1 type 2 hp attack defense speed
name
totodile 158 0.6 9.5 blue upright False Water NaN 50 65 64 43

Úkol (bonusový): Umíš napsat obdobu funkce sample() pomocí indexování (a modulu random)?

Shrnutí

V této lekci jsme si ukázali tři základní typy knihovny pandas:

  • Series coby jednorozměrný objekt obsahující hodnoty stejného typu
  • DataFrame coby dvojrozměrná tabulka složená z více Series
  • Index coby zobecněný popis, jak přistupovat k prvkům Series nebo DataFrame

Kromě toho jsme se naučili z tabulek vybírat sloupce, řádky, i jednotlivé hodnoty.

V další lekci si ukážeme, jaké datové typy (přesněji dtypes) lze v pandas použít, začneme počítat a vůbec napodobovat funkce tabulkových procesorů.

Cvičení

Místní zoo zvažuje investici do nového pavilonu vyhrazeného pro Pokemony. Ředitel zoo, pan Felix, si ale není jist, jestli by se tato investice vyplatila a co všechno by to pro zoo znamenalo. Někdo mu doporučil, aby si na pomoc pozval tebe (my za to nemůžeme, přísaháme - pozn. autoři kurzu). Pan ředitel sepsal seznam otázek, na které by chtěl znát odpověd.

  1. (pro opakování opět načti data ze souboru pokemon.csv)
  2. Pro kolik nových zvířátek by zoo potřebovala potravu? Pan ředitel by chtěl jednoho samce a jednu samičku od každého druhu (jména).
  3. Marketingové oddělení se chystá vytvořiť nové letáčky o Pokemonech pro návštěvníky zoo. Pro všechny Pokemony by potřebovali informace o jejich výšce, váze, délce, barvě a typu. Jsou všechny informace k dispozici?
  4. Provoz zoo považuje za ideální, kdyby Pokemony přivážely postupně, ve skupinách po osmi tak, jak jsou uvedeni v tabulce pokemon.csv. Kteří Pokemoni by byli v první, v druhé a v poslední skupině?
  5. Provoz zoo také upozornil na speciální podmínky nutné pro 3 nejvyšší Pokemony. V tabulce pokemon.csv jsou na pozicích 207, 320 a 796, nikto si však nepamatuje, o které Pokemony se jednalo. Jak se jmenují?
  6. Pan ředitel miluje Onixe. Chtěl by pro něho vybudovat speciální vyhlídku pro kamenné Pokemony s rychlostí vyšší než 50. Líbilo by se tam Onixovi?
  7. Pan ředitel by také chtěl vytvořit sekci pro všechny Pokemony začínající na "i". Temní Pokemoni ale nemohou být s normálními, ohniví s vodními nebo travnatými a elektrický s psychickými. Bude možné tuto sekci vytvořit? (tip: pro zobrazení všech Pokemonů na "i" budeš potřebovat mít index seřazen abecedně)

Toto je stránka lekce z kurzu, který probíhá nebo proběhl naživo s instruktorem.