Aihearkisto: Yleinen

Github – esimerkit omalle koneelle

Olen tallentanut esimerkkikoodit Jypyter-notebook -muotoisina githubiin. Esimerkit aukeavat selaimeen, josta voit halutessasi kopioida koodin pätkiä (solu kerrallaan) itsellesi. Kokonaisen Jupyter-notebookin tallentaminen omalle koneelle ei välttämättä onnistu (riippuu selaimesta ja monesta muustakin asiasta). Voit kuitenkin kloonata kaikki esimerkkini kerralla omalle koneellesi seuraavasti:

  • Siirry osoitteeseen https://github.com/taanila/tilastoapu
  • Napsauta vihreää Clone or download -painiketta ja valitse Download ZIP
  • Tallenna omaan kotihakemistoosi (sinne, missä säilytät Python-koodejasi)
  • Pura (Extract all) tallentamasi paketti (tilastoapu-master)

Kaikki esimerkit ovat tämän jälkeen avattavissa ja käytettävissä Jupyterissä.

Päivitän esimerkkejä aika ajoin. Tuoreimmat versiot saat käyttöösi suorittamalla yllä kuvatun kloonauksen uudelleen.

Kloonaa myös esimerkkikokoelmani kaavioista http://github.com/taanila/kaaviot

 

Data-analytiikka Pythonilla

Päivitetty 15.11.2019.

Data-analytiikka antaa vastauksia kysymyksiin

Data-analytiikka on tavoitteellista toimintaa: tavoitteena on vastata kysymyksiin. Data-analytiikan avulla vastataan monenlaisiin kysymyksiin:

  • Minkälainen ikäjakauma asiakkaillamme on?
  • Mihin toimintamme osa-alueisiin asiakkaamme ovat tyytymättömiä?
  • Onko asiakkaan iällä yhteyttä asiakastyytyväisyyteen?
  • Miten yrityksen työilmapiiri on muuttunut viimevuodesta?
  • Ketkä asiakkaistamme ovat vaarassa siirtyä kilpailijalle?
  • Keille tuotteen markkinointikampanja kannattaa suunnata?
  • Mikä mainosvaihtoehdoista tehoaa parhaiten kohderyhmään?
  • Mitä oheistuotteita verkkokaupasta ostaneella kannattaa tarjota?
  • Mikä on tuotteen ennustettu kysyntä ensi kuussa?
  • Liittyykö vakuutuskorvaushakemukseen vakuutuspetos?
  • Millä todennäköisyydellä laina-asiakas ei pysty maksamaan lainaansa takaisin?

Data

Tavoitteiden (kysymykset, joihin halutaan vastata) asettamisen jälkeen pitää selvittää minkälaista dataa tarvitaan. Data voi olla esimerkiksi:

  • Yrityksen tietokannoista löytyvää dataa (esimerkiksi CRM- ja ERP-järjestelmistä).
  • Erilaisten tiedontuottajien tarjoamaa ilmaista tai maksullista dataa.
  • Varta vasten kyselytutkimuksella tai kokeellisella tutkimuksella kerättyä dataa.
  • Erilaisten sensorien/mittalaitteiden mittaamaa dataa.

Blogissani rajoitun rakenteelliseen eli strukturoituun dataan. Rakenteellinen data on sellaista, joka voidaan tallentaa taulukkomuotoon. Yleisiä data-analytiikkaan sopivia tiedostomuotoja ovat pilkkueroteltu tekstimuoto (.csv) ja Excel-muoto (.xls tai .xlsx). Tietokannoista data haetaan kyselyiden (sql kyselykieli) avulla. Nettikyselyohjelmista datan saa yleensä ulos pilkkuerotellussa tekstimuodossa tai Excel-muodossa.

