Pylväskaavioita Pythonilla

Tämän artikkelin pylväskaaviot pohjautuvat artikkelissa Lukumääriä ja prosentteja laskettuihin lukumäärä- ja prosenttitaulukoihin, joiden laskemista en tässä enää uudelleen selitä.

Tämän artikkelin ohjelmakoodin ja tulosteet löydät GitHubista:

https://github.com/taanila/tilastoapu/blob/master/bar.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.

Johdatus kaavioiden laatimiseen

Kun käytän Pandas-ohjelmakirjastoa datojen analysointiin, niin sijoitan datat ja lasketut tulostaulukot dataframe-tyyppisiin muuttujiin. Voit ajatella dataframen taulukoksi. Dataframesta voin laatia kaavioita. Kaavioiden laadinnassa Pandas hyödyntää matplotlib.pyplot-ohjelmakirjastoa. Aluksi tuon tarvittavat ohjelmakirjastot. Vakiintuneen tavan mukaisesti käytän matplotlib.pyplot sijasta lyhennettä plt.

import pandas as pd
import matplotlib.pyplot as plt

Mukavuussyistä määrittelen grafiikat tulostettavaksi automaattisesti Jupyter notebookiin:

%matplotlib inline

Jos jätän %matplotlib inline -määrittelyn pois, niin jokaisen kaavion jälkeen tarvitsen komennon plt.show() kaavion näyttämiseksi Jupyter notebookissa.

Jos kaavioiden oletustyyli ei miellytä minua, niin voin käyttää tyylimäärittelyä. Esimerkiksi:

plt.style.use('seaborn-whitegrid')

Erilaisiin tyyleihin voit tutustua osoitteessa

https://matplotlib.org/devdocs/gallery/style_sheets/style_sheets_reference.html

Kaavion laadin komennolla df.plot. barh() (vaakapylväät), df.plot.bar() (pystypylväät) tai df.plot.hist() (histogrammi). Käytän tietysti df sijasta dataframea, josta haluan kaavion laatia. Jos en halua kaavioon kaikkia dataframen sarakkeita, niin nimeän kuvattavat sarakkeet, esimerkiksi df[’ikä’].plt.hist().

Transpose()-funktiolla voin vaihtaa dataframen rivit ja sarakkeet päittäin kaaviota varten, esimerkiksi df.transpose().plot.bar().

Kaaviotyypin jälkeisten sulkujen sisälle voin kirjoittaa lisämäärittelyitä, esimerkiksi pylväiden värin: df.plot.barh(color=’darkgreen’). Väreihin voit tutustua esimerkiksi osoitteessa

https://stackoverflow.com/questions/22408237/named-colors-in-matplotlib

Kaavion laatimiskomento, esimerkiksi df.plot.bar(), palauttaa Axes-objektin, jonka kautta monia kaavion yksityiskohtia voidaan viimeistellä. Jos tarvitsen Axes-objektia, niin tallennan sen muuttujan arvoksi, esimerkiksi ax=df.plot.bar().

Frekvenssit vaakapylväinä

Dataframe df1 sisältää koulutuksen frekvenssitaulukon:

bar1

Laadin vaakapylväskaavion:

df1['%'].plot.barh(color='C0')
plt.xlabel('prosenttia (n='+str(n)+')')

Outo värimääritys color=’C0′ vaatinee selitystä. Jos teen kuvion vain yhdestä dataframen sarakkeesta (esimerkiksi df1[’%’]) 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.

Funktiolla plt.xlabel() voin lisätä vaaka-akselille otsikon. Esimerkin tapauksessa olen aiemmin koodissa laskenut n-arvon: n=df1[’lkm’].sum(). Hyödynnän n-arvoa merkkijonoksi str(n) muunnettuna.

bar2

Histogrammi

Iän histogrammin voin laskea suoraan alkuperäisestä aineistosta df:

df['ikä'].plot.hist(bins=[20,30,40,50,60,70], 
   edgecolor='grey')
plt.xlabel('Ikä')
plt.ylabel('Lukumäärä')

Histogrammin lisämäärityksenä annan luokkarajat (bins). Käytännössä luokat ovat [20,30); [30,40); [40,40); [50,60); [60,70]. Tavallinen sulkumerkki tarkoittaa, että arvo ei kuulu kyseiseen luokkaan: esimerkiksi arvo 30 ei kuulu luokkaan [20,30).

