Adatbázis-műveletek 1. (groupby)

In [1]:
import pandas as pd # szokásos import

Érettségi adatok feldolgozása

Az alábbiakban az elmúlt pár év érettségi statisztikai adatait fogjuk megvizsgálni. Ez a példa sok szempontból jól illusztrál olyan problémákat, amelyek valós adatbázis-elemzések kapcsán felmerülhetnek. Ilyen például a hiányzó adatok kezelése, vagy a nem egészen kompatibilis adatbázisok egységes kezelése. Az érettségi adatokat tartalmazó honlap az előzőekben megismert elválasztóval tagolt tagolt csv formátumban teszi elérhetővé, itt az elválasztójel a pontosvessző.

In [2]:
df=pd.read_csv("data/erettsegi.csv.gz",sep=";",index_col=0)
df.head()
Out[2]:
intézmény megyéje intézmény városa vizsgázó évfolyama vizsgázó képzési típusa vizsgázó neme vizsgázó képzési munkarendje vizsgatárgy neve vizsga szintje vizsga fajtája vizsgamentesség ... írásbeli pontszám mérés és tételkifejtés: tartalmi helyesség mérés és tételkifejtés: felépítés, kifejtés szóbeli pontszám év szint időszak egy téma kifejtése kísérlettel vagy méréssel: tartalom egy téma kifejtése kísérlettel vagy méréssel: felépítés, kifejtés II. összetett feladatok
0 Budapest Budapest 12.0 gimnázium férfi nappali fizika közép elõrehozott - ... NaN NaN NaN NaN 2013 K tavasz NaN NaN NaN
1 Budapest Budapest 12.0 gimnázium nappali fizika közép elõrehozott - ... NaN NaN NaN NaN 2013 K tavasz NaN NaN NaN
2 Budapest Budapest 12.0 gimnázium férfi nappali fizika közép elõrehozott - ... 86.0 NaN NaN 60.0 2013 K tavasz 55.0 5.0 48.0
3 Budapest Budapest 13.0 gimnázium nappali fizika közép elõrehozott - ... 23.0 NaN NaN 38.0 2013 K tavasz 36.0 2.0 9.0
4 Budapest Budapest 12.0 gimnázium férfi nappali fizika közép elõrehozott - ... 66.0 NaN NaN 60.0 2013 K tavasz 55.0 5.0 42.0

5 rows × 29 columns

Látható, hogy az év, szint megadják, hogy melyik évben, melyik szintű érettségiről van szó. Azt is megállapíthatjuk, hogy ősszel vagy tavasszal (időszak) írta-e a diák az érettségit, az iskolájáról és a képzési típusról is rögzítve van a statisztika. Emellett részletes írásbeli és szóbeli, illetve összpontszám, összesített százalék is szerepel az adatok között.

Érdemes az első néhány sort kiíratni példaként, hogy lássuk, mivel is van dolgunk. Most transzponálva írjuk ki, hogy elférjen a képernyőre.

In [3]:
df.head().T
Out[3]:
0 1 2 3 4
intézmény megyéje Budapest Budapest Budapest Budapest Budapest
intézmény városa Budapest Budapest Budapest Budapest Budapest
vizsgázó évfolyama 12 12 12 13 12
vizsgázó képzési típusa gimnázium gimnázium gimnázium gimnázium gimnázium
vizsgázó neme férfi férfi férfi
vizsgázó képzési munkarendje nappali nappali nappali nappali nappali
vizsgatárgy neve fizika fizika fizika fizika fizika
vizsga szintje közép közép közép közép közép
vizsga fajtája elõrehozott elõrehozott elõrehozott elõrehozott elõrehozott
vizsgamentesség - - - - -
vizsga nyelve magyar magyar magyar magyar magyar
érdemjegy 1 1 5 3 5
össz százalék 26 19 97 40 84
össz pontszám 40 29 146 61 126
vizsgázó részvétele nem jelent meg nem jelent meg megjelent megjelent megjelent
I. feleletválasztós kérdéssor NaN NaN 38 14 24
II. esszé: tartalom NaN NaN NaN NaN NaN
II. esszé: kifejtés módja NaN NaN NaN NaN NaN
III. összetett feladatok NaN NaN NaN NaN NaN
írásbeli pontszám NaN NaN 86 23 66
mérés és tételkifejtés: tartalmi helyesség NaN NaN NaN NaN NaN
mérés és tételkifejtés: felépítés, kifejtés NaN NaN NaN NaN NaN
szóbeli pontszám NaN NaN 60 38 60
év 2013 2013 2013 2013 2013
szint K K K K K
időszak tavasz tavasz tavasz tavasz tavasz
egy téma kifejtése kísérlettel vagy méréssel: tartalom NaN NaN 55 36 55
egy téma kifejtése kísérlettel vagy méréssel: felépítés, kifejtés NaN NaN 5 2 5
II. összetett feladatok NaN NaN 48 9 42

Groupby, Csoportosítás

Csináljunk először egy kisebb táblázatot, hogy jól átlássuk.

In [4]:
subdf=df[['vizsga szintje','vizsgázó képzési típusa','írásbeli pontszám']]
subdf.head()
Out[4]:
vizsga szintje vizsgázó képzési típusa írásbeli pontszám
0 közép gimnázium NaN
1 közép gimnázium NaN
2 közép gimnázium 86.0
3 közép gimnázium 23.0
4 közép gimnázium 66.0

Egy oszlop értékei szerint csoportosíthatjuk a DataFrame-et, és utána a csoportokon végezhetünk műveleteket.

