Kirjoittajan arkistot: Aki Taanila

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.

 

Lineaarinen regressio 3

Jos koneoppiminen ja sklearn (scikit-learn) -kirjasto ovat sinulle täysin uusia, niin lue ennen tätä artikkelia Lineaarinen regressio 1 ja Lineaarinen regressio 2.

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

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

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

Tämän artikkelin esimerkkidatana käytän sklearn kirjastosta löytyvää dataa Bostonin asuntojen hinnoista. Artikkelin ideat olen lainannut osoitteesta

https://towardsdatascience.com/linear-regression-on-boston-housing-dataset-f409b7e4a155

Ohjelmakirjastojen tuonti

Kuvailevasta analyysistä tutut peruskirjastot ovat tarpeen:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

Datan valmistelu

Lataan esimerkkiaineiston sklearn-kirjastosta seuraavasti:

from sklearn.datasets import load_boston
boston_data = load_boston()

Aineistossa on eroteltu data (features), target (selitettävä muuttuja), feature_names (selittävien muuttujien nimet) ja DESCR (aineiston kuvaus). Tämä selviää keys()-funktiolla:

print(boston_data.keys())

dict_keys([’data’, ’target’, ’feature_names’, ’DESCR’])

Komennolla print(boston_data.DESCR) voit lukea aineiston kuvauksen.

Luon aineiston perusteella boston-nimisen dataframen:

boston = pd.DataFrame(boston_data.data, columns=boston_data.feature_names)
boston['MEDV'] = boston_data.target
boston.head()

linreg9

Selitettävä muuttuja MEDV kuvaa asuntojen mediaanihintoja tuhansina dollareina eri alueilla. Muut muuttujat ovat ehdokkaita mediaanihintaa selittäviksi muuttujiksi (voit lukea lisää aineiston kuvauksesta).

Varmuuden vuoksi kannattaa tarkistaa, onko datassa puuttuvia arvoja:

boston.isnull().sum()

linreg10

Puuttuvia arvoja ei ole, mikä on hyvä asia.

Seuraavaksi katson miten selitettävän muuttujan MEDV arvot ovat jakautuneet. Käytän tarkasteluun seaborn-kirjaston distplot-kuviota ja luokittelen hinnat 30 luokkaan:

sns.distplot(boston['MEDV'], bins=30)

linreg11

Kuvion mukaan asuntojen mediaanihintojen jakauma on oikealle vino. Vinoudesta huolimatta yritetään mallintaa hintoja lineaarisella regressiolla.

Selittävien muuttujien valinnassa voin hyödyntää korrelaatiokertoimia. Seaborn-kirjaston heatmap-funktiolla voin värjätä corr()-funktiolla lasketut korrelaatiokertoimet niiden arvon mukaan. Oletusarvolla heatmap tulostaa vain värilliset ruudut, mutta lisäparametrilla annot=True saan myös korrelaatiokertoimien arvot näkyviin:

correlation_matrix = boston.corr().round(2)
plt.figure(figsize=(12,9))
sns.heatmap(data=correlation_matrix, annot=True)

linreg12

Kaikkein eniten MEDV-muuttujan kanssa korreloivat RM (0,7) ja LSTAT (-0,74). Katson vielä kuvion avulla miltä kyseisten muuttujien korrelaatio MEDV-muuttujan kanssa näyttää. Seuraava koodi toimii vaikka lisäisit enemmänkin muuttujia features-listaan:

features = ['LSTAT', 'RM']
target = boston['MEDV']
plt.figure(figsize=(10, 5))
for i, col in enumerate(features):
   plt.subplot(1, len(features) , i+1)
   plt.scatter(boston[col], target)
   plt.xlabel(col)
   plt.ylabel('MEDV')

linreg13

Riippuvuus on selkeää molemmissa tapauksissa, mutta erityisesti LSTAT-muuttujan kohdalla riippuvuus ei ole täysin suoraviivaista. Tästä huolimatta jatketaan eteenpäin.