Jos en määritä edgecolor-arvoa, niin pylväiden väliset rajaviivat eivät erotu, koska oletuksena reunaviiva on samanvärinen kuin pylväs.

bar3

 

Kumulatiivinen histogrammi prosentteina

Kumulatiivisen histogrammin laatimiseksi käytän lisämäärettä cumulative=True. Prosentit (desimaalilukuna) saan lisämääreellä normed=1.

ax=df['ikä'].plot.hist(cumulative=True, 
   bins=[20,30,40,50,60,70], normed=1, edgecolor='grey')
plt.xlabel('Ikä')
plt.ylabel('Prosenttia')

vals = ax.get_yticks()
ax.set_yticklabels(['{:.0f} %'.format(y*100) 
    for y in vals])

Jos käytän histogrammin pystyakselilla prosentteja, niin niiden ulkoasun muotoiluun tarvitsen Axes-objektia. Tämän vuoksi sijoitan kaavion laadinnassa palautuvat objektin muuttujan ax arvoksi.

Pystyakselin prosentit tallennan vals-muuttujan arvoksi ja ax.set_yticklabels()-funktiolla muotoilen prosenttien ulkoasun. Sadalla kertomista (y*100) tarvitsen, koska prosenttiluvut ovat alunperin desimaalimuodossa. En näytä desimaaleja (.0f) ja perään laitan välilyönnin ja %-merkin.

bar4

Monivalintakysymyksen valintojen lukumäärät

Dataframe df5 sisältää monivalintakysymyksen valintojen lukumäärät:

bar5

Laadin vaakapylväskaavion:

df5.plot.barh(color='darkgreen')

 

bar6

Ristiintaulukointi pylväinä

Dataframe df6 sisältää koulutuksen ja sukupuolen ristiintaulukoinnin:

bar7

Laadin ristiintaulukoinnista vaakapylväskaavion:

ax=df6.plot.barh(color=['blue','red'])
plt.xlabel('Lukumäärä')

handles, labels = ax.get_legend_handles_labels()
ax.legend(reversed(handles), reversed(labels), loc='best')

Oletusvärien sijasta määritän, että ensimmäisen arvosarjan (mies) väriksi tulee sininen ja toisen arvosarjan (nainen) väriksi punainen.

Viheliäisenä yksityiskohtana pylväissä naisten pylväs on ylimpänä, mutta miesten väri on selitteessä ylimpänä. Mielestäni on katsojalle selkeämpää, jos järjestys on sama sekä pylväissä että selitteessä. Tämän korjaamiseksi vaihdan selitteiden järjestyksen (reversed(handles), reversed(labels)). Lisäksi määritän selitteen sijainniksi ’best’, joka useimmissa tapauksissa sijoittaa selitteen sopivaan vapaaseen paikkaan.

bar8

Ristiintaulukointi pinottuna 100 % pylväskaaviona

Dataframe df7 sisältää koulutuksen ja sukupuolen välisen ristiintaulukoinnin prosentteina sukupuolesta.

bar9

Pylväskaavioon en halua yhteensä-saraketta. Lisäksi haluan sukupuolet luokiksi (ei arvosarjoiksi). Teen siis kuvion taulukosta df7[[’mies’,’nainen’]].transpose():

ax=df7[['mies','nainen']].transpose().plot.barh(stacked=True)
plt.xlabel('Prosenttia sukupuolesta')

plt.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])

Pinotut pylväät saan lisämääreellä stacked=True.

Pinottu pylväskaavio valtaa koko kuva-alueen, jonka vuoksi selitteelle on vaikeaa löytää sopivaa sijaitia. Siirsin selitteen koko kuvion ulkopuolelle: plt.legend(loc=’upper center’, bbox_to_anchor=(0.5, 1.2), ncol=4). Bbox_to_anchor määrittää selitelaatikon sijainnin vaakasuunnassa (0.5) ja pystysuunnassa (1.2). Sopivien arvojen löytämiseksi jouduin suortittamaan useita kokeiluita. Ncol määrittää selitteen sarakkeiden määrän. Määrittämällä sarakkeiden määräksi arvosarjojen (koulutusten) lukumäärän (4) saan kaikki selitteen arvot vierekkäin.