Kun sopiva data on olemassa, niin datasta saadaan vastauksia kysymyksiin seuraavien vaiheiden kautta:

  • Datan valmistelu
  • Kuvaileva analytiikka
  • Selittävä analytiikka; selittävään analytiikkaan liittyy usein tilastollisen merkitsevyyden testaaminen: tilastollinen merkitsevyys kertoo, millä varmuudella otoksessa havaittuja eroja ja riippuvuuksia voidaan yleistää isompaan perusjoukkoon, josta otos on otettu.
  • Ennakoiva analytiikka; tämä tarkoittaa yleensä koneoppimisen mallien hyödyntämistä.

Datan valmistelu

Datan valmistelulla tarkoitan datojen yhdistelyä, dataan tutustumista, datan siivoamista ja datan muunnoksia.

Datan valmistelu voi olla data-analytiikan aikaa vievin vaihe. Ensimmäiseksi kannattaa varmistaa datan taulukkomuotoisuus: muuttujien nimet / kenttien nimet / sarakeotsikot ovat ensimmäisellä rivillä, datassa ei ole tarpeettomia tyhjiä rivejä tai sarakkeita, kuhunkin tilastoyksikköön/havaintoyksikköön liittyvät tiedot ovat yhdellä rivillä. Datan valmistelu voi sisältää muiden muassa seuraavia:

  • Eri lähteistä peräisin olevien datojen yhdistely.
  • Muuttujien uudelleen nimeäminen: jatkotoimet sujuvat sutjakkaammin, jos nimet ovat lyhyitä ja helposti tunnistettavia.
  • Desimaalipilkkujen tarkistaminen: vaikka Suomessa desimaalipilkkuna käytetään pilkkua, niin Pythonissa täytyy käyttää pistettä.
  • Päivämäärien muuntaminen päivämääriksi tunnistettavaan muotoon.
  • Mittayksiköiden tarkistaminen ja tarvittavien muunnosten tekeminen.
  • Puuttuvien arvojen käsittely: poistetaanko puuttuvia arvoja sisältävät rivit, korvataanko puuttuvat arvot jollain, miten puuttuvia arvoja merkitään?
  • Uusien muuttujien laskeminen: esimerkiksi summamuuttuja useasta mielipidemuuttujasta, tilauksen hinta tilausmäärän ja yksikköhinnan avulla jne.
  • Arvojen luokittelu ja uudelleenkoodaaminen: esimerkiksi ikäluokat iän arvoista.

Kuvaileva analytiikka

Datan kuvailu voi sisältää seuraavia:

  • Lukumäärä- ja prosenttiyhteenvetojen laskeminen (frekvenssitaulukot).
  • Tilastollisten tunnuslukujen laskeminen (keskiarvo, keskihajonta, viiden luvun yhteenveto).
  • Prosenttimuutosten laskeminen aikasarjoille.
  • Aikasarjojen tarkastelu viivakaavioina.
  • Liukuvien keskiarvojen esittäminen aikasarjojen yhteydessä.

Kuvailun tuloksia kannattaa visualisoida ja havainnollistaa hyvin viimeistellyillä taulukoilla ja kaavioilla.

Selittävä analytiikka ja tilastollinen merkitsevyys

Selittävä analytiikka voi sisältää seuraavia:

  • Tilastollisten tunnuslukujen vertailua eri ryhmissä.
  • Kategoristen muuttujien riippuvuuden tarkastelua ristiintaulukoimalla.
  • Määrällisten muuttujien välisten korrelaatioiden tarkastelua.
  • Havaittujen erojen ja riippuvuuksien tilastollisen merkitsevyyden tarkastelua.

Jos käytetty data on otos isommasta perusjoukosta, niin tulokset kuvaavat otosta. Jos tarkoituksena on arvioida koko perusjoukkoa, niin otoksessa havaittujen erojen ja riippuvuuksien tilastollinen merkitsevyys kertoo, millä varmuudella eroja ja riippuvuuksia voidaan yleistää otoksesta perusjoukkoon.

Ennakoiva analytiikka ja koneoppiminen

