Matplotlib - osa 1

Yksinkertainen pylväskaavio

Tämän jupyter-notebookin tueksi kannattaa lukea myös artikkeli https://tilastoapu.wordpress.com/2019/07/02/kuviot-ja-kaaviot-pythonilla/

Matplotlib on Pythonille tarkoitettu ohjelmakirjasto grafiikan tekemiseen.

Keskityn seuraavassa grafiikan tekoon pandas dataframesta, joten tuon pandas-kirjaston pd-nimisenä.

Matplotlib-grafiikkaa varten tuon matplotlib.pyplot-kirjaston plt-nimisenä.

Jupyter notebook tulostaa automaattisti grafiikan notebookiin, jos lisään notebookin alkuun komennon %matplotlib inline.

In [1]:
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
In [2]:
# Avaan datan
df = pd.read_excel('http://taanila.fi/data1.xlsx')

# Näytän datan viisi ensimmäistä riviä
df.head()
Out[2]:
nro sukup ikä perhe koulutus palveluv palkka johto työtov työymp palkkat työteht työterv lomaosa kuntosa hieroja
0 1 1 38 1 1.0 22.0 3587 3 3.0 3 3 3 NaN NaN NaN NaN
1 2 1 29 2 2.0 10.0 2963 1 5.0 2 1 3 NaN NaN NaN NaN
2 3 1 30 1 1.0 7.0 1989 3 4.0 1 1 3 1.0 NaN NaN NaN
3 4 1 36 2 1.0 14.0 2144 3 3.0 3 3 3 1.0 NaN NaN NaN
4 5 1 24 1 2.0 4.0 2183 2 3.0 2 1 2 1.0 NaN NaN NaN
In [3]:
# Lasken eri koulutuksen suorittaneiden lukumäärät
df1 = pd.crosstab(df['koulutus'], 'Lkm')

# koulutus-muuttujan numeroarvoja vastaavat tekstimuotoiset arvot listana
koulutus = ['Peruskoulu', '2. aste', 'Korkeakoulu', 'Ylempi korkeakoulu']

# Lisään koulutus-muuttujan tekstimuotoiset arvot dataframen indeksiin
df1.index = koulutus

# Näytän dataframen
df1
Out[3]:
col_0 Lkm
Peruskoulu 27
2. aste 30
Korkeakoulu 22
Ylempi korkeakoulu 2

Vaakapylväskaavion luonti

Teen äsken luomastani dataframesta df1 vaakapylväskaavion plot-toiminnolla. Plot-toiminnolla voin luoda eri kaaviolajeja, esimerkiksi pystypylväskaavion bar tai vaakapylväskaavion barh. Samaan lopputulemaan pääsisin myös komennolla df1.plot(kind = 'barh').

Sijoitan kaavion jatkomuokkausta varten muuttujaan. Tässä annan muuttujan nimeksi ax1.

In [4]:
ax1 = df1.plot.barh()

Kaavion tekstit

Mielestäni yhden arvosarjan kaavioissa selite on tarpeeton, joten piilotan sen legend-lisäparametrilla.

Kaavion otsikoksi annan 'Koulutusjakauma' set_title()-toiminnolla.

Arvoakselin (x-akseli) otsikoksi annan 'Lukumäärä' set_xlabel()-toiminnolla.

Jos en olisi lisännyt koulutusten tekstimuotoisia arvoja dataframen df1 indeksiin, niin voisin lisätä ne kaavioon toiminnolla ax1.set_yticklabels(koulutus).

In [5]:
ax2 = df1.plot.barh(legend = False)

ax2.set_title('Koulutusjakauma')
ax2.set_xlabel('Lukumäärä')
Out[5]:
Text(0.5, 0, 'Lukumäärä')

Hienosäätöä

Lisään x-akselin jakoviivojen mukaisen taustaviivoituksen grid()-toiminnolla. Taustaviivoitus menee oletuksena pylväiden päälle, mutta asia korjaantuu set_axisbelow()-toiminnolla.

Häivytän luokka-akselin jakoviivat tick_params()-toiminnolla.

Viimeiseksi kikkailen pylväiden pituuksia vastaavat lukumäärät pylväiden viereen. Käytän tähän text()-toimintoa, jolla voin lisätä kaavioon tekstejä:

  • Käytän apuna Pythonin for-silmukkaa, jolla käyn läpi kaavion pylväät (patches-kokoelma) yksi kerrallaan.
  • Vaakasuuntaisen pylvään pituuden saan get_width()-toiminnolla.
  • Vaakasuuntaisen pylvään alareunan sijainnin saan get_y()-toiminnolla.
  • Pylvään alareunan sijaintiin lisään puolet pylvään paksuudesta (get_height()).
  • Pylvään viereen lisättävän lukumäärän, jonka sijoitan label-nimiseen muuttujaan, muotoilen desimaalittomaksi.
  • Lisättävän merkkijonon sijainti määrittyy x:n ja y:n mukaan.
  • Lisäparametri s tarkoittaa lisättävää merkkijonoa, joka tässä tapauksessa sisältää välilyönnin ja pylvään pituutta kuvaavan lukumäärän.
  • va tarkoittaa pystysuuntaista tasausta (vertical alignment).
In [6]:
ax3 = df1.plot.barh(legend = False)

ax3.set_title('Koulutusjakauma')
ax3.set_xlabel('Lukumäärä')

