In [1]:
from datetime import datetime
print(f'Päivitetty {datetime.now()}')
Päivitetty 2021-08-15 12:48:11.954086

Matplotlib - osa 2

Usean arvosarjan pylväskaavio

Tämä on jatkoa sarjan ensimmäiselle osalle https://nbviewer.jupyter.org/github/taanila/kaaviot/blob/master/matplotlib1.ipynb. Oletan ensimmäisen osan asioiden olevan lukijalle tuttuja.

In [2]:
import pandas as pd
import matplotlib.pyplot as plt
In [3]:
df = pd.read_excel('http://taanila.fi/data1.xlsx')
df.head()
Out[3]:
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 [4]:
# sukup- ja koulutus-muuttujien tekstimuotoiset arvot
sukup = ['Mies', 'Nainen']
koulutus = ['Peruskoulu', '2. aste', 'Korkeakoulu', 'Ylempi korkeakoulu']
In [5]:
# Lasken eri koulutuksen suorittaneiden lukumäärät sukupuolen mukaan
df1 = pd.crosstab(df['koulutus'], df['sukup'])
df1.index = koulutus
df1.columns = sukup
df1
Out[5]:
Mies Nainen
Peruskoulu 22 5
2. aste 23 7
Korkeakoulu 15 7
Ylempi korkeakoulu 2 0

Vaakapylväskaavio

Dataframen riveistä tulee luokka-akselin luokat/kategoriat (koulutus) ja sarakkeista arvosarjat (sukupuoli). Jos haluat vaihtaa luokka-akselin luokat ja arvosarjat, niin transponoi dataframe T-toiminnolla: df1.T.plot.barh()

In [6]:
df1.plot.barh()

plt.title('Koulutusjakauma')
plt.xlabel('Lukumäärä')
Out[6]:
Text(0.5, 0, 'Lukumäärä')

Hienosäätöä

Parametri width muuttaa pylvään paksuutta ja samalla pylväiden välissä olevaa tyhjää tilaa. Arvo 0 häivyttää pylväät ja arvo 1 laittaa pylväät kiinni toisiinsa.

Parametri legend = 'reverse' kääntää selitteen järjestyksen. Käännetty järjestys on tässä tapauksessa havainnollisempi.

In [7]:
df1.plot.barh(width = 0.8, legend = 'reverse', zorder = 2)

plt.title('Koulutusjakauma')
plt.xlabel('Lukumäärä')

plt.grid(axis = 'x')
plt.tick_params(axis = 'y', length = 0)

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

Pystypylväskaavio

In [8]:
df1.plot.bar(width = 0.8, rot = 45, zorder = 2)

plt.title('Koulutusjakauma')
plt.ylabel('Lukumäärä')

plt.grid(axis = 'y')
plt.tick_params(axis = 'x', length = 0)
plt.ylim(0, 25)

for bar in plt.gca().patches:
    x = bar.get_x() + bar.get_width() / 2
    y = bar.get_height()
    label = '{:.0f}'.format(y)
    plt.text(x = x, y = y + 0.3, s = label, va = 'bottom', ha = 'center')

Pinottu (stacked) pylväskaavio

Pinottu pylväskaavio (stacked = True) näyttää arvosarjat samassa pylväässä, jolloin kokonaismäärä on helposti nähtävissä pylvään kokonaispituutena.

Huomaa, miten pylvään mitat saa kätevästi yhdellä get_bbox().bounds-toiminnolla.

In [9]:
df1.plot.barh(width = 0.8, legend = 'reverse', stacked = True, zorder = 2)

plt.title('Koulutusjakauma')
plt.xlabel('Lukumäärä')

plt.grid(axis = 'x')
plt.tick_params(axis = 'y', length = 0)

for bar in plt.gca().patches:
    left, bottom, width, height = bar.get_bbox().bounds
    if width > 0:
        label = '{:.0f}'.format(width)
        plt.text(x = left + width / 2, y = bottom + height / 2, s = label, ha = 'center', va = 'center')

Prosentit pinottuna (stacked) pylväskaaviona

Selite ei aina automaattisesti osu mieleiseen paikkaan. Seuraavassa tuunaan selitettä legend-toiminnolla:

  • ncol-parametrilla lisään selitteen sarakkeiden määräksi 4, jolloin saan koulutukset vierekkäin.
  • Muutamien kokeilujen jälkeen päädyin sijoittamaan selitteen sijaintiin (-0.15, -0.2). Koordinaatiston (0, 0) on kaavion vasemmassa alakulmassa ja (1, 1) on kaavion oikeassa yläkulmassa.
Lue lisää legend-tominnosta:

https://matplotlib.org/api/_as_gen/matplotlib.pyplot.legend.html

In [10]:
# Lasken prosenttitaulukon
df2 = pd.crosstab(df['sukup'], df['koulutus'], normalize = 'index') * 100
df2.index = sukup
df2.columns = koulutus
df2
Out[10]:
Peruskoulu 2. aste Korkeakoulu Ylempi korkeakoulu
Mies 35.483871 37.096774 24.193548 3.225806
Nainen 26.315789 36.842105 36.842105 0.000000
In [11]:
df2.plot.barh(width = 0.8, stacked = True, zorder = 2)

plt.title('Koulutusjakauma')
plt.xlabel('% sukupuolesta')

plt.grid(axis = 'x')
plt.tick_params(axis = 'y', length = 0)
plt.xlim(0, 100)

plt.legend(loc = (-0.15, -0.25), ncol = 4)

for bar in plt.gca().patches:
    left, bottom, width, height = bar.get_bbox().bounds
    if width > 0:
        label = '{:.0f} %'.format(width)
        plt.text(x = left + width / 2, y = bottom + height / 2, s = label, ha = 'center', va = 'center')