Grupperte data

Vi skal bestille klovnebukser til medlemmene i fanklubben til det norske curlinglandslaget. Medlemmene har oppgitt livvidde, og opptellingen viste at vi trenger:

15 i small, 142 i medium, 79 i large, 171 i XL, 114 i XXL og 37 i 3XL. Under setter vi dette opp i en OrderedDict. Denne gjør at vi beholder størrelsene i riktig rekkefølge i kommende tabeller. En vanlig dict / hashmap vil ikke beholdt rekkefølgen.

In [1]:
from collections import OrderedDict

data = OrderedDict()
data['S'] = {
    'Fra': 76,
    'Til': 82,
    'Antall': 15
}
data['M'] = {
    'Fra': 82,
    'Til': 90,
    'Antall': 142
}
data['L'] = {
    'Fra': 90,
    'Til': 98,
    'Antall': 79
}
data['XL'] = {
    'Fra': 98,
    'Til': 106,
    'Antall': 171
}
data['XXL'] = {
    'Fra': 106,
    'Til': 110,
    'Antall': 114
}
data['3XL'] = {
    'Fra': 110,
    'Til': 116,
    'Antall': 37
}

data
Out[1]:
OrderedDict([('S', {'Antall': 15, 'Fra': 76, 'Til': 82}),
             ('M', {'Antall': 142, 'Fra': 82, 'Til': 90}),
             ('L', {'Antall': 79, 'Fra': 90, 'Til': 98}),
             ('XL', {'Antall': 171, 'Fra': 98, 'Til': 106}),
             ('XXL', {'Antall': 114, 'Fra': 106, 'Til': 110}),
             ('3XL', {'Antall': 37, 'Fra': 110, 'Til': 116})])

Vi har satt opp hver gruppe med mål i cm. Det vil si at størrelse S gjelder for livvidder fra 76 til 82 cm. Nå kan vi gjøre dette om til en DataFrame, slik at vi kan få laget tabeller og grafer av dette.

In [2]:
from pandas import DataFrame

df = DataFrame.from_dict(data) # Lager en DataFrame fra vår data-variabel, som er av type OrderedDict / dict

# Henter ut kolonnen "Antall", og gjør den om til en DataFrame. Uten "to_frame" ville vi ikke fått en like fin tabell.
frekvens = df.loc['Antall'].to_frame()

frekvens
Out[2]:
Antall
S 15
M 142
L 79
XL 171
XXL 114
3XL 37

Nå kan vi videre lage et frekvensdiagram. Vi er fremdeles bare interessert i "Antall"-kolonnen, så vi gjenbruker "frekvens"-variabelen fra tidligere.

In [3]:
# Setter opp plotteverktøyet slik at vi får se figuren direkte i notebooken
%matplotlib inline
import matplotlib.pyplot as plt

# Plotter frekvens som en "bar chart", med full bredde (kan være 0-1), slik at stolpene treffer hverandre
frekvens.plot.bar(width=1)
plt.show()

Med det samme kan vi også lage en kumulativ frekvenstabell, og plotte denne.

In [4]:
frekvens_kumulativ = frekvens.cumsum()

frekvens_kumulativ
Out[4]:
Antall
S 15
M 157
L 236
XL 407
XXL 521
3XL 558
In [5]:
frekvens_kumulativ.plot.bar(width=1)
plt.show()

Intervallgrenser, bredde og gjennomsnitt

Nå skal vi utvide DataFramen vår. Vi har allerede fra- og til-grenser, men vi kan enkelt også regne ut bredden for hvert av disse intervallene. Med pandas kan vi bruke "loc" på DataFrame-objekter for å hente ut en rad. Vi definerer hvilken rad det er snakk om som en tekstverdi som indeks, mellom [ og ].

In [6]:
df.loc['Bredde']  = df.loc['Til']-df.loc['Fra'] # Verdi 1 i Til trekkes fra verdi 1 i Fra, osv, og lager en ny serie
df.loc['Gjennomsnitt'] = (df.loc['Til']+df.loc['Fra'])/2 # (Til+Fra)/2 for alle verdier i seriene

df # Vis tabellen med Bredde og Gjennomsnitt
Out[6]:
S M L XL XXL 3XL
Antall 15 142 79 171 114 37
Fra 76 82 90 98 106 110
Til 82 90 98 106 110 116
Bredde 6 8 8 8 4 6
Gjennomsnitt 79 86 94 102 108 113

Over lager vi en ny rad ved navn "Bredde", og setter denne til å være lik "Til"-raden minus "Fra"-raden. Da tar Pandas alle verdier i "Til", og trekker fra samme kolonne i "Fra", og lagrer dette som en ny rad.

Tilsvarende gjør vi får gjennomsnitt. Da tar vi til- og fra-grenser, legger dem sammen, og deler på to. Det gir verdien midt i intervallet.

Median og kvartiler for grupperte data

Det å finne median o.l. for grupperte data er ikke helt rett-frem. Den enkleste måten er faktisk å lage en ny serie, hvor vi ekspanderer gjennomsnittsverdiene. Det vil si at istedenfor det vi hadde over, får vi i serien 15 datapunkter med 79, 142 med 86 osv. [79, 79, 79, ..., 142, 142 ...] og regner så de ulike beliggenhetsmålene på denne serien. Selv om datamaskinen får flere tall å tygge på, blir koden kortere, og lettere for oss å forholde oss til.

In [7]:
from pandas import Series

# Forkortelser, så vi slipper å gjenta df.loc[...]
gjsnitt = df.loc['Gjennomsnitt']
antall = df.loc['Antall']

# Ny liste
ekspandert = []
for i in range(len(antall)): # For hver i i rekken [0, 1, ... |antall|]
    repetert = int(antall[i]) # Antall ganger gjennomsnittsverdien skal gjentas
    ekspandert.extend([gjsnitt[i]] * repetert) # Utvid listen med gjennomsnittsverdien "repetert" ganger

# Lager en ny serie av de ekspanderte dataene, og bruker .median() osv. på denne.
s = Series(ekspandert)
print('Median:       ', s.median())
print('1. kvartil:   ', s.quantile(0.25))
print('2. kvartil:   ', s.quantile(0.75))
print('Gjennomsnitt: ', s.mean())
print('Varians:      ', s.var())
Median:        102.0
1. kvartil:    86.0
2. kvartil:    108.0
Gjennomsnitt:  98.1326164874552
Varians:       89.00931127455745

Koden over er kanskje ikke helt selvforklarende, om man ikke har jobbet med Python noe særlig før. Vi lager en liste, og så kjører vi en for-løkke like mange ganger som antall buksestørrelser vi har i datasettet (antall kolonner). Vi finner så hvor mange som skal ha denne størrelsen (gjennomsnittsverdien i gruppen), og utvider så "ekspandert"-listen. ".extend()"-metoden tar inn en liste som første parameter, og utvider den opprinnelige listen ved å slå disse sammen. Vi lager egentlig bare en ny liste med ett element, men så ganger vi den med antallet vi trenger (repetert). Da får vi en lang liste, med gjentatte verdier.



Laget våren 2016, som prosjektoppgave i MA-155 Statistikk ved UiA av Olav Andreas Lindekleiv