Koneoppimisen malleilla voidaan luokitella (asiakkaat luottoriski-asiakkaisiin ja muihin, vakuutuskorvaushakemukset selviin tapauksiin ja petokselta haiskahtaviin, sähköpostiviestit roskapostiin ja kunnollisiin viesteihin jne.) ja ennakoida määrällisen muuttujan arvoja (käytetyn auton hinta, tuleva kysyntä jne.). Koneoppiminen perustuu siihen, että kone oppii käytettävän mallin parametrit olemassa olevasta datasta ja tämän jälkeen mallia voidaan soveltaa uuteen dataan.

Koneoppimisalgoritmit voidaan luokitella  seuraavasti (suomennokset eivät ole vakiintuneita):

  • Supervised learning (ohjattu oppiminen): Algoritmi opetetaan opetusdatalla (training data). Esimerkiksi roskapostisuodatin opetetaan sähköpostidatalla, jossa on erilaisia tietoja kustakin sähköpostiviestistä sekä tieto siitä oliko sähköpostiviesti roskapostia. Tämän datan perusteella muodostuu malli, jota käyttäen tulevista sähköpostiviesteistä voidaan tunnistaa roskapostiviestit.
  • Unsupervised learning (ohjaamaton oppiminen): Esimerkiksi asiakkaiden jakaminen asiakassegmentteihin.
  • Reinforcement learning (vahvistusoppiminen): Algoritmi suorittaa toimia ja saa niistä palautetta palkkioiden ja rangaistuksen muodoissa. Algoritmi oppii saamistaan palkkioista ja rangaistuksista. Vahvistettua oppimista käytetään esimerkiksi robotiikassa.

Seuraavassa jaotellaan ohjattu ja ohjaamaton oppiminen edelleen alatyyppeihin:

kone1

Ohjattu oppiminen

Label tarkoittaa ennakoitavaa asiaa (selitettävää muuttujaa).

Diskreetti label

Jos ennakoitava asia on diskreetti (epäjatkuva), niin kyseeseen tulevat luokittelua suorittavat algoritmit, esimerkiksi logistinen regressio tai päätöspuut.

Esimerkkejä, joissa on diskreetti label:

  • Roskapostisuodatin: Label on tieto siitä, onko sähköpostiviesti roskapostia vai ei?
  • Lääketieteellinen diagnoosi: Label on tieto siitä, onko tutkitulla potilaalla tietty sairaus vai ei?
  • Vakuutuspetosten tunnistaminen: Label on tieto siitä, liittyykö korvaushakemukseen petos vai ei?

Jatkuva label

Jos ennakoitava asia on jatkuva, niin kyseeseen tulevat regressiomallit ja aikasarjaennustamisen menetelmät. Esimerkkejä, joissa on jatkuva label:

  • Vanhan osakehuoneiston hinnan arviointi: Label on asunnon hinta.
  • Kysynnän ennustaminen aikaisemman kysynnän perusteella: Label on kysyntä.

Ohjaamaton oppiminen

Ohjaamattomassa oppimisessa ei ole labelia (selitettävää/ennakoitavaa muuttujaa). Ohjaamattoman oppimisen algoritmi muodostaa mallin suoraan datasta. Esimerkkinä asiakassegmenttien määrittäminen asiakasdatan pohjalta. Paljon käytetty algoritmi on k-means clustering.

Jos datassa on paljon muuttujia, jotka mittaavat osittain samoja asioita, niin dimensionality reduction -tyyppisillä algoritmeilla voidaan pienentää muuttujien määrää yhdistämällä niitä kimpuiksi. Tunnetuin algoritmi tähän tarkoitukseen on pääkomponenttianalyysi.

Data-analytiikkaa Pythonilla

Jos aiot käyttää Pythonia data-analytiikassa, niin aloita asentamalla Anaconda.

 

Olennaiset taidot kuvailevaan analyysiin

Ohjelmakoodin ja tulosteet löydät GitHubista:

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

Tulosten siirtäminen raporttiin

Taulukot

Taulukoiden ulkoasu riippuu jonkin verran käyttämästäsi selaimesta (Jupyter notebook toimii oletusselaimessasi). Tämän artikkelin esimerkeissä olen käyttänyt Microsoftin Edge-selainta.