Akselin prosenttilukujen ulkoasun muotoilen jo aiemmin esiin tulleella tavalla.

bar10

Monivalinnan vaihtoehtojen lukumäärät taustamuuttujan mukaan

Dataframe df9 sisältää monivalintakysymyksen valintojen lukumääriä sukupuolen mukaan ryhmiteltyinä.

bar11

Laadin pylväskaavion:

df9.plot.bar()
plt.ylabel('lukumäärä')
plt.xticks(rotation='horizontal')

Käännän luokka-akselin otsikot vaaka-asentoon (plt.xtics(rotation=’horizontal’))

bar12

Dataframe df10 sisältää äskeisen tiedon prosentteina sukupuolesta.

bar13

Laadin pylväskaavion:

ax=df10.plot.bar()
plt.ylabel('prosenttia sukupuolesta')
plt.xticks(rotation='horizontal')

plt.legend(loc='upper center', bbox_to_anchor=
   (0.5, 1.2), ncol=4)

vals = ax.get_yticks()
ax.set_yticklabels(['{:.0f} %'.format(x*100) 
   for x in vals])

Jo aiemmin opitulla tavalla siirrän selitteen kaavion ulkopuolelle (plt.legend(…) ja muotoilen prosenttilukujen ulkoasun (ax.set_yticklabels(…).

bar14

Vaihtoehtoisesti voin toteuttaa kaavion myös seuraavasti:

ax=df10.transpose().plot.bar()
plt.ylabel('prosenttia sukupuolesta')
plt.xticks(rotation='horizontal')

vals = ax.get_yticks()
ax.set_yticklabels(['{:.0f}%'.format(x*100) 
   for x in vals])

bar19

Mielipiteiden jakaumat

Dataframe df11 sisältää koontitaulukon mielipiteiden lukumääristä eri asioiden kohdalla.

bar15

Laadin pinotun vaakapylväskaavion:

df11.transpose().plot.barh(stacked=True, color=['darkred',
   'red','grey','blue','darkblue'])
plt.xlabel('Lukumäärä')
plt.legend(bbox_to_anchor=(1, 0.5))

bar17

Voin myös laatia pylväskaavion jokaisen asian osalta:

df11.plot.bar(subplots=True, layout=(1,5), sharex=True, 
   sharey=True, figsize=(12,2), color='maroon', legend=False)

Kuvion laadinnassa syntyy kaksi objektia: Figure-objekti ja Axes-objekti. Axes-objekti sisältää yksittäisen kaavion sisällön. Figure-objekti sisältää Axes-objektin, mutta voi sisältää myös useita Axes-objekteja.

  • Lisämääreellä subplots=True määritän, että Figure-objektiin sijoitetaan useita kaavioita.
  • Lisämääreellä layout=(1,5) määritän, että kaaviot sijoitetaan 1 riville, 5 rinnakkain.
  • Lisämääreillä sharex=True, sharey=True määritän, että kaikilla kaavioilla on yhteiset akselit.
  • Lisämääreellä figsize=(12,2) määritän kuvion koon.
  • Lisämääreellä color=’maroon’ määritän pylväiden väriksi maroon.
  • Lisämääreellä legend=False jätän kaavioista selitteet pois.

bar16

 

Mielipiteiden keskiarvot pylväinä

Dataframe df12 sisältää mielipiteiden keskiarvoja. Laadin pylväskaavion:

ax=df12.plot.barh(color='C0')
plt.xlim(1,5)
plt.title('Tyytyväisyyskeskiarvoja')

ax.set(xticks=[1,2,3,4,5]) 
ax.set_xticklabels(['erittäin tyytymätön','tyytymätön',
   'neutraali', 'tyytyväinen','erittäin tyytyväinen'], rotation=30)

Lisäasetuksella plt.xlim(1,5) asetan arvoakselin pienimmän ja suurimman arvon (tässä mielipideasteikko oli 1 – 5).

Lisään kaavioon pääotsikon (plt.title(’Tyytyväisyyskeskiarvoja’)).

Määritän akselin jaotuksen (ax.set(xticks=[1,2,3,4,5])) ja korvaan akselin numerot mielipiteiden nimillä (ax.set_xticklabels(…). Akselin otsikot käännän 30 asteen kulmaan.

bar18

 

 

Mainokset