Valmistellaan vielä X (features-matriisi) ja y (target) mallintamista varten:

X = boston[['LSTAT', 'RM']]
y = boston['MEDV']

Opetusdata ja testidata

Jos dataa on riittävästi, niin kannattaa jakaa se opetusdataan ja testidataan. Testidatan avulla voidaan arvioida opetusdatan perusteella laadittua mallia. Seuraavassa jaan datan sattumanvaraisesti opetusdataan ja testidataan (20 % datasta). Parametri random_state asettaa satunnaislukugeneraattorin siemenluvun. Jos jätän sen asettamatta, niin saan eri kerroilla erilaisen jaon opetusdataan ja testidataan. Käyttämällä samaa siemenarvoa saan aina saman jaon.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, 
   test_size = 0.2, random_state=5)
print(X_train.shape)
print(X_test.shape)

(404, 2)
(102, 2)

Huomaan, että opetusdatassa on 404 havaintoa ja testidatassa 102 havaintoa.

Mallin sovitus

Mallin sovitukseen kuuluu LinearRegression-mallin tuonti lineaaristen mallien kirjastosta ja mallin sovitus fit-funktiolla.

from sklearn.linear_model import LinearRegression
malli = LinearRegression()
malli.fit(X_train, y_train)

Tuloksesta näen  mallin lähtötiedot, joita olisin halutessani voinut säätää:

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)

Mallin sopivuuden arviointi

Mallin sopivuutta arvioin keskivirheen ja selityskertoimen avulla.

RMSE (root mean squared error) eli keskivirhe lasketaan seuraavasti: lasketaan toteutuneiden havaintojen ja mallin ennustamien arvojen erotusten neliöt yhteen ja jaetaan havaintojen lukumäärällä (mean squared error); lopuksi otetaan neliöjuuri.

Mean squared error löytyy sklearn.metrics-kirjastosta:

from sklearn.metrics import mean_squared_error

y_train_predict = malli.predict(X_train)
rmse = (np.sqrt(mean_squared_error(y_train, y_train_predict)))
r2 = malli.score(X_train,y_train)

print('Mallin sopivuus opetusdataan')
print("--------------------------------------")
print('Keskivirhe: {}'.format(rmse))
print('Selityskerroin: {}'.format(r2))
print("n")

y_test_predict = malli.predict(X_test)
rmse = (np.sqrt(mean_squared_error(y_test, y_test_predict)))
r2 = malli.score(X_test, y_test)

print('Mallin sopivuus testidataan')
print('--------------------------------------')
print('Keskivirhe: {}'.format(rmse))
print('Selityskerroin: {}'.format(r2))

Mallin sopivuus opetusdataan
————————————–
Keskivirhe: 5.6371293350711955
Selityskerroin: 0.6300745149331701

Mallin sopivuus testidataan
————————————–
Keskivirhe: 5.137400784702911
Selityskerroin: 0.6628996975186953

Katson vielä kuviona, miten hyvin ennustaminen onnistuu testidatassa:

plt.scatter(y_test, y_test_predict)
plt.xlabel('y_test')
plt.ylabel('y_test_predict')

linreg14

Malli on sitä parempi, mitä lähempänä suoraa viivaa kuvion pisteet ovat.

Mallin parantaminen

Edellä esimerkkinä laskettu malli on kelvollinen, mutta voisin parantaa mallia monin tavoin:

Asuntojen mediaanihinnat eivät ole normaalisti jakautuneet (jakauma on oikealle vino). Mallin parantamiseksi voisin kokeilla jompaa kumpaa seuraavista:

  • pudotan jakauman yläpään suurimmat mediaanihinnat pois opetusdatasta
  • muunnan mediaanihintoja paremmin normaalijakaumaa vastaaviksi.

