Avainsana-arkisto: Matplotlib

Kuviot ja kaaviot Pythonilla

Päivitetty 6.5.2020

Kuvioiden ja kaavioiden peruskirjasto on matplotlib.pyplot. Vakiintuneen tavan mukaan käytän kirjastosta plt-lyhennettä:

import matplotlib.pyplot as plt
%matplotlib inline

Jupyteria käyttäessäni lisään rivin %matplotlib inline, jonka ansiosta laaditut kaaviot tulostuvat Jupyter-notebookiin ilman erillistä tulostuskomentoa (plt.show()).

Kaavioiden luomiseen ja muotoiluun on erilaisia tapoja. Tarkastelen seuraavassa kahta tapaa, jotka sopivat kaavioiden luomiseen pandas dataframesta. Ensimmäinen tapa on yksinkertaisempi, mutta toinenkin tapa kannattaa opetella.

Tapa1

Pystypylväskaavion (bar) voin luoda dataframesta df:

df.plot.bar()

Muita paljon käytettyjä kaaviolajeja ovat barh (vaakapylväs), pie (piirakka), hist (histogrammi), line (viiva), scatter (piste) ja box (ruutu- ja janakaavio).

Kaavion luonnin yhteydessä voin tehdä kaavioon liittyviä asetuksia lisäparametreilla, esimerkiksi:

  • legend arvoksi voin asettaa False tai ’reverse’. Oletusarvona on True. legend=’reverse’ kääntää selitteen arvojen järjestyksen.
  • width arvoksi voin asettaa luvun väliltä 0-1. Oletusarvona on 0.5. Mitä suurempi arvo, sitä lähempänä toisiaan ja sitä paksumpia pylväät ovat. Jos width=1, niin pylväät ovat kiinni toisissaan.
  • rot kääntää luokka-akselin jakoviivojen (ticks) nimiöt annetun asteluvun mukaan. Esimerkiksi rot=45 kääntää nimiöt 45 asteen kulmaan.
  • stacked=True luo pinotun pylväskaavion.
  • color määrittää pylväiden värin. Esimerkiksi color=’C0′ asettaa väriksi käytössä olevan väripaletin ensimmäisen värin. Voin käyttää värien nimiä tai koodeja (katso https://htmlcolorcodes.com/).
  • style määrittää viivakaavion viivan ja havaintopisteiden tyylin. Esimerkiksi style=’ro-’ tekee punaisen (r) viivan (), jossa havaintopisteet ovat ympyröitä (o). Lisätietoa https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.axes.Axes.plot.html
  • edgecolor määrittää pylväiden reunan värin.

Lisätietoa ominaisuuksista, jotka voit asettaa lisäparametreilla:

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html

Lisäparametrien lisäksi voin muotoilla kaaviota matplotlib.pyplot-funktioilla. Esimerkiksi plt.title(’Kaavion otsikko’). Funktiot kohdistuvat aina viimeksi luotuun kaavioon. Funktiot tarjoavat Matlab-matematiikkaohjelmiston käyttäjille tutun käyttöliittymän kaavioihin. Lisätietoa:

https://matplotlib.org/3.1.0/tutorials/introductory/pyplot.html

Esimerkeissäni en yleensä käytä matplotlib.pyplot-funktioita, vaan muotoilen kaaviot kaavion luonnin palauttaman olion kautta. Tätä varten sijoitan kaavion luonnin palauttaman olion muuttujan arvoksi:

ax = df.plot.bar()

Tämän Axes-luokan olion kautta pääsen käsiksi kaavion elementteihin, esimerkiksi:

  • ax.set_title(’Kaavion otsikko’)
  • ax.set_xlabel(’x-akselin otsikko’)
  • ax.set_ylabel(’y-akselin otsikko’)

Koodiesimerkeistäni löydät malleja useimmin tarvittavista muotoilu-komennoista. Lisätietoa Axes-luokan oliosta:

https://matplotlib.org/3.1.0/api/axes_api.html

Tapa 2

Toinen tapa kaavion luontiin vaatii hieman enemmän koodia (vertaa ax = df.plot.bar()):

fig, ax = plt.subplots()
df.plot.bar(ax = ax)

Tämän hankalamman tuntuisen tavan perusteluksi kannattaa perehtyä kaavion luonnin yhteydessä syntyviin olioihin.

Kaavio itsessään on Axes-luokan olio.

Kaavio sijoittuu aina kuvion sisään. Kuvio puolestaan on Figure-luokan olio. Jos kuvioon pitää viitata, niin kannattaa käyttää kaavion luontiin plt.subplots()-funktiota, joka palauttaa sekä kuvion että ”tyhjän” kaavion. plt.subplots()-funktion palauttaman kuvion ja kaavion sijoitan muuttujien arvoiksi (esimerkiksi fig, ax = plt.subplots()).

Kaavion sisällön määritän plot-funktiolla. Lisäparametrilla ax kerron, minkä nimiseen Axes-luokan olioon kaavio piirretään (esimerkiksi df.plt.bar(ax = ax)) .

Oliohierarkian hahmottamista auttaa oheinen osoitteesta https://realpython.com/python-matplotlib-guide/ lainattu havainnollistus:

matplotlib1

Uloimpana on kuvio (Figure) ja sen sisällä kaavio (Axes). Kaavion sisällä on useita olioita, esimerkiksi kaavion otsikko, akselit, akselien otsikot, jne.

Ensimmäinen, yksinkertaisempi tapa kaavion luontiin, tuottaa saman olioiden hierarkian, mutta ei palauta Figure-luokan olioita siten että voisin sijoittaa sen suoraan muuttujan arvoksi. Saan tosin selville Figure-luokan olion jälkikäteen funktiolla plt.gcf(). Figure-luokan olioita tarvitsen ainakin seuraavissa tilanteissa:

  • Kuvion tallentaminen: jos haluan tallentaa kaavion, niin minun pitää tallentaa kuvio.
  • Useita kaavioita samassa kuviossa: voin sijoittaa saman kuvion sisään useita kaavioita vierekkäin ja allekkain.

Seuraavaksi

Pääset hyvään alkuun tutustumalla esimerkkikokoelmani koodeihin:

https://nbviewer.jupyter.org/github/taanila/kaaviot/tree/master/

Samalla kannattaa alkaa kokeilla omien kaavioiden luomista ja muotoilua.

Perusasiat ja useimmin tarvitut muotoilukomennot oppii nopeasti ulkoa, mutta harvemmin tarvittavia temppuja voi selvitellä googlaamalla. Hämmennystä aiheuttaa netistä löytyvien esimerkkien moninaisuus ja vaihtelevat käytännöt. Monissa koodeissa käytetään sekaisin Axes-olion kautta tehtyjä muotoiluja ja matplotlib.pyplot-funktioilla tehtyjä muotoiluja. Kaavioiden luonnissa käytetään tässä esitettyjen tapojen lisäksi myös kaavion luomista suoraan Axes-olioon (ax.bar()).

Artikkelista jupyter: taulukot ja kaaviot raporttiin selviää, miten tallennat ja siirrät kaavioita raporttiin.

Jupyter: Taulukot ja kaaviot raporttiin

Päivitetty 1.7.2019.

Tässä artikkelissa kerron, miten voit siirtää Pythonilla Jupyter-notebookiin lasketut taulukot ja kaaviot Word-raporttiin.

Taulukot

Jupyter-notebook toimii oletusselaimessa. Laskettujen (html-muotoisten) taulukoiden ulkoasu riippuu käytetystä selaimesta.

Ennen Wordiin kopiointia taulukon lukumuotoilut kannattaa laittaa kuntoon (desimaalien määrä, mahdolliset prosenttimuotoilut).

Valitse taulukko ja kopioi-liitä se Word-dokumenttiin. Näin saat Word-taulukon, jonka ulkoasua voit  muuttaa Wordin taulukkotyökaluilla:

  • Valitse solu taulukon alueelta.
  • Valitse Table Tools – Design -työkaluista haluamasi tyyli.

taulukko

Vieressä on kuvakaappaus Jupyter-notebookista.

Kopioi-liitä liittää sen Wordiin karun näköisenä taulukkona (keskimmäinen taulukko).

Tämän jälkeen valitsin Table Tools – Design (Taulukkotyökalut – Rakenne) -työkaluista mieleiseni tyylin. Tästä voisin vielä jatkaa esimerkiksi tasaamalla koulutuksia kuvaavat tekstit vasempaan reunaan, korvaamalla desimaalipisteet pilkuilla ja vaihtamalla fonttia.

Taulukon sisältöä, tekstejä ja lukuja, voin tarvittaessa muokata.

Kaaviot

Samassa Jupyter-notebookin solussa luodun kaavion voit tallentaa komennolla

plt.savefig('nimi.png', bbox_inches='tight')

Tärkeitä huomioita:

  • Jos tiedostonimeen ei liity polkua, niin kaavio tallentuu samaan kansioon kuin Jypyter-notebook.
  • Ilman bbox_inches=’tight’-parametria kaavion reunoilla olevat tekstit jäävät usein osittain tallennetun kaavion ulkopuolelle.
  • Tiedostoformaatti määräytyy tiedostonimen tarkentimesta (edellä .png).

Järjestelmäsi tukemat kuvaformaatit saat selville komennolla

plt.figure().canvas.get_supported_filetypes()

Tuloksena saat listauksen kuvaformaateista, esimerkiksi:

{'ps': 'Postscript',
 'eps': 'Encapsulated Postscript',
 'pdf': 'Portable Document Format',
 'pgf': 'PGF code for LaTeX',
 'png': 'Portable Network Graphics',
 'raw': 'Raw RGBA bitmap',
 'rgba': 'Raw RGBA bitmap',
 'svg': 'Scalable Vector Graphics',
 'svgz': 'Scalable Vector Graphics',
 'jpg': 'Joint Photographic Experts Group',
 'jpeg': 'Joint Photographic Experts Group',
 'tif': 'Tagged Image File Format',
 'tiff': 'Tagged Image File Format'}

Kaavion voit myös kopioida Jupyter-notebookista suoraan Wordiin kopioi-liitä-toiminnolla (löydät kopioi-komennon napsauttamalla hiiren kakkospainiketta kaavion päällä).

Kaavioiden fontti

Kaavion  fonttia et voi vaihtaa enää raportissa. Pythonilla fontin vaihto onnistuu, mutta se pitää tehdä ennen kaavioiden luontia. Voit antaa seuraavat komennot heti ohjelmakirjaston tuonnin (import  matplotlib.pyplot as plt) jälkeen. Komennot vaikuttavat näin kaikkiin samassa notebookissa laadittaviin kaavioihin.

plt.rcParams['font.sans-serif'] = "Arial"
plt.rcParams['font.family'] = "sans-serif"

Ensimmäisellä komennolla määrität fonttilajin. Yllä on määritelty ’sans-serif’-fontiksi ’Arial’. Sen jälkeen on määritelty, että kaavioissa käytetään ’sans-serif’-fonttia. ’sans-serif’ tarkoittaa fontteja ilman pääteviivaa (groteski). Voit myös määrittää käytettäväksi ’serif’-fontin eli pääteviivallisen fontin.

Voit halutessasi säätää myös fonttikoot, esimerkiksi:

plt.rcParams['font.size'] = 12 #oletusfontti
plt.rcParams['axes.titlesize'] = 14 #kaavion otsikko
plt.rcParams['axes.labelsize'] = 12 #akselien otsikot
plt.rcParams['xtick.labelsize'] = 10 #x-akselin jaotuksen nimiöt
plt.rcParams['ytick.labelsize'] = 10 #y-akselin jaotuksen nimiöt
plt.rcParams['legend.fontsize'] = 12 #selite
plt.rcParams['figure.titlesize'] = 14 #kuvion otsikko

 

Kuviot Pythonilla 1

Tässä artikkelissa perehdyn kuvioiden laatimisen (pylväskuviot) alkeisiin. Tämän artikkelin ohjelmakoodin ja tulosteet löydät GitHubista:

https://github.com/taanila/tilastoapu/blob/master/kuviot1.ipynb

Jos kopioit koodia itsellesi, niin kannattaa käyttää GitHubia. Tästä artikkelista kopioidut koodit eivät välttämättä toimi oikein.

Oletan, että lukijalla on asennettuna Anaconda ja sen mukana tuleva Jupyter notebook.

Pandas-kirjaston dataframe-tietorakenne on keskeinen datojen analysoinnissa: avaan analysoitavan datan dataframeen ja lasketut tulostaulukot ovat enimmäkseen dataframe-muotoisia. Tässä ja muissa kuvioita käsittelevissä artikkeleissani tarkastelen kuvioiden luomista dataframe-muotoisesta datasta.

Matplotlib

Kuvioiden luomisen kulmakivi on matplotlib-kirjasto. Yleensä riittää, että otan käyttöön matplotlib.pyplot-kirjaston (ja tietenkin pandas-kirjaston):

import pandas as pd
import matplotlib.pyplot as plt

Jos käytän Jupyter-notebookia, niin suoritan myös komennon:

%matplotlib inline

Tämän ansiosta kuviot tulostuvat Jupyter-notebookiin ilman erillistä tulostuskomentoa.

Yksinkertainen pylväskuvio

Ensimmäisenä esimerkkinä kuvaan yksinkertaisen myyntilukuja sisältävän taulukon pylväskuviona. Luon myynnit-nimisen dataframen, jossa on neljän eri alueen myyntiluvut:

raw_data={'Alue': ['Helsinki', 'Turku', 'Tampere', 'Oulu'],
    'Myynti': [1321847, 852669, 1032199, 568230]}
 myynnit = pd.DataFrame(raw_data)
 myynnit.index = myynnit['Alue']
 myynnit

Yksinkertaisimmillaan luon vaakapylväskuvion myynnit-dataframen sisällöstä komennolla:

myynnit.plot.barh()

kuviot1

Kuvion muotoilu

Kuvion ulkoasuun vaikutan nopeimmin vaihtamalla tyyliä. Tarjolla olevien tyylien nimet näen komennolla plt.style.available. Tyylin vaihto esimerkiksi tyyliin ’ggplot’ onnistuu komennolla plt.style.use(’ggplot’).

Jos haluan räätälöidä kuviota yksilöidymmin, niin tallennan kuvion luonnin palauttaman Axes-objektin muuttujan arvoksi. Axes-objektin kautta voin muotoilla kuviota monin tavoin. Seuraavassa

  • otan käyttöön ’ggplot’-tyylin
  • järjestän myynnit suuruusjärjestykseen ennen plot.barh-komentoa
  • lisään kuvioon pääotsikon (title) ja poistan selitteen (legend) näkyviltä
  • tallennan kuvion luonnin palauttaman Axes-objektin ax-nimiseen muuttujaan
  • määritänn otsikon x-akselille (xlabel) ja tyhjennän y-akselin otsikon (ylabel)
  • haen x-akselin  ticksit ja muotoilen ne miljooniksi euroiksi yhdellä desimaalilla.
plt.style.use('ggplot')
ax=myynnit.sort_values(by='Myynti').plot.barh
   (title='Myynti vuonna 2017', legend=False)
ax.set(xlabel='Miljoonaa euroa', ylabel='')
vals = ax.get_xticks()
ax.set_xticklabels(['{:.1f}'.format(x/1000000) for x in vals])

kuviot2

Seuraavaksi lisään dataframeen prosenttiosuudet kokonaismyynnistä ja esitän prosenttiosuudet pylväskuviona:

n=myynnit['Myynti'].sum()
myynnit['%'] = myynnit['Myynti'] / n * 100
ax=myynnit.sort_values(by='%')['%'].plot.barh
   (title='Osuus kokonaismyynnistä vuonna 2017', legend=False, 
   width=0.7, color='C0')
ax.set(xlabel='%', ylabel='')
vals = ax.get_xticks()
ax.set_xticklabels(['{:.0f} %'.format(x) for x in vals])

kuviot3

Lisämääritys width=0.7 vaikuttaa pylväiden leveyteen ja samalla pylväiden väliseen tyhjään tilaan. Oletus on width=0.5. Mitä suurempi arvo sitä lähempänä pylväät ovat toisiaan. Jos width=1, niin pylväät ovat kiinni toisissaan.

Outo värimääritys color=’C0′ vaatinee selitystä. Jos teen kuvion vain yhdestä dataframen sarakkeesta (esimerkiksi myynnit[’%’]) niin kyseessä ei ole enää dataframe, vaan series. Tällöin jokainen pylväs esitetään omalla värillään (vanhemmissa versioissa tällaista ominaisuutta ei ollut). Verkkokeskusteluiden perusteella on epäselvää, onko kyseessä virhe vai tarkoituksellinen ominaisuus. Esimerkin tapauksessa värit eivät mielestäni tuo lisäarvoa. Värimääritys color=’C0′ pakottaa pylväisiin käytössä olevan väripaletin ensimmäisen värin. Vastaavasti ’C1’ pakottaisi käytössä olevan väripaletin toisen värin jne.

Vierekkäset pylväät

Seuraavaksi luon dataframen, jossa on eri alueiden myynnit vuoden 2017 lisäksi myös vuodelta 2016 ja esitän molempien vuosien myynnit vierekkäisinä pylväinä:

raw_data={'Alue': ['Helsinki', 'Turku', 'Tampere', 'Oulu'], 
   'Myynti 2017': [1321847, 852669, 1032199, 568230],
   'Myynti 2016': [1203434, 923450, 1023563, 542399]}
myynnit2 = pd.DataFrame(raw_data)
myynnit2.index = myynnit2['Alue']
ax=myynnit2.plot.bar(figsize=(10,6), title='Myynti vuosina 
   2016 ja 2017', rot=0, width=0.7)
ax.set(xlabel='Alue', ylabel='Euroa')

kuviot4

Lisämääreenä määritin kuvion koon (figsize=(10,6)) ja käänsin luokka-akselin tekstit vaakaasentoon (rot=0).

Pinotut pylväät

Lisämääreellä stacked=True voin laatia pinotun pylväskaavion

ax=myynnit2.plot.bar(figsize=(10,6), title='Myynti 
   vuosina 2016 ja 2017', rot=0, width=0.7, legend=
   'reverse', stacked=True)
ax.set(xlabel='Alue', ylabel='Euroa')

kuviot5

Lisämääreellä legend=’reverse’ vaihdoin selitteen järjestyksen, jotta se vastaisi pinottujen pylväiden järjestystä.

Pinotut 100 % pylväät

Katsotaan vielä, miten laadin ristiintaulukoinnista 100 % pylväskuvion. Avaan datan, jossa on tietoja erään yrityksen työntekijöistä (muiden muassa sukupuoli ja koulutus) ja ristiintaulukoin koulutuksen ja sukupuolen. Kuvioksi sopii 100 % pinottu pylväskuvio:

df1 = pd.crosstab(df['koulutus'], df['sukupuoli'], margins = 
   True, normalize = 'columns')
df1.index = ['peruskoulu','2.aste','korkeakoulu',
   'ylempi korkeakoulu']
df1.columns = ['mies','nainen','yhteensä']

ax=df1[['mies','nainen']].transpose().plot.
   barh(stacked=True)
ax.set_xlabel('Prosenttia sukupuolesta')
ax.legend(loc='upper center', bbox_to_anchor=
   (0.5, 1.2), ncol=4)
vals = ax.get_xticks()
ax.set_xticklabels(['{:.0f} %'.format(x*100) 
   for x in vals])

kuviot6

Laadin ristiintaulukoinnin käyttämällä sukupuolta sarakemuuttujana (monilla on tapana ottaa selittävä muuttuja sarakemuuttujaksi). Tästä en kuitenkaan saa laadittua halutunlaista 100 % pylväskuviota ilman T-toimintoa. T vaihtaa rivi ja sarakemuuttujat keskenään kuvion laadintaa varten.

Selitteen (legend) sijoittelukomento on taas melko esoteerisen näköinen. Pääset perille määreen bbox_to_anchor=(0.5, 1.2) tarkoituksesta kokeilemalla sulkeiden sisällä erilaisia lukuarvoja. Määre ncol=4 määrittää selitteen leveydeksi 4 saraketta (jokainen sarake sisältää yhden värin selitteen), joten värien selitykset sijoittuvat vierekkäin.

Figure-objekti

Plot-toiminnon myötä taustalla luodaan figure-olio ja tämän sisälle axes-olio. Voin tallentaa kuvion talletamalla figure-olion:

plt.gcf().savefig('kuva.png')
plt.tight_layout()

gcf()-funktio palauttaa viimeksi luodun figure-olion (get current figure). Tallennusformaatti määräytyy tiedostonimen tarkentimen (esim. png) perusteella. Matplotlib tukee yleisimpiä kuvaformaatteja.

plt.tight_layout() on tarpeen, jos tallennettu kuva ilman sitä näyttää reunoista liian ahtaalta.

Yhden figure-objektin sisälle on mahdollista luoda useita vierekkäin ja/tai allekkain sijoitettuja axes-objekteja. Yhden figure-objektin sisällä voi siis olla esimerkiksi useita pylväskuvioita.

Dataframe.plot

Tarkkaan ottaen hyödynsin tämän artikkelin kuvioiden luonnissa pandas-kirjaston dataframe.plot-toimintoa. Tämä toiminto kutsuu matplotlib.pyplot-kirjaston plot toimintoa. Lue lisää https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.plot.html