Afsnit Py.6: Statistik
Py.6.1 Simulere stokastiske variable
I statistik vil vi ofte simulere data for at se, hvordan disse opfører sig
under idealiserede forhold. Dette kan være noget så simpelt som at se,
hvad der sker, når vi kaster en "fair" terning. Som et eksempel kan vi
undersøge, hvor ofte summen af antal øjne på to terninger er
større end eller lig med 9.
Vi vil derfor gerne kunne få
python til at kaste
terningerne for os. Man taler om at
generere tilfældige tal
(random number generator). Følgende tabel viser nogle af mulighederne i
python. For
binom,
poisson,
multinomial
og
norm skal disse importeres via kommandoen
from scipy.stats import navn.
Som et første eksempel vil jeg svare på det spørgsmål, der blev stillet
ovenfor i forbindelse med kast af to terninger.
Jeg vil kaste de to terninger 10
000 gange og se, hvor stor en andel
af disse hvor summen af antal øjne er større end eller lig med 9.
De 10
000 kast af den ene terning genereres med
sample(6,10000,replace=T), hvor "6"
angiver, at de mulige udfald er
svarende til en
terning, og "10000" siger, at der skal laves en vektor med
10
000 tilfældige
udfald.
import numpy as np
x=1+np.random.choice(6,10000,replace=True) # 1.terning kastes 10000 gange
y=1+np.random.choice(6,10000,replace=True) # 2.terning kastes 10000 gange
z=x+y # sum af antal øjne i de 10000 kast
print(sum(z>=9)/10000) # andel med sum >=9
Som et andet eksempel vil jeg finde variansen på en transformeret
størrelse ved simulering. Lad os betragte volumet
af en cylinder
med radius
og højde
,
.
Radius er målt til
med en standard error på
og højden er målt til
med en standard error på
For at simulere en spredning på det målte volumen
vil vi trække nye
målinger af radius fra en
-fordeling og nye målinger af
højden fra en
-fordeling
import numpy as np
from scipy.stats import norm
nSim=10000
vhat=np.pi*(10**2)*2
rtilde=norm.rvs(10,0.2,nSim)
htilde=norm.rvs(2,0.1,nSim)
vtilde=np.pi*rtilde*rtilde*htilde
print('Skøn:',vhat,' Standard Error:',np.sqrt(sum((vtilde-vhat)**2)/nSim))
Py.6.2 Fordelingsfunktioner og fraktiler
I
python kan man finde fordelingsfunktionen, det vil sige sandsynligheden
for at ligge til venstre for et punkt, for alle standard fordelinger.
Disse er navngivet ved, at der sættes
.cdf efter
navnet på fordelingen.
De tilsvarende funktioner for at
finde fraktiler i en fordeling fremkommer ved at
sætte
.ppf efter navnet på
fordelingen.
Ved en standard normalfordeling kan man udelade
mu=0 og sigma=1 i kaldet til
norm.
Følgende tabel viser nogle af mulighederne for fordelingsfunktionen.
For at have fordelingen til
rådighed skal denne importeres via kommandoen
from scipy.stats import navn.
from scipy.stats import norm, chi2
print(norm.cdf(1.96,0,1))
| 0.9750021048517795
print(norm.cdf(1.96))
| 0.9750021048517795
print(chi2.cdf(5.99,2))
| 0.9499633729134137
print(chi2.ppf(0.95,2)
| 5.991464547107979
Py.6.3 Beskrivende statistik
For en vektor
med observerede dataværdier beregner man ofte
forskellige beskrivende mål. Tabellen nedenfor gengiver de mest
almindelige, hvor
numpy er importeret som
np.
For at få en direkte fornemmelse af fordelingen kan man lave et
histogram.
I
python gøres dette med funktionen
plt.hist efter
komandoen
import matplotlib.pyplot as plt.
Denne funktion laver en histogramfigur og danner en struktur, hvorfra antallene
i de forskellige intervaller kan findes.
Hvis data ligger i en vektor
bliver det grundlæggende kald til
funktionen
plt.hist(x). Den intervalinddeling, som
hist laver, er ikke særlig køn, idet
den blot laver 10 intervaller fra den mindste til den største værdi i
vektoren
. Man kan lave sin egen inddeling ved at tilføje
bins=endePkt til kaldet, hvor
endePkt er en
vektor med endepunkter for intervallerne.
Som default laver
hist et antalhistogram. Man kan få et
tæthedshistogram ved at lave tilføjelsen
density=True til kaldet.
I optællingen af antal observationer i et interval inkluderer
hist
det venstre endepunkt, men ikke det højre (pånær det sidste interval, som
også inkluderer det højre endepunkt). Dette er modsat af definitionen af et
histogram i denne webbog.
Hvis vi også vil have antallet af observationer i de forskellige intervaller
som output bliver det generelle kald:
antal,bi,pa=plt.hist(x,bins=endePkt)
hvor antallene ligger i vektoren
antal. Hvis man laver tilføjelsen
density=True indeholder vektoren
antal tæthedsværdierne
i stedet for antallene.
Titel for figuren, og titler på akserne, kan sættes på som ved et
almideligt kald af
plt.plot
Py.6.4 Goodness of fit test
Et jordstykke er delt op i 100 lige store områder. I hvert af disse områder
er der plantet 50 ærter, og der registreres hvor mange af disse, der spirer
og vokser op. De målte antal betegnes
Nedenfor laves et histogram med intervallængde 2 og
efterfølgende
et goodness of fit test for, om data kan beskrives med en
binomialfordeling. Metoden er omtalt i afsnit
3.4
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import binom, chi2
x=np.array([31,34,31,35,37,35,37,25,32,35,33,40,31,34,30,37,41,34,36,\
31,28,33,40,28,32,31,39,34,38,34,35,36,36,32,36,35,38,40,\
37,42,38,37,33,34,35,36,37,32,36,39,32,37,28,34,33,31,36,\
41,38,32,33,39,34,37,36,40,32,39,39,38,34,34,38,35,31,33,\
32,28,38,37,34,36,35,37,34,37,34,40,39,38,34,28,30,29,32,\
39,36,33,36,31])
n=len(x) # antal observationer
phat=sum(x)/(50*n) # skøn over p
endePkt=np.linspace(24,44,11)-0.5 # intervalendepunkter i histogram
antal,bi,pa=plt.hist(x,bins=endePkt) # figur med antalshistogram
# beregne forventede fra binomialfordeling
endePkt0=endePkt[1:10]
pr0=binom.cdf(endePkt0,50,phat) # sandsynlighed til venstre for endepunkt
pr=np.append(pr0,1)-np.append(0,pr0) # intervalsandsynligheder
# første interval fra 0 til og med 25, sidste fra 42 til 50
ex=n*pr # forventede
print(np.array([antal,np.around(ex,2)]))
| [[ 1. 0. 6. 10. 16. 22. 22. 15. 7. 1. ]
| [ 0.29 1.15 3.97 10.03 18.32 23.87 21.77 13.52 5.49 1.59]]
# slår intervaller sammen for at få forventede >= 5
antal1=np.concatenate(([sum(antal[0:3])],antal[3:8],[antal[8]+antal[9]]))
ex1=np.concatenate(([sum(ex[0:3])],ex[3:8],[ex[8]+ex[9]]))
print(np.array([antal1,np.around(ex1,2)]))
| [[ 7. 10. 16. 22. 22. 15. 8. ]
| [ 5.41 10.03 18.32 23.87 21.77 13.52 7.08]]
gTest=2*sum(antal1*np.log(antal1/ex1))
df=len(antal1)-1-1
pval=1-chi2.cdf(gTest,df)
print('G:',gTest,' Df:',df,' P-værdi:',pval)
| G: 1.1580228811532804 Df: 5 P-værdi: 0.9488405467978349
Py.6.5 Test
I dette afsnit nævnes, hvordan nogle af de klassiske statistiske tests
kan laves i
python.
Jeg starter med
-tests for normalfordelte data med et eller to
observationssæt. Her er de indbyggede funktioner i
scipy.stats eller
statsmodels ikke optimale, og jeg har i
stedet lavet nogle funktioner, der ligger i filen
pytFunktioner.py.
Hvis
er en vektor med dataværdier, kan man lave et
-test for, at
middelværdien har en bestemt værdi
mu0 med
ttest.
Dette er gennemgået i afsnit
4.7.
Funktionen kaldes på følgende vis:
ttest(x,mu=mu0)
Hvis
mu=mu0 udelades, laves et test for, om middelværdien er nul.
Output fra
ttest er en pandas dataframe.
Hvis output placeres i
testUD kan man få
de forskellige dele
af output ved at skrive
testUD['navn'][0], hvor mulighederne er:
Konfidensintervallet er for den ukendte middelværdi, og
størrelsen af konfidensintervallet bestemmes af et niveau, der kan
vælges
ved at tilføje
conflevel=0.95 i kaldet til
ttest.
Funktionen
ttest laver et såkaldt tosidet test, det vil sige, at
både store positive og store negative værdier er kritiske.
Ovenfor blev
-testet for en enkelt observationsrække omtalt. Lad os nu betragte
-testet for samme middelværdi i to observationsrækker.
Dette svarer til modellen i afsnit
6.1.
Antag, at vektoren
indeholder dataværdierne for den ene
observationsrække og
dataværdierne for den anden
observationsrække. Den relevante kommando er
ttest2(x,y,varequal=True)
som laver
-testet under antagelse om, at varianserne i de to grupper
er ens. Hvis varianserne ikke er ens, skal man ændre True
til False. Outputtet fra funktionen indeholder de samme elementer som
ovenfor. Skøn og konfidensintervallet er i dette tilfælde for
forskel i middelværdi mellem de to grupper
I tilfældet med to grupper af observationer kan man lave et test for,
at de to varianser er ens ved
vartest2(x,y)
Output ligner output ovenfor, borset fra at
tstat og
df
er blevet til
fstat,
df1 og
df2.
Skøn og konfidensinterval er for forholdet mellem de to spredninger.
Py.6.6 Regression
Den lineære regressionsmodel fra afsnit
7.1
analyseres i
python ved hjælp af funktionen
ols
fra modulet
statsmodels.formula.api. Input er en
modelformel på formen
'xt',
hvor
er responsvariablen, og
er den forklarende variabel.
Desuden skal data være en del af input i form af en
dataframe. Formelt bliver kaldet af funktionen på formen
ols(formula=modelformel,data=dataframe).fit()
Den følgende kode viser et eksempel med Hubbles data omtalt i
afsnit
7.6.
De to figurer der produceres, kan ses i den sidste figur
i afsnit
Py.2
øverste højre hjørne og nederste delfigur.
En meget brugbar efterbehandling af resultatet fra
ols er
funktionen
summary2.
Denne giver tre tabeller hvor det især er den miderste
parametertabel der er af interesse, hvor der er skøn
og konfidensintervaller for parametrene såvel som
et
-test for, om parameteren
kan antages at være nul.
I den første tabel optræder
Scale, som indeholder
variansskønnet
fra afsnit
7.2.
from statsmodels.formula.api import ols
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pytFunktioner import *
a=np.array([0.032,0.034,0.214,0.263,0.275,0.275,0.450,0.500,\
0.500,0.630,0.800,0.900,0.900,0.900,0.900,1.000,\
1.100,1.100,1.400,1.700,2.000,2.000,2.000,2.000])
h=np.array([170,290,-130,-70,-185,-220,200,290,270,200,300,\
-30,650,150,500,920,450,500,500,960,500,850,800,1090])
ahdata=pd.DataFrame({'a':a,'h':h})
plt.plot(a,h,'o')
plt.xlabel('Afstand')
plt.ylabel('Hastighed')
plt.title('Regression')
plt.show()
# model analyseres og resultat placeres i lmUD
lmUD=ols(formula='h~a',data=ahdata).fit()
summaryLM(lmUD) # parameterestimater
| Estimated Coefficients:
| Coef. Std.Err. t P>|t| [0.025 0.975]
| Intercept -40.7836 83.4389 -0.4888 0.6298 -213.8253 132.2580
| a 454.1584 75.2371 6.0364 0.0000 298.1262 610.1906
|
| Number of observations: 24 Error degrees of freedom: 22
| Root Mean Squared Error: 232.9
| R-squared: 0.624 Adjusted R-Squared: 0.606
| F-statistic vs. constant model: 36.4 p-value = 4.48e-06
par=lmUD.params
plt.refline(par[1],par[0]) # linje indtegnes
print({'s(M)':np.sqrt(lmUD.mse_resid)})
| {'s(M)': 232.9106701830066}
plt.figure()
plt.plot(a,lmUD.resid,'o')
plt.title('Residualer')
plt.axhline(0) # nullinje indtegnes
plt.show()
Funktionen
ols kan også bruges til den generelle lineære model
beskrevet i kapitel
8 og kapitel
9.
Følgende eksempel ser på data i afsnit
8.6.
Variable med tekststrenge bliver automatisk opfattet som faktorer.
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
import numpy as np
import pandas as pd
from scipy.stats import f
from pytFunktioner import *
logFlu=np.log(np.array([303,335,86,168,243,1323,568,1165,669,966,1724,3339,
3259,1731,1224,3140,2276,2861,2077,1893,2969,3000,5285,3536,3781,
132,23,105,62,12,229,264,119,81,434,917,266,782,1291,506,622,883,
532,1388,493,1459,1168,898,1774,1772]))
prodrug=np.repeat(["a","M"],25)
tid=np.repeat(["T1","T2","T3","T4","T5","T1","T2","T3","T4","T5"],5)
datadrug=pd.DataFrame({'logFlu':logFlu,'prodrug':prodrug,'tid':tid})
# tosidet variansanalyse
lmUD=ols(data=datadrug,formula='logFlu~prodrug*tid').fit()
summaryLM(lmUD)
| Estimated Coefficients:
| Coef. Std.Err. t P>|t| [0.025 0.975]
| Intercept 3.8569 0.2388 16.1524 0.0000 3.3743 4.3394
| prodrug[T.a] 1.4630 0.3377 4.3324 0.0001 0.7805 2.1455
| tid[T.T2] 1.3944 0.3377 4.1293 0.0002 0.7119 2.0769
| tid[T.T3] 2.6344 0.3377 7.8013 0.0000 1.9519 3.3169
| tid[T.T4] 2.7289 0.3377 8.0814 0.0000 2.0465 3.4114
| tid[T.T5] 3.3651 0.3377 9.9651 0.0000 2.6826 4.0475
| prodrug[T.a]:tid[T.T2] 0.0796 0.4776 0.1667 0.8685 -0.8856 1.0448
| prodrug[T.a]:tid[T.T3] -0.3100 0.4776 -0.6490 0.5200 -1.2751 0.6552
| prodrug[T.a]:tid[T.T4] -0.2637 0.4776 -0.5521 0.5840 -1.2288 0.7015
| prodrug[T.a]:tid[T.T5] -0.4882 0.4776 -1.0223 0.3128 -1.4534 0.4770
|
| Number of observations: 50 Error degrees of freedom: 40
| Root Mean Squared Error: 0.5339
| R-squared: 0.878 Adjusted R-Squared: 0.85
| F-statistic vs. constant model: 31.9 p-value = 1.58e-15
# additive model
lmUD1=ols(data=datadrug,formula='logFlu~prodrug+tid').fit()
summaryLM(lmUD1)
| Estimated Coefficients:
| Coef. Std.Err. t P>|t| [0.025 0.975]
| Intercept 3.9551 0.1805 21.9115 0.0000 3.5913 4.3189
| prodrug[T.a] 1.2665 0.1474 8.5938 0.0000 0.9695 1.5636
| tid[T.T2] 1.4342 0.2330 6.1546 0.0000 0.9646 1.9038
| tid[T.T3] 2.4794 0.2330 10.6399 0.0000 2.0098 2.9490
| tid[T.T4] 2.5971 0.2330 11.1451 0.0000 2.1275 3.0668
| tid[T.T5] 3.1210 0.2330 13.3931 0.0000 2.6513 3.5906
|
| Number of observations: 50 Error degrees of freedom: 44
| Root Mean Squared Error: 0.5211
| R-squared: 0.872 Adjusted R-Squared: 0.857
| F-statistic vs. constant model: 60.0 p-value = 1.57e-18
# Lave F-test for additiv struktur
df=lmUD.df_resid; SSD=lmUD.mse_resid*df
df1=lmUD1.df_resid; SSD1=lmUD1.mse_resid*df1
Ftest=((SSD1-SSD)/(df1-df))/(SSD/df)
pval=1-f.cdf(Ftest,df1-df,df)
print([Ftest,pval])
| [0.47644440231630847, 0.7527549308670061]
Foregående