Selittävien muuttujien ja mediaanihintojen välinen riippuvuus ei ollut aivan suoraviivainen. Tämä saattaisi korjaantua, jos muunnan mediaanihinnat paremmin normaalijakaumaa vastaaviksi. Tarvittaessa voin tehdä myös selittäville muuttujille muunnoksia tai voin valita malliksi ei-lineaarisen mallin.

Malliin voin myös lisätä selittäviä muuttujia. PTRATIO-muuttujan ottamista selittäväksi muuttujaksi kannattaisi kokeilla.

Jos teen korjauksia malliin, niin voin arvioida korjatun mallin sopivuutta vertaamalla sen keskivirhettä ja selityskerrointa tämän artikkelin malliin.

Lineaarinen regressio 2

Jos koneoppiminen ja sklearn (scikit-learn) -kirjasto ovat sinulle täysin uusia, niin lue ennen tätä artikkelia Lineaarinen regressio 1

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

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

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

Tämän artikkelin esimerkeissä käytän datoja http://taanila.fi/mokki.xlsx ja http://taanila.fi/mokkinew.xlsx

Lineaarisella regressiomallilla voidaan ennustaa jatkuvaluonteisen muuttujan arvoja selittävien muuttujien avulla, jos selittävien muuttujien ja ennustettavan muuttujan välillä on likimain lineaarinen (suoraviivainen) riippuvuus.

Lineaarista regressiomallia voidaan pitää koneoppimisen mallina, jos kone oppii mallin parametrit olemassa olevan datan perusteella.

Tarkastelen esimerkkinä kuvitteellista aineistoa kesämökkien hinnoista. Hintaa selittävinä muuttujina ovat rantaviivan pituus metreinä, mökin pinta-ala neliömetreinä ja dikitominen muuttuja sähköliittymästä (1 = sähköliittymä, 0 = ei sähköliittymää).

Ohjelmakirjastojen tuonti

Tuon kuvailevasta analyysista tutut kirjastot (numpy-kirjastoa en tällä kertaa tarvitse):

import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

Datan valmistelu

Luen datan Excel-tiedostosta dataframeen:

df=pd.read_excel('http://taanila.fi/mokki.xlsx')
df

linreg4

Muodostan feature-matriisin selittävistä muuttujista ranta, pinta-ala ja sähkö. Selitettäväksi muuttujaksi (target) tulee hinta.

X=df[['ranta', 'pinta-ala','sähkö']]
y=df['hinta']

Mallin sovitus

Tuon lineaaristen mallien kirjastosta LinearRegression-mallin. Mukavuussyistä annan mallille nimeksi malli.

Sovitan mallin dataan fit-funktiolla.

from sklearn.linear_model import LinearRegression
malli=LinearRegression()
malli.fit(X,y)

Tuloksesta näen  mallin lähtötiedot, joita olisin halutessani voinut säätää:

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)

Mallin vakiotermi:

malli.intercept_

-96.94145434036429

Selittävien muuttujien kertoimet:

malli.coef_

array([ 1.9750098 ,  2.77578415, 20.29877373])

Mallin sopivuuden arviointi

Mallin selityskerroin:

malli.score(X,y)

0.9819136190845801

Selityskertoimen mukaan 98,2 % hinnan varianssista voidaan selittää selittävien muuttujien avulla.

Mallin sopivuutta voin arvioida myös virhetermejä (ennusteen ero toteutuneeseen hintaan) tarkastelemalla:

plt.scatter(malli.predict(X), malli.predict(X)-y)
plt.hlines(y=0,xmin=50,xmax=250)
plt.xlabel('Ennuste')
plt.ylabel('Poikkeama todellisesta')

linreg5

Virhetermit ovat melko satunnaisesti jakautuneet, mikä on hyvä asia.

Seuraavassa tarkastelen vielä pistekuviona toteutunutta hintaa ja mallin ennustamaa hintaa:

plt.scatter(df['hinta'], malli.predict(X))
plt.xlabel('Todellinen hinta')
plt.ylabel('Ennuste')

