from datetime import datetime
print(f'Päivitetty {datetime.now().date()} / Aki Taanila')
Päivitetty 2024-07-23 / Aki Taanila
Tämän muistion avulla voit laskea automaattisesti frekvenssitaulukot, ristiintaulukoinnit, dikotomisten muuttujien yhteenvedot, tilastolliset tunnusluvut ja korrelaatiot. Tulokset kirjoitetaan Excel-tiedostoon.
Sinun tehtäväksesi jää
# Analysoinnissa käytettävät Python-paketit
import numpy as np
import pandas as pd
from scipy.stats import chi2_contingency, ttest_ind, pearsonr
Esimerkkinä käyttämäni data osoitteessa https://taanila.fi/data1.xlsx. Datan tilalle voit vaihtaa toisen datan.
info()-listauksesta näet
df = pd.read_excel('https://taanila.fi/data1.xlsx')
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 82 entries, 0 to 81 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 nro 82 non-null int64 1 sukup 82 non-null int64 2 ikä 82 non-null int64 3 perhe 82 non-null int64 4 koulutus 81 non-null float64 5 palveluv 80 non-null float64 6 palkka 82 non-null int64 7 johto 82 non-null int64 8 työtov 81 non-null float64 9 työymp 82 non-null int64 10 palkkat 82 non-null int64 11 työteht 82 non-null int64 12 työterv 47 non-null float64 13 lomaosa 20 non-null float64 14 kuntosa 9 non-null float64 15 hieroja 22 non-null float64 dtypes: float64(7), int64(9) memory usage: 10.4 KB
Määrittele listoina kategoriset, määrälliset (kvantit) ja dikotomiset muuttujat. Tarvittaessa sama muuttuja voi olla useammassakin listassa. Muuttujan voi jättää myös pois listoista, jolloin sitä ei analysoida.
Tärkeää: Jos kategoriset, kvantit tai dikotomiset -listaan ei tule muuttujia, niin jätä kuitenkin tyhjä lista [ ]
.
Jos kategorisen muuttujan arvot ovat datassa numeroita, niin voit määritellä numeroita vastaavat tekstimuotoiset arvot listat-sanakirjana. Näin saat tekstimuotoiset arvot automaattisesti tulostaulukoihin. Kirjoita tekstimuotoiset arvot niitä vastaavien numeroarvojen mukaisessa järjestyksessä.
Tärkeää: Jos et määrittele tekstimuotoisia arvoja, niin jätä kuitenkin tyhjä listat-sanakirja listat = { }
Voit hyödyntää sarakenimien listaa (columns) omia listoja kirjoittaessa:
df.columns
Index(['nro', 'sukup', 'ikä', 'perhe', 'koulutus', 'palveluv', 'palkka', 'johto', 'työtov', 'työymp', 'palkkat', 'työteht', 'työterv', 'lomaosa', 'kuntosa', 'hieroja'], dtype='object')
# Tarkasteltavat muuttujat
kategoriset = ['sukup', 'perhe', 'koulutus', 'johto', 'työtov', 'työymp', 'palkkat', 'työteht']
kvantit = ['ikä', 'palveluv', 'palkka', 'johto', 'työtov', 'työymp', 'palkkat', 'työteht']
dikotomiset = ['työterv', 'lomaosa', 'kuntosa', 'hieroja']
# Esimerkkidatassa on useita samalla asteikolla mitattuja muuttujia
# Toiston välttämiseksi tallennetaan asteikko listana
tyytyvaisyys = ['Erittäin tyytymätön', 'Tyytymätön','Ei tyytymätön eikä tyytyväinen',
'Tyytyväinen', 'Erittäin tyytyväinen']
# Tekstiarvot df:n kategorisille muuttujille, joiden arvot datassa koodattu numeroina
listat = {'sukup':['Mies', 'Nainen'],
'perhe':['Perheetön', 'Perheellinen'],
'koulutus':['Peruskoulu', '2. aste', 'Korkeakoulu', 'Ylempi korkeakoulu'],
'johto':tyytyvaisyys,
'työtov':tyytyvaisyys,
'työymp':tyytyvaisyys,
'palkkat':tyytyvaisyys,
'työteht':tyytyvaisyys}
Seuraavan solun koodin avulla voit tarkistaa kategoristen ja dikotomisten muuttujien arvot.
for var in df[kategoriset + dikotomiset]:
try:
print(var, np.unique(df[var]))
except:
print(var, pd.unique(df[var]))
sukup [1 2] perhe [1 2] koulutus [ 1. 2. 3. 4. nan] johto [1 2 3 4 5] työtov [ 2. 3. 4. 5. nan] työymp [1 2 3 4 5] palkkat [1 2 3 4 5] työteht [1 2 3 4 5] työterv [ 1. nan] lomaosa [ 1. nan] kuntosa [ 1. nan] hieroja [ 1. nan]
Voit halutessasi vaihtaa Excel-tiedoston (pika.xlsx) nimen.
Tärkeää: Jos samanniminen Excel-tiedosto on jo olemassa niin menetät sen, koska koodi ylikirjoittaa tiedoston kysymättä.
# Excel-tiedoston luonti
writer = pd.ExcelWriter('pika.xlsx', engine='xlsxwriter')
workbook = writer.book
# Muotoilut
decimal3_format = workbook.add_format({'num_format':'0.000'})
percent_format = workbook.add_format({'num_format':'0.0 %'})
header_format = workbook.add_format({'align': 'right', 'bottom': 1})
title_format = workbook.add_format({'align': 'left', 'bottom':1})
index_format = workbook.add_format({'align': 'left', 'border':0})
bold_format = workbook.add_format({'bold': True})
right_format = workbook.add_format({'align':'right'})
Tulosten laskenta toistuu samanlaisena usealle muuttujalle. Tämän vuoksi seuraavassa käytetään valmiita funktioita (def), joita kutsutaan jokaisen laskettavan muuttujan kohdalla.
Tulosten kirjoittaminen Exceliin ja muotoilu Excelissä muodostaa ison osan koodista. Tässä käytetään xlsxwriteria, josta löydät lisätietoa osoitteesta https://xlsxwriter.readthedocs.io/
def frekv(muuttuja):
'''Frekvenssitaulukko df:n muuttujalle muuttuja'''
df1 = pd.crosstab(df[muuttuja], columns='f')
df1.columns.name = ''
n = df1['f'].sum()
df1['%'] = df1['f']/n
# muuttujan tekstimuotoiset arvot
if muuttuja in listat.keys():
if len(listat[muuttuja]) == len(df1.index):
df1.index = listat[muuttuja]
df1.loc['Yhteensä'] = df1.sum()
return df1
if kategoriset:
rivi = 2 # Excelin rivinumero, johon seuraava taulukko kirjoitetaan
ws1 = workbook.add_worksheet('frekvenssitaulukot')
for muuttuja in kategoriset:
df1 = frekv(muuttuja)
df1.to_excel(writer, sheet_name='frekvenssitaulukot', startrow=rivi)
ws1.write(rivi, 0, muuttuja, title_format)
for sarake, arvo in enumerate(df1.columns):
ws1.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(df1.index):
ws1.write(rivi+rivinumero+1, 0, arvo, index_format)
rivi = rivi+df1.shape[0] + 2
ws1.set_column('C:C', cell_format=percent_format)
ws1.write(0, 0, 'Kategoristen muuttujien frekvenssitaulukot', bold_format)
def risti(muuttuja1, muuttuja2):
'''Ristiintaulukointi df:n muuttujille muuttuja1 ja muuttuja2.
Funktio palauttaa ristiintaulukoinnin lisäksi n-arvot ja khiin neliö -testin tuloksen.'''
khi_testi = chi2_contingency(pd.crosstab(df[muuttuja1], df[muuttuja2]))
df1 = pd.crosstab(df[muuttuja1], df[muuttuja2], margins=True)
df2 = pd.crosstab(df[muuttuja1], df[muuttuja2], margins=True, normalize='columns')
# muuttujien tekstimuotoiset arvot
if muuttuja1 in listat.keys():
if len(listat[muuttuja1]) == len(df1.index) - 1:
df1.index = listat[muuttuja1] + ['Yhteensä']
df2.index = listat[muuttuja1]
else:
df1.rename(index={'All':'Yhteensä'}, inplace=True)
else:
df1.rename(index={'All':'Yhteensä'}, inplace=True)
if muuttuja2 in listat.keys():
if len(listat[muuttuja2]) == len(df1.columns) - 1:
df1.columns = listat[muuttuja2] + ['Yhteensä']
df2.columns = listat[muuttuja2] + ['Yhteensä']
else:
df1.rename(columns={'All':'Yhteensä'}, inplace=True)
df2.rename(columns={'All':'Yhteensä'}, inplace=True)
else:
df1.rename(columns={'All':'Yhteensä'}, inplace=True)
df2.rename(columns={'All':'Yhteensä'}, inplace=True)
n_arvot = ['n']
for i in range(df1.shape[1]):
n_arvot.append(df1.iloc[-1, i])
return df2, n_arvot, khi_testi
if len(kategoriset) > 1:
rivi = 3 # Excelin rivinumero, johon seuraava taulukko kirjoitetaan
ws2 = workbook.add_worksheet('ristiintaulukoinnit')
for muuttuja1 in kategoriset:
for muuttuja2 in kategoriset:
if muuttuja1 != muuttuja2:
df2, n_arvot, khi_testi = risti(muuttuja1, muuttuja2)
df2.to_excel(writer, sheet_name='ristiintaulukoinnit', startrow=rivi)
for sarake, n in enumerate(n_arvot):
ws2.write(rivi+df2.shape[0]+1, sarake, n)
ws2.write(rivi, 0, muuttuja1, title_format)
ws2.write(rivi-1, 1, muuttuja2)
for sarake, arvo in enumerate(df2.columns):
ws2.write(rivi, sarake + 1, arvo, header_format)
for rivinumero, arvo in enumerate(df2.index):
ws2.write(rivi+rivinumero+1 , 0, arvo, index_format)
for rivinumero in range(rivi+1, rivi+df2.shape[0]+1):
ws2.set_row(rivinumero, cell_format=percent_format)
ws2.write(rivi, df2.shape[1]+2, f'khiin neliö = {khi_testi[0]:.3f}, p-arvo = {khi_testi[1]:.3f}, vapausasteet = {khi_testi[2]}')
ws2.write(rivi+1, df2.shape[1]+2, f'Alle viiden suuruisia odotettuja frekvenssejä {((khi_testi[3]<5).sum()/khi_testi[3].size*100).round(2)} %')
rivi = rivi+df2.shape[0] + 4
ws2.write(0, 0, 'Kategoristen muuttujien ristiintaulukoinnit', bold_format)
def dikot(dikotomiset):
'''Frekvenssit dikotomiset-listasta löytyville df:n muuttujille'''
df1 = df[dikotomiset].sum().to_frame('f').sort_values('f', ascending=False)
n = df.shape[0]
df1['% vastaajista'] = df1['f']/n
return df1, n
if dikotomiset:
rivi = 2 # Excelin rivinumero, johon seuraava taulukko kirjoitetaan
ws3 = workbook.add_worksheet('dikotomiset')
df1, n = dikot(dikotomiset)
df1.to_excel(writer, sheet_name='dikotomiset', startrow=rivi)
ws3.write(rivi, 0, '', title_format)
ws3.write(rivi+df1.shape[0]+1, 2, f'n = {n}', right_format)
for sarake, arvo in enumerate(df1.columns.values):
ws3.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(df1.index.values):
ws3.write(rivi+rivinumero+1 , 0, arvo, index_format)
ws3.set_column('C:C', 12, cell_format=percent_format)
ws3.write(0, 0, 'Dikotomisten muuttujien yhteenvedot', bold_format)
def risti_dikot(muuttuja, dikotomiset):
'''Ristiintaulukointi df:n muuttujan muuttuja ja dikotomiset-listan mukaisten
dikotomisten muuttujien välille'''
df1 = df.groupby(muuttuja)[dikotomiset].sum()
dfn = pd.crosstab(df[muuttuja], 'f') # n-arvot
# Prosenttien laskenta
n_arvot = ['n']
for i in range(df1.shape[0]):
df1.iloc[i, :] = df1.iloc[i, :]/dfn.iloc[i, 0]
n_arvot.append(dfn.iloc[i, 0])
# kategorisen muuttujan tekstimutoiset arvot
if muuttuja in listat.keys():
if len(listat[muuttuja]) == len(df1.index):
df1.index = listat[muuttuja]
return df1.T, n_arvot
if dikotomiset:
if kategoriset:
rivi = 3
ws4 = workbook.add_worksheet('kategoriset+dikotomiset')
for muuttuja in kategoriset:
df1, n_arvot = risti_dikot(muuttuja, dikotomiset)
df1.to_excel(writer, sheet_name='kategoriset+dikotomiset', startrow=rivi)
ws4.write(rivi-1, 1, muuttuja)
for sarake, n in enumerate(n_arvot):
ws4.write(rivi+df1.shape[0]+1, sarake, n)
ws4.write(rivi, 0, '', title_format)
for sarake, arvo in enumerate(df1.columns.values):
ws4.write(rivi, sarake + 1, arvo, header_format)
for rivinumero, arvo in enumerate(df1.index.values):
ws4.write(rivi+rivinumero+1 , 0, arvo, index_format)
for i in range(rivi+1, rivi+df1.shape[0]+1):
ws4.set_row(i, cell_format=percent_format)
rivi = rivi + df1.shape[0] + 4
ws4.write(0, 0, 'Kategoristen muuttujien ristiintaulukoinnit dikotomisten kanssa', bold_format)
tunnusluvut = ['Lukumäärä', 'Keskiarvo', 'Keskihajonta', 'Pienin', 'Alaneljännes',
'Mediaani', 'Yläneljännes', 'Suurin']
def tunnu(kvantit):
'''Laskee kvantit-listan sisältämien muuttujien tilastolliset tunnusluvut'''
if len(kvantit) == 1:
df1 = df[kvantit].describe().to_frame()
else:
df1 = df[kvantit].describe()
df1.index = tunnusluvut
return df1
def risti_tunnu(muuttuja1, muuttuja2):
'''Muuttujan muuttuja2 tunnuslukuja kategorisen muuttujan muuttuja1 määräämissä ryhmissä
Jos kategorinen muuttuja määrittää täsmälleen kaksi ryhmää, niin funktio
palauttaa myös kahden riippumattoman otoksen t-testin tuloksen.'''
df1 = df.groupby(muuttuja1)[muuttuja2].describe().T
df1.index = tunnusluvut
if muuttuja1 in listat.keys():
if len(listat[muuttuja1]) == len(df1.columns):
df1.columns = listat[muuttuja1]
kategoriat = df[muuttuja1].dropna().unique()
if len(kategoriat) == 2:
ryhma1 = df[muuttuja2][df[muuttuja1]==kategoriat[0]]
ryhma2 = df[muuttuja2][df[muuttuja1]==kategoriat[1]]
testi = ttest_ind(ryhma1, ryhma2, equal_var=False, nan_policy='omit')
else:
testi=False
return df1, testi
if kvantit:
rivi = 2
ws5 = workbook.add_worksheet('tunnusluvut')
df1 = tunnu(kvantit)
df1.to_excel(writer, sheet_name='tunnusluvut', startrow=rivi)
ws5.write(rivi, 0, '', title_format)
for sarake, arvo in enumerate(df1.columns.values):
ws5.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(df1.index.values):
ws5.write(rivi+rivinumero+1, 0, arvo, index_format)
if kategoriset:
rivi = rivi + 11
for muuttuja1 in kategoriset:
for muuttuja2 in kvantit:
if muuttuja1 != muuttuja2:
df1, testi = risti_tunnu(muuttuja1, muuttuja2)
df1.to_excel(writer, sheet_name='tunnusluvut', startrow=rivi)
ws5.write(rivi-1, 1, muuttuja1)
ws5.write(rivi, 0, muuttuja2, title_format)
for sarake, arvo in enumerate(df1.columns.values):
ws5.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(df1.index.values):
ws5.write(rivi+rivinumero+1 , 0, arvo, index_format)
if testi != False:
ws5.write(rivi+2, 4, f't = {testi[0]:.3f}, p-arvo = {testi[1]:.3f}')
rivi = rivi+11
ws5.set_column(0, 0, 12)
ws5.write(0, 0, 'Määrällisten muuttujien tunnusluvut ja tunnusluvut kategoristen muuttujien määräämissä ryhmissä', bold_format)
def korre(kvantit):
'''Laskee kvantit-listan sisältämien muuttujien väliset Pearsonin korrelaatiokertoimet sekä
kertoimiin liittyvät p-arvot ja n-arvot.'''
dfr = pd.DataFrame(index=df[kvantit].columns, columns=df[kvantit].columns).astype('float')
dfp = pd.DataFrame(index=df[kvantit].columns, columns=df[kvantit].columns).astype('float')
dfn = pd.DataFrame(index=df[kvantit].columns, columns=df[kvantit].columns)
for muuttuja1 in df[kvantit]:
for muuttuja2 in df[kvantit]:
if muuttuja1 != muuttuja2:
df_dropna = df.dropna(subset=[muuttuja1, muuttuja2])
r, p = pearsonr(df_dropna[muuttuja1], df_dropna[muuttuja2])
n = df_dropna.shape[0]
dfr.loc[muuttuja1, muuttuja2] = r
dfp.loc[muuttuja1, muuttuja2] = p
dfn.loc[muuttuja1, muuttuja2] = n
return dfr, dfp, dfn
if kvantit:
if len(kvantit) > 1:
rivi = 2
ws6 = workbook.add_worksheet('korrelaatiot')
dfr, dfp, dfn = korre(kvantit)
dfr.to_excel(writer, sheet_name='korrelaatiot', startrow=rivi)
ws6.write(rivi, 0, '', title_format)
for sarake, arvo in enumerate(dfr.columns.values):
ws6.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(dfr.index.values):
ws6.write(rivi+rivinumero+1, 0, arvo, index_format)
for rivinumero in range(rivi+1, rivi+dfr.shape[0]+1):
ws6.set_row(rivinumero, cell_format=decimal3_format)
rivi = rivi + dfr.shape[0] + 2
dfp.to_excel(writer, sheet_name='korrelaatiot', startrow=rivi)
ws6.write(rivi, 0, 'p-arvot', title_format)
for sarake, arvo in enumerate(dfr.columns.values):
ws6.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(dfr.index.values):
ws6.write(rivi+rivinumero+1, 0, arvo, index_format)
for rivinumero in range(rivi+1, rivi+dfp.shape[0]+1):
ws6.set_row(rivinumero, cell_format=decimal3_format)
rivi = rivi + dfr.shape[0] + 2
dfn.to_excel(writer, sheet_name='korrelaatiot', startrow=rivi)
ws6.write(rivi, 0, 'n-arvot', title_format)
for sarake, arvo in enumerate(dfr.columns.values):
ws6.write(rivi, sarake+1, arvo, header_format)
for rivinumero, arvo in enumerate(dfr.index.values):
ws6.write(rivi+rivinumero+1, 0, arvo, index_format)
ws6.write(0, 0, 'Määrällisten muuttujien väliset korrelaatiokertoimet', bold_format)
Tärkeää: Viimeisen solun suoritus on tärkeää, jotta tuotoksena syntynyt Excel-tiedosto on myöhemmin avattavissa
Jos tulostiedoston avaaminen Exceliin ei onnistu, niin todennäköisesti jokin koodisolu on antanut virheilmoituksen ja writer.close()-komento on jäänut suorittamatta. Suorita tällöin komento writer.close() seuraavasta solusta. Selvitä sen jälkeen mistä virheilmoitus johtui.
writer.close()