Julkaistavaksi tarkoitetun taulukon voin valita, kopioida ja liittää (copy – paste) esimerkiksi PowerPointiin, Wordiin tai Exceliin. Liitetty taulukko on myös taulukko ja voin muokata sitä taulukkotyökaluilla.

Python käyttää desimaalipilkkuna pistettä. PowerPointissa ja Wordissä voin helposti korvata pisteet pilkulla. Excelissä osa desimaalipisteellä varustetuista luvuista muuttuu päivämääriksi. Tämän voin estää kierrättämällä taulukko Wordin kautta ja korvaamalla pisteet pilkuilla Wordissä.

Voin myös tallentaa taulukot csv-tiedostoon. Tällöin desimaalipilkkujen kanssa ei tule ongelmia.

Jos taulukko kelpaa sellaisenaan julkaistavaksi, niin voin toki leikata sen ja liittää kuvana raporttiin.

Kuviot

Kuvion voin tallentaa plt.gcf().savefig -komennolla (gcf = get current figure). Tallennusformaatti määrittyy  kuviolle annettavan nimen tarkentimesta (esimerkiksi png):

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

Järjestelmäsi tukemat kuvaformaatit saat selville komennolla

plt.gcf().canvas.get_supported_filetypes()

Jos tarvitset tietyn kokoisia kuvia tai haluat käyttää tietynlaista tai tietynkokoista fonttia, niin voit tehdä määritykset heti ohjelmakirjastojen tuonnin jälkeen (ei kuitenkaan samassa Jupyter notebookin solussa kuin ohjelmakirjastojen tuonti, koska se ei jostain syystä toimi). Esimerkkejä mahdollisista määrityksistä:

plt.rc('figure', figsize = (8, 6))
plt.rc('font', family = 'sans-serif', size = 8)
plt.rc('axes', titlesize = 8 )
plt.rc('axes', labelsize = 8)
plt.rc('xtick', labelsize = 8)
plt.rc('ytick', labelsize = 8)
plt.rc('legend', fontsize = 8)

Kuviot Pythonilla 3

Kategoristen muuttujien jakauman esitetän lukumäärä- tai prosenttipylväitä käyttäen. Näistä voit lukea lisää Kuviot Pythonilla 1 ja Kuviot Pythonilla 2. Tässä artikkelissa esitän esimerkkejä määrällisen muuttujien kuvaamisesta. Tämän artikkelin ohjelmakoodin ja tulosteet löydät GitHubista:

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

Ensimmäiseksi minun täytyy ottaa käyttöön tämän artikkelin kuvioissa tarvittavat kirjastot:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
plt.style.use('seaborn-white')

Tyylin ’seaborn-white’ sijasta voit käyttää sinulle mieluista tyyliä.

Avaan seaborn-kirjastoon sisältyvän esimerkkiaineiston tips (tietoja ravintola-asiakkaiden ravintolakäynneistä):

tips = sns.load_dataset('tips')
tips.head()

tips

Histogrammi

Ensimmäiseksi kuvaan ravintola-asiakkaiden laskujen loppusumman jakauman histogrammina:

ax=tips['total_bill'].plot.hist(bins=5)
ax.set_xlabel('total_bill')

hist1

Lisämääre bins=5 määrittää luokkien (histogrammin pylväiden) lukumäärän.

Voin myös itse määrittää luokkarajat:

ax=tips['total_bill'].plot.hist(bins=[0,10,20,30,40,50,60], 
   edgecolor='white')
ax.set_xlabel('total_bill')

hist2

Antamani luokkarajat tuottavat luokat [0,10), [10,20), [20,30), [30,40), [40,50) ja [50,60]. Luokan alaraja siis sisältyy aina luokkaan, mutta luokan yläraja ei sisälly luokkaan viimeistä luokkaa lukuun ottamatta.

Lisämääre edgecolor=’white’ lisää pylväille valkoisen reunan.