linreg6

Ennustaminen

Mallin perusteella voin laskea hintaennusteita uudelle datalle, jota ei käytetty mallin laatimiseen:

Xuudet=pd.read_excel('http://taanila.fi/mokkinew.xlsx')
Xuudet['Hintaennuste']=malli.predict(Xuudet)
Xuudet

linreg7

Perinteisempi regressiotuloste

Halutessani saan perinteisemmän regressiotulosteen statsmodels-kirjaston toiminnoilla:

import statsmodels.api as sm
X = sm.add_constant(X)
malli_sm = sm.OLS(y, X)
results = malli_sm.fit()
print(results.summary())

linreg8

Lineaarinen regressio 1

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

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

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

Tämän artikkelin esimerkeissä käytän dataa http://taanila.fi/linreg1.xlsx

Yleistä koneoppimisen malleista

Tästä artikkelista opit sklearn (scikit-learn) -ohjelmakirjaston koneoppimisen mallien käyttöliittymän pelkistetyn esimerkin avulla. Käyttöliittymä sklearn-kirjaston malleihin on yksinkertainen sisältäen seuraavat vaiheet:

Valmistele data

Ohjatun (supervised) oppimisen malleissa datasta täytyy erottaa selittävät muuttujat (feature-matriisi , x-muuttujat) ja selitettävä muuttuja (label, target, y-muuttuja).

Ohjaamattomissa (unsupervised) malleissa tarvitaan ainoastaan feature-matriisi.

Tuo malli ja säädä lähtötiedot

Esimerkiksi tässä artikkelissa käytettävä lineaarinen regressiomalli tuodaan seuraavasti:

from sklearn.linear_model import LinearRegression

Lineaaristen mallien kirjastosta siis tuodaan LinearRegression-malli. Mallin lähtötietojen säätäminen edellyttää käytettävän mallin tuntemusta. Jos et ole perehtynyt malliin, niin voit tyytyä lähtötietojen oletusarvoihin.

Sovita malli (fit)

Tässä vaiheessa suoritetaan varsinainen laskenta. Onneksi valmiit algoritmit hoitavat laskennan.

Arvioi mallin sopivuutta

Sopivuuden arviointiin on monia menetelmiä, esimerkiksi selityskertoimen laskeminen, visuaalinen tarkastelu tai mallin toimivuuden testaaminen testidatalla.

Ennusta (predict)

Ennakoivassa analytiikassa keskeisin vaihe on tietysti ennusteiden laskeminen uudelle datalle.

Lineaarinen regressio

Lineaarisella regressiomallilla voidaan ennustaa jatkuvaluonteisen muuttujan arvoja selittävien muuttujien avulla, jos selittävien muuttujien ja ennustettavan muuttujan välillä on likimain lineaarinen (suoraviivainen) riippuvuus.

Lineaarista regressiomallia voidaan pitää koneoppimisen mallina, jos kone määrittää (oppii) mallin parametrit olemassa olevan datan (training data) perusteella.

Ohjelmakirjastojen tuonti

Kuvailevasta analyysistä tutut peruskirjastot ovat tarpeen:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

Datan valmistelu

Tässä esimerkissä luen datan Excel-tiedostosta dataframeen:

df = pd.read_excel('linreg1.xlsx')
df

linreg1

Yritän selittää myyntiä mainoskuluilla, joten määritän mainoskulut selittäväksi muuttujaksi (x) ja myynnin selitettäväksi muuttujaksi (y). Feature-matriisin täytyy olla mallia sovitettaessa dataframe-muodossa, joten teen muunnoksen to_frame-funktiolla.

x=df['Mainoskulut 1000 €']
X=df['Mainoskulut 1000 €'].to_frame() #feature-matriisi
y=df['Myynti 1000 €'] #target
plt.scatter(x,y)

linreg2

