Mål og forberedelser
I denne arbeidsboken skal du lære:
- Hvordan funksjoner fungerer.
- Å bruke if-setninger (logiske betingelser).
- Å bruke løkker (“loops”).
- Å bruke funksjoner i
apply
-familien.
Funksjoner
Gjennom å bruke funksjoner kan man automatisere vanlige oppgaver på en enklere og mer effektiv måte enn å bruke copy-paste. På denne måte minimerer vi duplikasjon av kode. I alle delene til denne arbeidsboken har vi vist hvordan man kan bruke funksjoner for å gjennomføre ulike typer operasjoner fra å hente deskriptiv statistikk til å kjøre regresjonsanalyser og å lage grafer. Funksjonene har enten ligget i base R eller åpent i pakker laget av forskjellige kodere. Noen ganger kan det være at vi trenger å gjøre operasjoner med dataene våre flere ganger, og som det ikke allerede finnes en funksjon for. I slike tilfeller kan det være nyttig å lage sin egen funksjon.
Funksjoner er bare så smarte som koderen som har laget dem. Funksjoner kan bare gjøre akkurat hva du har fortalt dem at de skal gjøre, og dette krever at man eksplisitt sier hva som skal gjøres i hvert steg av oppgaven. I de fleste funksjoner så må vi spesifisere minst et argument
som brukeren av funksjonen må fylle ut med informasjon, og hva funksjonen skal gjøre når den har fått denne informasjonen (uttrykk
). For å lage en funksjon, så bruker vi funksjonen function()
.
funksjonsnavn <- function(argument1, argument2, ...){uttrykk}
De viktigste stegene for å lage en ny funksjon er:
- Gi funksjonen et logisk navn.
- Spesifiser argumenten(e) til funksjonen
function()
. - Legge koden for hva funksjonen skal gjøre skal (
{uttrykk}
) i klammeparantesene som følgerfunction(...)
.
I R finnes det ikke en egen funksjon som finner typetall eller modus. For å illustrere hvordan vi kan lage våre egne funksjoner, viser koden hvordan vi kan lage vår egen funksjon som finner typetallet til et objekt.
# Lager en funksjon som finner modus
modus <- function(x) { # Opretter en funksjon som heter modus
unik <- unique(x) # Finner alle unike verdiene i et objekt
unik[which.max(tabulate(match(x, unik)))] # Matcher verdiene som er like (match), hvor mange ganger de finnes (tabulate) og hvilken verdi som det er flest av (which.max)
}
# Lager en vektor vi vil finne modus for
tall <- c(2,1,2,3,1,2,3,4,1,5,5,3,2,3)
# Finner modus for vektoren tall
modus(tall)
## [1] 2
I R ligger det en funksjon som finner variansen for oss, denne heter var()
. For å illustrere hvordan vi kan lage våre egne funksjoner, viser koden hvordan vi enkelt kan lage vår egen funksjon som finner variansen. Husk at varians er det kvarderte standardavviket (sd()
).
Lag en funksjon som finner variansen til en vektor og kall den varians
.
# Lager en funksjon som heter varians
varians <- function(x){sd(x)^2} # Funksjonen finner standardavviket for objektet x og kvarderer dette
tall
med funksjonen varians()
samt den innbygde funksjonen til R var()
.
# Finner variansen for en vektor som inneholder alle tallene mellom 1 og 5
varians(tall)
# Sjekker at vi får samme output som om vi brukte funksjonen var()
var(tall)
If-setninger
Noen ganger har man bare lyst til at en kode skal kjøres hvis bestemte betingelser er oppfylt Dette kan man få til med hjelp av if
-setninger, hvor man sier “dersom dette er sant, gjøre dette”. Hvis man i tillegg legger til else
, kan man si “dersom dette er sant, gjøre denne tingen og hvis ikke gjør denne andre tingen”.
if(betingelse){
# Kode som kjøres hvis betingelsen er TRUE
} else {
# Kode som kjøres hvis betingelsen er FALSE
}
For å illustrere dette, kan vi kode en vits (hentet fra An Introduction to R).
Partneren til en programmerer sier: Kan du være så snill å gå på butikken og kjøpe en melkekartong og hvis de har egg, kjøp seks.
Programmeren kommer tilbake med seks melkekartonger. Partneren ser dette, og sier: Hvorfor i alle dager kjøpte du seks melkekartonger?
Programmereren sier: De hadde egg.
For å forklare vitsen, kan vi skrive kode med en if-else
-setning:
egg <- TRUE
if(egg == TRUE){ # Hvis det er egg
antall.melk <- 6 # Kjøp seks melkekartonger
} else { # Hvis det ikke er egg
antall.melk <- 1 # Kjøp en melkekartong
}
Så kan vi sjekke antall.melk
for å se hvor mange melkekartonger programmereren kom tilbake med:
I koden under, fyll inn if
-setning som illustrerer følgende setning: Hvis det ikke er melk i butikken, kjøp appelsinjus. Indiker i koden at det ikke er melk i butikken.
melk <- FALSE
# FYLL INN KODE HER
appelsinjus
melk <- FALSE
if(melk == FALSE){
appelsinjus <- 1
} else {
appelsinjus <- 0
}
appelsinjus
Løkker
Hvordan bruke løkker?
På samme måte som funksjoner kan hjelp med å minimere mengden med duplisert kode, kan man bruke løkker når man skal gjøre de samme operasjonene mange ganger. Når man lager en løkke, så vil R gjøre det løkken spesifiserer et bestemt antall ganger eller til en bestemt betingelse er møtt. Hovedtypene av løkker er for
-løkker, while
-løkker og repeat
-løkker. I denne arbeidsboken skal vi fokusere på for
-løkker.
# Et enkelt eksempel
for (i in 1:5){
print(i+1)
}
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
I koden over så har vi brukt en for
-løkke, som vil si at en operasjon blir gjentatt et bestemt antall ganger. Her, har vi sagt at iterator variabelen i
skal iterativt bli byttet ut med hver verdi i vektoren 1:5
, og i klammeparantesene så sier vi at printe i+1.
En for
-løkke vi begynne med den første verdien i sekvensen eller vektoren vi har spesifisert, og så kjøre gjennom som står i klammeparatesene. I dette tilfellet er første verdien i sekvensen vår er 1, så loopen begynner med å bytte ut i
med 1 og legge på 1.
Lag en løkke hvor du sier at for hver iterasjon skal i
byttes ut med tall fra en vektor som inneholder tall mellom 5 og 15. Som output, ønsker du å printe tallene i vektoren minus 2.
for (i in 5:15){
print(i-2)
}
Når man lager løkker må man ha med en iterator variabel, som brukes av løkken og blir tildelt et og et element fra den oppgitte sekvensen eller vektoren for hver løkke. I eksempelet over er iterator variabelen i
, som er en forkortelse for iteration. Selv om i
ofte brukes i løkker, står man fritt til å velge hva iterator variabelen skal hete. (Bortsett fra hvis man har nøstede løkker, hvor man må ha forskjellige navn i hver løkke.)
# Et eksempel som bruker tall og ikke i
for (tall in 2:7){
print(tall*2)
}
## [1] 4
## [1] 6
## [1] 8
## [1] 10
## [1] 12
## [1] 14
Fyll ut koden under for å lage en løkke som deler alle tallene i en sekvens på to. Du velger selv hva du vil kalle iterator variabelen og hvilke tall som skal være med i løkken.
Når burde man bruke og ikke bruke løkker?
apply
-familien
En mer kompakt måte å lage løkker enn å bruke for
-løkker, er å bruke funksjoner fra apply
-familien. Disse kan ofte erstatte mange av oppgavene man koder selv med for
-løkker, samt at de ofte er raskere og at det er mindre sannsynlig at man gjør feil.
Bruksområder for funksjonene i apply
-familien:
apply()
: løkker for datasett eller matriser, vanlig å bruke når man ønsker å gjøre en operasjon på alle rader eller alle kolonner.lapply()
: løkker for lister, datasett, og vektorer.sapply()
: gjør det samme somlapply
, men forenkler outputen.mapply()
: en multivariat versjon avlapply()
.tapply()
: lager grupperte sammendrag, hvor gruppene er oppgitt som faktorer.replicate()
: brukes for å evaluere et uttrykk flere ganger.
apply()
# Subsetter vdem-datasettet til å bare inneholde observasjoner (land) fra 2010
vdem2010 <- vdem %>% filter(year == 2010)
# Bruker apply() og mean() til å finne gjennomsnittlig score for de første 10 landene på alle indikatorene
vdem2010 %>% select_if(., is.numeric) %>% apply(., 2, mean, na.rm = TRUE)
## year v2x_polyarchy v2x_libdem v2x_partipdem v2x_delibdem
## 2010.0000000 0.5267640 0.4133539 0.3417753 0.4247079
## v2x_egaldem v2x_api v2x_mpi v2x_suffr v2xel_frefair
## 0.4036292 0.7036236 0.3499831 0.9850000 0.5360674
lapply()
I kode-chunken under viser ønsker vi å finne gjennomsnittlig score på V-Dems “High-Level Democracy”-indikatorer i 2010 ved å lage en liste.
# Lager en liste som bare inneholder "High-Level Democracy"-indikatorene
dem_high <- list(vdem2010$v2x_polyarchy, vdem2010$v2x_libdem, vdem2010$v2x_partipdem, vdem2010$v2x_delibdem, vdem2010$v2x_egaldem)
# Bruker lapply() og mean() til å finne gjennomsnittlig score på alle indikatorene
lapply(dem_high, mean)
## [[1]]
## [1] 0.526764
##
## [[2]]
## [1] 0.4133539
##
## [[3]]
## [1] 0.3417753
##
## [[4]]
## [1] 0.4247079
##
## [[5]]
## [1] 0.4036292
lapply()
kan også brukes på datasett. Under gjør vi det samme som i kode-chunken over, men på en litt annen måte.
# Gjør samme operasjon, men indekserer datasettet fra 2010 og velger "High-Level Democracy"-indikatorene
lapply(vdem2010[3:8], mean)
## $year
## [1] 2010
##
## $v2x_polyarchy
## [1] 0.526764
##
## $v2x_libdem
## [1] 0.4133539
##
## $v2x_partipdem
## [1] 0.3417753
##
## $v2x_delibdem
## [1] 0.4247079
##
## $v2x_egaldem
## [1] 0.4036292
Bruk lapply()
til å median-verdiene for alle “Mid-Level Democracy”-indikatorene. Disse indikatorene har vi lagt i en liste som heter dem_mid
, se koden under.
# Lager en liste med alle "Mid-Level Democracy"-indikatorene
dem_mid <- list(vdem$v2x_api, vdem$v2x_mpi, vdem$v2x_suffr, vdem$v2x_frefair)
lapply(dem_mid, median)
sapply()
Over fikk vi outputen som en liste, men vi kan også få akkurat samme output som en vektor ved å bruke sapply()
i stedet for lapply()
.
# Bruker sapply() og mean() til å finne gjennomsnittlig score på alle indikatorene
sapply(dem_high, mean)
## [1] 0.5267640 0.4133539 0.3417753 0.4247079 0.4036292
mapply()
# Bruker mapply() og mean() til å finne gjennomsnittlig score på alle indikatorene
mapply(median, na.rm = TRUE, dem_high)
## [1] 0.5195 0.3760 0.3200 0.3860 0.3360
mapply(rep, times = 1:4, MoreArgs = list(x = 42))
## [[1]]
## [1] 42
##
## [[2]]
## [1] 42 42
##
## [[3]]
## [1] 42 42 42
##
## [[4]]
## [1] 42 42 42 42
tapply()
Dersom man ønsker å få output fra en funksjon basert på nivåer i en faktorvariabel, kan man bruke tapply()
. I eksemplet under ønsker vi sammenligne gjennomsnittlig Elecotral democracy index for observasjoner etter den kalde krigen med observasjoner fra før den kalde krigens slutt.
# Lager en faktorvariabel som indikerer om det en observasjon er fra etter den kalde krigen (1) eller før/under (0)
vdem <- vdem %>% mutate(post_cold_war = ifelse(year > 1991, "Post-CW", "Not post-CW"),
post_cold_war = as.factor(post_cold_war))
# Bruker tapply() for å regne ut gjennomsnittet for Electoral democracy index (v2x_polyarchy) før og etter den kalde krigens slutt
tapply(vdem$v2x_polyarchy, vdem$post_cold_war, mean, na.rm = TRUE)
## Not post-CW Post-CW
## 0.1964808 0.5068927
Finn gjennomsnittlig score på Electoral democracy index gruppert etter observasjonenes verdi på Candidate restriction (v2elrstrct), som indikerer om det er restriksjoner for hvem som kan stille til valg basert på etnisitet, religion eller språk (0 = ja, 1 = nei). Variabelen er allerede blitt gjort om til en faktorvariabel.
tapply(vdem$v2x_polyarchy, vdem$v2elrstrct, mean, na.rm = TRUE)