ax3.grid(axis = 'x')
ax3.set_axisbelow(True)
ax3.tick_params(axis = 'y', length = 0)

for bar in ax3.patches:
    x = bar.get_width()
    y = bar.get_y() + bar.get_height() / 2
    label = '{:.0f}'.format(x)
    ax3.text(x = x, y = y, s = ' ' + label, va = 'center')

Pystypylväskaavio

Pienin muutoksin voin toteuttaa edellisen pystypylväskaaviona.

  • Huomaa luokka-akselin otsikoiden 45 asteen kääntö (rotation).
  • Huomaa pylvään pituuksia vastaavien lukuarvojen siirto hieman ylöspäin (y = y + 0.3), koska muuten lukuarvot olisivat pylväissä kiinni.
  • ha tarkoittaa vaakasuuntaista tasausta (horizontal alignment).
  • Huomaa, että yläreunaan on lisätty hieman tilaa skaalamalla arvoakseli (ax3.set_ylim(0, 35)).
In [7]:
ax4 = df1.plot.bar(legend = False)

ax4.set_title('Koulutusjakauma')
ax4.set_ylabel('Lukumäärä')
ax4.set_xticklabels(koulutus, rotation = 45)

ax4.grid(axis = 'y')
ax4.set_axisbelow(True)
ax4.tick_params(axis = 'x', length = 0)

for bar in ax4.patches:
    x = bar.get_x() + bar.get_width() / 2
    y = bar.get_height()
    label = '{:.0f}'.format(y)
    ax4.text(x = x, y = y + 0.3, s = label, va = 'bottom', ha = 'center')
ax4.set_ylim(0, 35)
Out[7]:
(0, 35)

Prosenttien esittäminen

Arvoakselin prosenttilukujen muotoilulause ei ole ihan helpon näköinen: ax5.set_xticklabels(['{:.0f} %'.format(x) for x in ax5.get_xticks()])

  • Muotoilulauseke '{:0f} %' määrittää desimaalittoman prosenttimuotoilun, jolla muotoillaan kaikki x:t.
  • x:t haetaan for-silmukalla yksi kerrallaan kaavion xtickseistä get_xticks()-toiminnolla.
  • Huomaa, että kaikki ovat hakasulkujen [] sisällä (lista xticksejä!).
In [8]:
# Luon dataframen, jossa prosentit
df2 = pd.crosstab(df['koulutus'], 'Lkm')
df2['%'] = df2['Lkm'] / df2['Lkm'].sum() * 100
df2.index = koulutus
df2
Out[8]:
col_0 Lkm %
Peruskoulu 27 33.333333
2. aste 30 37.037037
Korkeakoulu 22 27.160494
Ylempi korkeakoulu 2 2.469136
In [9]:
ax5 = df2['%'].plot.barh()

ax5.set_title('Koulutusjakauma')
ax5.set_xlabel('Lukumäärä')

ax5.grid(axis = 'x')
ax5.set_axisbelow(True)
ax5.tick_params(axis = 'y', length = 0)

ax5.set_xticklabels(['{:.0f} %'.format(x) for x in ax5.get_xticks()])

for bar in ax5.patches:
    x = bar.get_width()
    y = bar.get_y() + bar.get_height() / 2
    label = '{:.0f} %'.format(x)
    ax5.text(x = x, y = y, s = ' ' + label, va = 'center')

Kaavion tallennus

Kun tallennan kaavion savefig-toiminnolla, niin tarkkaan ottaen tallennan kuvion (figure), jonka sisällä kaavio on. Kuvioon pääsen käsiksi plt.gcf()-toiminnolla (get current figure).

Ilman lisäparametria bbox_inches = 'tight' osia kaavion reunoilta voi jäädä tallentumatta.

Tallennusmuoto määräytyy tiedostonimen tarkentimen (esim. png) perusteella. Mahdolliset tallennusmuodot selviävät komennolla plt.gcf().canvas.get_supported_filetypes()

In [10]:
ax6 = df2['%'].plot.barh()

ax6.set_title('Koulutusjakauma')
ax6.set_xlabel('Lukumäärä')

ax6.grid(axis = 'x')
ax6.set_axisbelow(True)
ax6.tick_params(axis = 'y', length = 0)

ax6.set_xticklabels(['{:.0f} %'.format(x) for x in ax6.get_xticks()])

for bar in ax6.patches:
    x = bar.get_width()
    y = bar.get_y() + bar.get_height() / 2
    label = '{:.0f} %'.format(x)
    ax6.text(x = x, y = y, s = ' ' + label, va = 'center')

# Tallennus
plt.gcf().savefig('testi1.png', bbox_inches = 'tight')

Lisätietoa

plot-toiminnon yhteydessä voin antaa monia lisäparametreja (edellä hyödynsin legend-lisäparametria): https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html

Tekstiolioiden yhteydessä voin käyttää lukuisia tekstille ominaisia lisäparametreja: https://matplotlib.org/api/text_api.html#matplotlib.text.Text

Edellä käytetyillä toiminnoilla on lukuisia lisäparametreja, jotka löydät helposti googlaamalla. Googlaamalla esimerkiksi matplotlib pyplot tick_params löydät helposti tick_params()-toiminnon kuvauksen ja lisäparametrit.

Matplotlib - osa 2 käsittelee useamman arvosarjan pylväskaavioita: https://nbviewer.jupyter.org/github/taanila/kaaviot/blob/master/matplotlib2.ipynb