Pistekaavion perusteella riippuvuus mainoskulujen ja myynnin välillä näyttää likimain lineaariselta (suoraviivaiselta).

Mallin sovitus

Tuon LinearRegression-mallin lineaaristen mallien kirjastosta ja nimeän sen yksinkertaisuuden vuoksi nimellä malli.

from sklearn.linear_model import LinearRegression
malli=LinearRegression()
malli.fit(X,y)

Tulosteena saan mallin lähtötiedot (joita olisin voinut halutessani säätää):

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)

Mallin sovittamisen jälkeen voin katsoa mallin parametreja. Yksinkertaisen lineaarisen regression tapauksessa mallin parametrit ovat suoran kulmakerroin ja vakiotermi.

malli.coef_ #kulmakerroin

array([52.56756757])

Kulmakerroin tulee listamuodossa (array), koska mallissa voisi olla useampia selittäviä muuttujia ja näin ollen myös useampia kulmakertoimia.

malli.intercept_ #vakiotermi

46.486486486486505

Mallina on siis suora, jonka yhtälö: y = 52,568x + 46,486

Mallin sopivuuden arviointi

Mallin selityskertoimen saan:

malli.score(X,y)

0.7663982928521625

Voin siis todeta: 76,6 % myynnin varianssista voidaan selittää mainoskuluilla.

Voin tarkastella mallin sopivuutta myös graafisesti:

xfit=np.linspace(0.4,1.4) #50 arvoa tasavälein väliltä 0.4 - 1.4
Xfit=pd.DataFrame(xfit)
yfit=malli.predict(Xfit)
plt.scatter(x,y)
plt.plot(xfit,yfit)
plt.xlabel('Mainoskulut 1000 €')
plt.ylabel('Myynti 1000 €')

linreg3

Ennustaminen

Jos mainoskuluihin suunnitellaan käytettäväksi 700, 800 tai 900 euroa, niin mallin mukaiset myyntiennusteet:

malli.predict([0.7, 0.8, 0.9])

array([83.28378378, 88.54054054, 93.7972973 ])

Yhteenveto

Yksinkertaisimmillaan tuodaan ja määritellään malli, sovitetaan malli ja ennustetaan:

from sklearn.linear_model import LinearRegression 

malli=LinearRegression() 

malli.fit(X,y)

malli.predict([0.7, 0.8, 0.9])

 

Data-analytiikkaa Pythonilla

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

Tavoitteen (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.

Data-analytiikan tarpeisiin datan täytyy olla taulukkomuotoista. Yleisiä data-analytiikkaan sopivia tiedostomuotoja ovat pilkkueroteltu tekstimuoto (.csv) ja Excel-muoto (.xls tai .xlsx). Tietokannoista data haetaan kyselyiden 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.
  • Datan kuvailu.
  • Tilastollinen merkitsevyys: Tämä vaihe tulee kyseeseen, jos data pohjautuu isommasta joukosta poimittuun otokseen. Tilastollinen merkitsevyys kertoo, millä varmuudella otoksessa havaittuja eroja ja riippuvuuksia voidaan yleistää isompaan joukkoon.
  • Koneoppiminen ja ennakoiva analytiikka.

Datan valmistelu

Datan valmistelu on yleensä 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:

  • 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.

Datan kuvailu

Datan kuvailu voi sisältää seuraavia:

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

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

Tilastollinen merkitsevyys

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ää perusjoukkoon.

Koneoppiminen ja ennakoiva analytiikka

Koneoppimisen malleilla voidaan luokitella (asiakkaat luottoriski-asiakkaisiin ja muihin, vakuutuskorvaushakemukset selviin tapauksiin ja petokselta haiskahtaviin jne.) ja ennakoida (tulevaa 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 esimerkiksi regressiomallit ja aikasarjaennustamisen menetelmät. Esimerkkejä, joissa on jatkuva label:

  • Osakehuoneiston hinnan ennustaminen: 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 kannattaa aloittaa 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