In [5]:
g=subdf.groupby('vizsga szintje')
g
Out[5]:
<pandas.core.groupby.DataFrameGroupBy object at 0x7ff618e8bd30>

Vegyük a csoportok maximumát!

In [6]:
g.max()
Out[6]:
vizsgázó képzési típusa írásbeli pontszám
vizsga szintje
emelt szakközép 100.0
közép szakközép 90.0

Átlagát (figyeljük meg, hogy a numerikus oszlopra csinálta csak meg)!

In [7]:
g.mean()
Out[7]:
írásbeli pontszám
vizsga szintje
emelt 63.410343
közép 52.651527

Kiválaszthatunk egy oszlopot.

In [8]:
g['írásbeli pontszám'].mean()
Out[8]:
vizsga szintje
emelt    63.410343
közép    52.651527
Name: írásbeli pontszám, dtype: float64

Ha két szögletes zárójellel választjuk ki, akkor egy oszlopra nem Series, hanem DataFrame lesz az eredmény, és szebben formázódik.

In [9]:
g[['írásbeli pontszám']].mean()
Out[9]:
írásbeli pontszám
vizsga szintje
emelt 63.410343
közép 52.651527

Egyszerre két oszlop szerint is csoportosíthatunk, ilyenkor listát kell a groupby-nak átadnunk. Itt már nem csak az Emelt oszlop, hanem a Nem oszlop is a táblázat indexének a része, ezt hívjuk többszintű indexelésnek.

In [15]:
g=subdf.groupby(['vizsga szintje','vizsgázó képzési típusa'])
g.mean()
Out[15]:
írásbeli pontszám
vizsga szintje vizsgázó képzési típusa
emelt - 51.651575
gimnázium 65.804943
szakközép 53.922330
közép - 51.004021
gimnázium 58.023469
szakközép 40.857205

Eltekinthetünk attól, hogy a csoportok indexként jelenjenek meg, és oszlopot is kaphatunk belőlük.

In [10]:
g=subdf.groupby(['vizsga szintje','vizsgázó képzési típusa']
           ,as_index=False)
g.mean()
Out[10]:
vizsga szintje vizsgázó képzési típusa írásbeli pontszám
0 emelt - 51.651575
1 emelt gimnázium 65.804943
2 emelt szakközép 53.922330
3 közép - 51.004021
4 közép gimnázium 58.023469
5 közép szakközép 40.857205

A hierarchikusan indexelt táblázatot az unstack függvénnyel átrendezhetjük, ha úgy használhatóbb.

In [11]:
g=subdf.groupby(['vizsga szintje','vizsgázó képzési típusa'])
g.mean().unstack(level=0)
Out[11]:
írásbeli pontszám
vizsga szintje emelt közép
vizsgázó képzési típusa
- 51.651575 51.004021
gimnázium 65.804943 58.023469
szakközép 53.922330 40.857205
In [12]:
g.mean().unstack(level=1)
Out[12]:
írásbeli pontszám
vizsgázó képzési típusa - gimnázium szakközép
vizsga szintje
emelt 51.651575 65.804943 53.922330
közép 51.004021 58.023469 40.857205

A következőkben felteszünk néhány példakérdést, és megválaszoljuk azt.

  • Melyik évben mennyi volt az emelt szintű érettségik jegyeinek átlaga?

Ehhez először kiválasztjuk az emelt szintű érettségit tartalmazó sorokat,

In [13]:
emelt=df[df["szint"]=="E"]

Azokat év szerint csoportosítjuk.

In [14]:
group=emelt.groupby("év")

Kiválasztjuk az "érdemjegy" oszlopot, amit a végén átlagolunk. A csoportosítás miatt az átlag évenként kerül kiszámításra.

In [15]:
group[["érdemjegy"]].mean()
Out[15]:
érdemjegy
év
2011 4.125000
2012 4.429338
2013 4.232607
2014 4.404090
2015 4.432165

Egyben:

In [16]:
df[df["szint"]=="E"].groupby("év")[["érdemjegy"]].mean()
Out[16]:
érdemjegy
év
2011 4.125000
2012 4.429338
2013 4.232607
2014 4.404090
2015 4.432165
  • Vajon a fiúk vagy a lányok írtak jobb pontszámú középszintű érettségit 2015-ben?

Először logikai indexeléssel kiválasztjuk a 2015-ös középszintű érettségiket tartalmazó sorokat. Több feltételt a sorokra egyszerre az and operátor helyett az & operátorral adhatunk meg, és a feltételeket zárójeleznünk kell, hogy jól olvassa az értelmező.

Ezek után csoportosítunk a vizsgázó neme szerint, majd vesszük az összpontszámok átlagát.

In [24]:
k_2015=df[(df["szint"]=="K") & (df["év"]==2015)]
k_2015.groupby("vizsgázó neme")[["össz pontszám"]].mean()
Out[24]:
össz pontszám
vizsgázó neme
férfi 100.270135
97.180775
  • Számoljuk le, melyik iskolatípusban hány érettségiző jelent meg, illetve nem jelent meg!

Most egyszerre két oszlop szerint is csoportosítottunk, a csoportosítás alapját képező oszlopok nevét listaként kell megadni a groupby-nak. Utána egy tetszőleges oszlopot (pl. év) kiválasztva megszámláltathatjuk csoportonként a sorokat a count-tal.

In [25]:
df.groupby(["vizsgázó képzési típusa",
            "vizsgázó részvétele"])[["év"]].count()
Out[25]:
év
vizsgázó képzési típusa vizsgázó részvétele
- megjelent 1260
nem jelent meg 490
gimnázium megjelent 14448
nem jelent meg 551
szakközép megjelent 5007
nem jelent meg 134