Seuraavaksi kuvaan palvelurahan määrän jakaumaa. Jos haluan esittää histogrammissa lukumäärien sijasta prosentit, niin voin käyttää esimerkiksi seuraavaa esoteerisen näköistä koodia:

weights=np.ones_like(tips['tip'])/float(len(tips['tip']))
ax=tips['tip'].plot.hist(bins=[0,2,4,6,8,10], weights=weights, 
   edgecolor='white')
ax.set_xlabel('tip')
ax.set_ylabel('%')
vals = ax.get_yticks()
ax.set_yticklabels(['{:.0f} %'.format(y*100) for y in vals])

hist3

weights vaatinee selitystä:

Otin edellä käyttöön numpy-kirjaston (import numpy as np). Numpy (numerical python) on peruskirjasto numeerisen tiedon käsittelyyn. Tässä käytän np.ones_like toimintoa, joka korvaa kunkin ravintola-asiakkaan palvelurahan ykkösellä. Jakamalla ykkösen palvelurahaa maksaneiden lukumäärällä (float(len(tips[’tip’]) saan selville prosenttiosuuden kaikista palvelurahaa maksaneista. Kaikkiaan weights=np.ones_like(tips[’tip’])/float(len(tips[’tip’])) muodostaa sarjan prosenttiosuuksia, joilla voin painottaa histogrammin lukumääriä ja näin lopulta saan histogrammiin prosentit.

Todennäköisyysjakauman estimointi

Voin edetä histogrammista taustalla olevan todennäköisyysjakauman tiheysfunktion estimointiin käyttämällä seaborn-kirjaston distplot-toimintoa:

sns.distplot(a=tips['tip'].dropna(), bins=[0,2,4,6,8,10])

distplot1

distplot antaa virheilmoituksen puuttuvista arvoista. Tämän vuoksi poistan dropna()-funktiolla mahdollisia puuttuvia arvoja sisältävät rivit. Kuvion pystyakselin arvot ovat todennäköisyysjakauman tiheysfunktion arvoja. Todennäköisyysjakauma estimoidaan ei-parametrisella KDE-menetelmällä (kernel density estimation). Voit lukea Wikipediasta lisää KDE-menetelmästä https://en.wikipedia.org/wiki/Kernel_density_estimation

Lisämääreellä kde=False voin laatia pelkän histogrammin:

sns.distplot(a=tips['tip'].dropna(), bins=[0,2,4,6,8,10], 
   kde=False, rug=True)

distplot2

Luokkien lukumäärien lisäksi voin tarkastella arvojen jakaumia luokkien sisällä alareunan viivoista. Nämä tulivat kuvioon lisämääreen rug=True ansiosta.

Ruutu- ja janakaavio

Ruutu- ja janakaavio on tehokas tapa määrällisen muuttujan jakauman tarkasteluun kategorisen muuttujan määräämissä luokissa. Voin esimerkiksi tarkstella palvelurahan suuruutta eri kokoisten seurueiden kohdalla:

ax=tips.boxplot(column='tip', by='size')
ax.set_title('')
plt.suptitle('')
ax.set_ylabel('tip')

box1

Ruutu- ja janakaavio vaatii lähtötietoinaan kuvattavan muuttujan (column) ja luokittelevan muuttujan (by). Jos ruutu- ja janakaavio ei ole sinulle entuudestaan tuttu, niin lue lisää artikkelistani Ruutu- ja janakaavio.

Voin laatia ruutu- ja janakaavion kätevämmin seaborn-kirjaston avulla. Esimerkiksi palvelurahat seurueen koon mukaan luokiteltuna sukupuolittain:

sns.boxplot(x='size', y='tip', hue='sex', data=tips)

box2

Pairplot

Kahden muuttujan välisiä riippuvuuksia voin tarkatella seaborn-kirjaston pairplot-kuviona.

Käytän esimerkkinä aineistoa, jossa on eri aiheiden osaamista/lahjakkuutta kuvaavia pistemääriä ja arvosanoja sekä opintomenestystä kuvaava pistemäärä.

opintomenestys = pd.read_excel('http://taanila.fi/
   opintomenestys.xlsx')
opintomenestys.head()

opintomenestys
Voin laatia kaikki parittaiset hajontakuviot useammasta muuttujasta:

sns.pairplot(opintomenestys[['verbaalinen','looginen','kielet',
   'matematiikka','opintomenestys']], kind='reg')

pairplot1

Yllä on näkyvillä vain osa kuvioista. Tuloksena saan jokaisesta muuttujasta histogrammin ja kaikki parittaiset hajontakaaviot pienimmän neliösumman regressiosuoralla täydennettynä. Voin tarkastella asiaa myös sukupuolittain:

sns.pairplot(opintomenestys[['verbaalinen','looginen','kielet',
   'matematiikka','opintomenestys','sukupuoli']], 
   hue='sukupuoli', kind='reg')

pairplot2

Histogrammien sijasta saan KDE-menetelmällä estimoidut todennäköisyysjakaumat miehille (oranssi) ja naisille (sininen). Jos haluan histogrammit, niin voin käyttää lisäparametria diag_kind=’hist’.

Jointplot

Yksittäisen muuttujaparin hajontakuvion saan kätevimmin seaborn-kirjaston jointplot-toiminnolla:

sns.jointplot(x='kielet', y='opintomenestys', data=opintomenestys, 
   kind='reg')

jointplot1

Kuvion reunoilla on muuttujien histogrammit sekä KDE-menetelmällä estimoidut todennäköisyysjakumat.

Tarvittaessa voin laskea kuvioon korrelaatiokertoimen ja sen merkitsevyyttä mittaavan p-arvon. Korrelaatiokertoimen laskemiseen tarvittavat funktiot löytyvät scipy.stats-kirjastosta. Pearsonin korrelaatiokerroin löytyy pearsonr-nimellä ja Spearmanin järjestyskorrelaatiokerroin spearmanr-nimellä.

from scipy.stats import spearmanr
sns.jointplot(x='matematiikka', y='opintomenestys', data=opintomenestys, 
   kind='reg', stat_func=spearmanr)

jointplot2

Aikasarjaennustaminen 3

Päivitetty 22.12.2019

Tämä artikkeli on jatkoa artikkeleille Aikasarjaennustaminen 1 ja Aikasarjaennustaminen 2.

Edellisen artikkelin Aikasarjaennustaminen 2 lopussa totesin, että esimerkkinä käyttämässäni aikasarjassa on neljän vuosineljänneksen välein toistuvaa kausivaihtelua, joka on syytä huomioida ennustamisessa. Tässä artikkelissa tarkastelen kausivaihtelun huomioivaa Holt-Winterin menetelmää.

Holt-Winterin tulomallissa aikasarjan tason L (level) hetkellä t määrittää lauseke

Lt = alfa * Yt/St-s + (1 – alfa)(Lt-1 + Tt-1)

Yllä Yt on viimeisin havainto, St-s on edellisen vastaavan periodin kausivaihtelu ja  Tt-1 on edellinen trendi.

Trendille T hetkellä t saadaan arvio lausekkeesta

Tt = beta * (Lt – Lt-1) + (1 – beta) * Tt-1

Kausivaihtelulle S hetkellä t saadaan arvio lausekkeesta

St = gamma * Yt/Lt + (1 – gamma) * St-s

Ennuste hetkelle t + p saadaan

(Lt + pTt)St-s

Yllä on kyse Holt-Winterin tulomallista, jossa kausivaihtelu huomioidaan kausivaihtelukertoimena. Holt-Winterin mallia voidaan soveltaa myös summamallina, jolloin kausivaihtelu huomioidaan lisättävänä kausivaihteluterminä. Tulomalli soveltuu paremmin tilanteisiin, joissa kausivaihtelukomponentin suuruus vaihtelee aikasarjan tason L mukaan. Summamalli soveltuu tilanteisiin, joissa kausivaihtelukomponentin suuruus ei riipu aikasarjan tasosta L.

Mallin parametrit alfa, beta ja gamma pyritään määrittämään siten että ennustevirheiden neliöiden keskiarvo saadaan mahdollisimman pieneksi.

Python toteutus

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

https://nbviewer.jupyter.org/github/taanila/tilastoapu/blob/master/forecast3.ipynb

 

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

Jupyter

Jupyter on kätevä väline data-analytiikkaan liittyvien Python-ohjelmien kirjoittamiseen ja suorittamiseen. Jupyter asentuu Anacondan asentamisen yhteydessä.

Käynnistä Jupyter

Käynnistä Jupyter Notebook suoraan käynnistysvalikosta. Jupyter Notebookin löydät Anacondan alta. Voit halutessasi tehdä sille oman kuvakkeen työpöydälle.

Jupyter käynnistyy oletusselaimeesi Home-nimiselle välilehdelle.

Luo uusi notebook

Voit luoda uuden tyhjän notebookin Jupyterin etusivun oikean yläreunan New – Python 3 -toiminnolla. Uusi notebook avautuu selaimeen uudelle välilehdelle.

jupyter1

Notebookin yläreunasta löydät työkalupainikkeita ja valikoita, joiden sisältöön kannattaa heti alussa tutustua. Notebookin voit nimetä uudelleen File-valikon Rename-toiminnolla.

Kirjoita koodia

Jupyter-notebook koostuu soluista. Soluun voit kirjoittaa Python-koodia ja kommentteja. Kommenttirivi alkaa aina #-merkillä. Koodin voit suorittaa usealla tavalla:

  • Cell-valikon kautta
  • työkalurivin painikkeella
  • näppäinyhdistelmällä ctrl-enter tai shift-enter (siirtää kohdistimen samalla seuraavaan soluun). Lisää pikanäppäimiä löydät Googlaamalla Jupyter keyboard shortcuts.

jupyter2

Koodin suorituksesta mahdollisesti seuraavat tulosteet, varoitukset ja virheilmoitukset tulostuvat solun alapuolelle. Voit milloin tahansa muuttaa solun koodia ja suorittaa koodin uudelleen.

Uusia soluja voit lisätä Insert-valikon kautta ja soluja voit tuhota Edit-valikon kautta.

Aiemmin suorittamasi koodin tallentamat muuttujat ym. tiedot säilyvät koko istunnon ajan. Voit näin ollen suorittaa osan ohjelmasta yhdessä solussa ja jatkaa ohjelman suoritusta toisessa solussa.

Jos muokkaat ja suoritat uudelleen aiempia koodisoluja, niin ajaudut helposti ennakoimattomiin virhetilanteisiin. Tarvittaessa voit tyhjentää istunnon tallentamat tiedot valitsemalla Kernel-valikosta Restart & Clear Output. Jokaisella notebookilla on oma Kernel. Restart & Clear Output vaikuttaa ainostaan aktiiviseen notebookiin.

Tallenna notebook

Notebook tallentuu Jupyterin oletuskansioon. Minun Windows 10 -koneella oletuskansiona on C:/Users/taaak/ (taaak on minun käyttäjätunnukseni). Jupyter notebook -tiedoston tunnistat tiedostonimen loppuosasta .ipynb. Tallentamasi notbookit löydät Jypyterin etusivulta (Home-välilehti) ja voit avata notebookin omalle välilehdelleen napsauttamalla notebookin nimeä.

Voit luoda oletuskansion alle uusia kansioita. Voit siirtyä toiseen kansioon napsauttamalla Jupyterin etusivulla (Home-välilehti) kansion nimeä. Uusi notebook tallentuu aina siihen kansioon, joka on valittuna.

Opettele Pythonin perusteet

Otettuasi Jupyterin käyttöön voitkin opetella Pythonin perusteet Teemu Sirkiän mainion materiaalin avulla:

http://www.cs.hut.fi/~ttsirkia/Python.pdf