Sesjon 2: Datatyper, objekter og funksjoner

R-workshop 30. og 31. august, OsloMet

Vi har tre hovedtyper av dataformater i R:

  • vektorer
  • datamatriser
  • lister

Vektorer

Definer en vektor

Vi kan lage en vektor med kommandoen c() (kortform for “concatenate”).

c(1, 2, 3, 4)
## [1] 1 2 3 4

Hver observasjon separeres med komma. Vær nøye med å lukke parentesen. Det er slik R forstår at du er ferdig med å snakke, og at det er R sin tur til å svare. Om du markerer den ene delen av en parentes, vil RStudio vise deg hvor tvillingen befinner seg.

Engelske komma Desimalskilletegnet i R er punktum ..

c(1.2, 3.2, .0)
## [1] 1.2 3.2 0.0

Lagre vektoren vår i et objekt

hoyre <- c(3.75, 7.5, 7.25, 6, 2.25, 4, 1.5, 6.75)
hoyre
## [1] 3.75 7.50 7.25 6.00 2.25 4.00 1.50 6.75

Vektorer kan ha ulike målenivå: tall (“numeric”), bokstaver (“character”) og pre-definerte kategorier (“factor”).

Når vi lager en bokstavvektor, må vi “pakke inn” ordene i hermetegn

parti<-c("Ap", "FrP", "H", "KrF", "MDG", "Sp","SV", "V")
parti
## [1] "Ap"  "FrP" "H"   "KrF" "MDG" "Sp"  "SV"  "V"

Matematiske operasjoner

Vi kan gjøre matematiske operasjoner på vektorer også. Her øker jeg alle verdier med to.

hoyre
## [1] 3.75 7.50 7.25 6.00 2.25 4.00 1.50 6.75
hoyre + 2
## [1] 5.75 9.50 9.25 8.00 4.25 6.00 3.50 8.75

Skal du beholde hva du nettopp gjorde, må du ta vare på resultatet

hoyre_omk <- hoyre + 2
hoyre_omk
## [1] 5.75 9.50 9.25 8.00 4.25 6.00 3.50 8.75

R gir deg beskjed om målenivået er feil.

parti + 2

## Error in parti + 2 : non-numeric argument to binary operator

Indeksering

base-R følger matematiske prinsipper. Dermed er indekseringen symmetrisk med hva dere allerede kan. I tidyverse vil du bruke andre teknikker for å hente ut observasjoner.

Tredje observasjon

parti
## [1] "Ap"  "FrP" "H"   "KrF" "MDG" "Sp"  "SV"  "V"
parti[3]
## [1] "H"

Jeg kan også be om å få se observasjonene 1 og 3. Da oppretter jeg i praksis en vektor med observasjonsnumrene inni hakeparentesen.

  parti[c(1,3)]
## [1] "Ap" "H"

Andre og tredje

parti[c(1,2)]
## [1] "Ap"  "FrP"

Andre til og med tredje

parti[1:3]
## [1] "Ap"  "FrP" "H"

Funksjoner

Funksjoner er verbene i R-språket.

Hver funksjon har et navn og krever som et minimum at du oppgir hva du skal gjøre operasjonen på (objektet). Dette oppgir vi i parentes etter navnet (slik er syntaksen).

Generelt trenger du å oppgi:

  • navnet på funksjonen
  • et argument: obligatorisk informasjon i parantes; i det minste objektet og
  • eventuelle tilleggsargumenter

Her summerer jeg på to forskjellige vis:

#på gamlemåten
2+2
## [1] 4
#med en funksjon på en vector
sum(c(2,2))
## [1] 4

Argument Hva er summen av alle tallene i vektoren min?

sum(x = hoyre)
## [1] 39

Implisitt argument Du kan av og til skippe navnet på argumentet

sum(hoyre)
## [1] 39

Tilleggsargument Eksempelvis, fjerne NA.

sum(hoyre, na.rm = TRUE)
## [1] 39

Funksjoner er grunnsteinen i R-språket sammen med objektene. De er i praksis pre-definerte operasjoner som du gjør på en viss type objekter; hvorav verb-analogien.

Hvor finner jeg funksjoner?

Du kan få enkle og generelle, eller spesifikke og/eller infløkte funksjoner.

  • base-R: har latterlig mange funksjoner. De kommer som legoklosser og er ofte enkle og generelle. Du kan alltid si hva du vil med base-R, men det kan kreve mange kodelinjer og tar av og til lang tid (særlig hvis du skriver løkker).

  • R-pakker distribusjon av funksjoner er kjerneoppgaven til “R-pakkene”. Dette er årsaken til at R er blitt et ledende programspråk. Kildekoden er åpen, slik at alle kan skrive funksjoner, “pakke dem inn” og dele dem. Når en ny metode blir introdusert til forskere/analytikere, vil mange benytte sjansen til å bygge sin forskningsmessige merkevare ved å skrive en egen R-pakke med de relevante funksjonene.

  • du selv Du kan fint skrive din R-funksjon og ta vare på den i et R-objekt. Jeg har flere slike. De befinner seg sammen med forskningsdataene mine i private R-pakker. Den jeg bruker mest, heter silje.

Repeter tall

rep(x = 1, times = 5)
## [1] 1 1 1 1 1

sekvens av tall

seq(from = 1, to = 5)
## [1] 1 2 3 4 5

Med tilleggsargument

seq(from = 1, to = 5, by = 0.5)
## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0

Utforsk med funksjoner

Lengde

length(hoyre)
## [1] 8

Målenivå

class(parti)
## [1] "character"
class(hoyre)
## [1] "numeric"

Din tur

Bruk indekseringen for å omkode variabelene.

  • bruk mean() for å regne ut gjennomsnitlig høyreorientering for regjeringspartiene.
  • omkod Arbeiderpartiet til Kystpartiet.
  • omkod MDG til 3.
  • snu skalaretningen og regn ut ny gjennomsnitlig høyreorientering

Matriser

Vi kan ha alle slags objekter i R-området vårt, men datamatriser er fortsatt selve grunnsteinen i R. Siden datamatrisene også bare er et R-objekt, kan du ha mange datasett framme i arbeidsområdet samtidig.

Vi skal se på indeksering: * nyttig for omkoding + beskrivende statistikk * målenivå; også viktig når vi omkoder

Definer en matrise

Du vil oppleve at rutearkene dine kan klassifiseres på tre ulike vis:

  • matrise (et ruteark med info; base R)
  • datasett (dataframe; base R). R vet at kolonnene dine er variabler og lar deg gjøre analyser på den.
  • datasett med penere printfunksjon (tibble; tidyverse).

Matriser er i praksis to eller flere vektorer som er limt sammen kolonnevis.

df <- cbind(parti, hoyre)

Hvordan ser objektet vårt ut?

df
##      parti hoyre 
## [1,] "Ap"  "3.75"
## [2,] "FrP" "7.5" 
## [3,] "H"   "7.25"
## [4,] "KrF" "6"   
## [5,] "MDG" "2.25"
## [6,] "Sp"  "4"   
## [7,] "SV"  "1.5" 
## [8,] "V"   "6.75"

cbind() (og tvillingen rbind()) binder vektorer sammen. Om vektorene ikke er like lange, vil R begynne å “resirkulere”, men vil gi den en “advarsel”.

annen <- c(1,2,3)
cbind(df, annen)
## Warning in cbind(df, annen): number of rows of result is not a multiple of
## vector length (arg 2)
##      parti hoyre  annen
## [1,] "Ap"  "3.75" "1"  
## [2,] "FrP" "7.5"  "2"  
## [3,] "H"   "7.25" "3"  
## [4,] "KrF" "6"    "1"  
## [5,] "MDG" "2.25" "2"  
## [6,] "Sp"  "4"    "3"  
## [7,] "SV"  "1.5"  "1"  
## [8,] "V"   "6.75" "2"

Advarsler (“Warning”) er noe annet en “Error”. Begge kommer med rød skrift, men en feil betyr at operasjonen ikke ble gjennomført. En advarsel flagger mulige feil.

R skiller mellom matriser og datasett (data.frame). Dette er foreløpig en matrise.

class(df)
## [1] "matrix" "array"

Datasett Vi kan omdefinere matrisen til et datasett. Da får bl.a. hver variabel et definert målenivå, og indekseringen går enklere.

df <- as.data.frame(df)
class(df)
## [1] "data.frame"
df

Dimensjonen på datasettet

dim(df)
## [1] 8 2

Hvilke variabler har jeg?

names(df)
## [1] "parti" "hoyre"

Hva er første variabel?

names(df)[1]
## [1] "parti"

Se dataene Du kan åpne dataene i et eget vindu.

View(df)

Indeksere

Indeksering av data matriser følger vanlige matematisk notasjon: matrise[rad,kolonne]

Jeg kan inspisere den første observasjonen i første kolonne.

df[1,1]
## [1] "Ap"

… første linje (observasjon)

df[1,]

Tre synonymer

…første kolonne (variabel)

df[,1]

… parti-variabelen med matriseindeksering (== første kolonne)

df[,"parti"]
## [1] "Ap"  "FrP" "H"   "KrF" "MDG" "Sp"  "SV"  "V"

… parti-variabelen med indeksering for datasett

df$parti
## [1] "Ap"  "FrP" "H"   "KrF" "MDG" "Sp"  "SV"  "V"

Vi må alltid oppgi navnet på datasettet (objektet) + dollartegn ($) + variabelnavn. Dette er prisen for å ha flere dataobjekter oppe samtidig.

Betinget omkoding

Det er også mulig å trekke fram én eller flere observasjoner basert på verdiene til en gitt variabelverdi.

Hvilke observasjoner skårer seks på høyreorientering?

df$hoyre == 6
## [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE

Svaret er alltid like langt som dataene du putter inn.

Hvilket observasjonsnummer har de?

which(df$hoyre == 6)
## [1] 4

Hvilket parti skårer 6?

df[df$hoyre==6, "parti"]
## [1] "KrF"
#evt
df[which(df$hoyre == 6), "parti"]
## [1] "KrF"

Nå kan jeg se alle partier som skårer 6 eller høyere

df$parti[df$hoyre >= 6]
## [1] "FrP" "H"   "KrF" "V"

… da er jeg ett skritt fra å omkode. Her lager jeg en binær variabel for høyreorientering.

df$hoyre_bin = NA
df$hoyre_bin[df$hoyre >= 6] = 1
df$hoyre_bin[df$hoyre < 6] = 0

Hvor mange partier ble klassifisert som høyreorientert?

sum(df$hoyre_bin)
## [1] 4

Dette er mye skriving for å oppnå en omkoding. Det finnes andre måter å gjøre det på, men R er alltid basert på TRUE/FALSE gjetteleken.

Jeg kan også indeksere på bokstavverdier

df[df$parti == "KrF", "hoyre"]
df$hoyre[df$parti == "KrF"]

Ordne data Jeg kan ordne dataene fra lav til høy verdi

#Rangeringen til hvert parti
order(df$hoyre)
#Bruk denne i indekseringen
df[order(df$hoyre),]

Her sorterer jeg etter to variabler

df[order(df$parti, df$hoyre),]

Husk at denne sorteringen blir ikke lagret, med mindre vi oppretter et nytt dataobjekt.

nyedata <- df[order(df$parti, df$hoyre),]
nyedata

Målenivå

Målenivået til variabelen avgjør hvilke operasjoner du kan gjøre på den; som i det virkelige liv.

To regler:

  • Sjekk alltid omkodingen før du overskriver den originale variabelen.
  • Definer alltid en variabel som bokstaver før du omkoder dem til numre

Numeriske variabler (“Numeric”):

Når dataene ser ut som tall, vil R stort sett klassifisere dem som tall.

df$hoyre
## [1] "3.75" "7.5"  "7.25" "6"    "2.25" "4"    "1.5"  "6.75"
is.numeric(df$hoyre)
## [1] FALSE
as.numeric(df$hoyre)
## [1] 3.75 7.50 7.25 6.00 2.25 4.00 1.50 6.75

Om du forsøker å definere en bokstavvariabel til tall, vil hele vektoren forvandles til (“not available”).

Bokstavvariabler (“Character”):

Skrift kan defineres som skrift.

as.character(df$hoyre)
## [1] "3.75" "7.5"  "7.25" "6"    "2.25" "4"    "1.5"  "6.75"

Jeg har som absolutt regel at jeg alltid går via bokstavdefinisjonen når jeg skal omdefinere variabler fra ett målenivå til et annet. Årsaken henger sammen med R sin bruk av kategoriske variabler.

Kategoriske variabler - (“Factor”):

Faktorer/kategorier er en måte for R å spare plass på: Informasjon blir lagret som kategorier. Det betyr at teksten eller tallene som befant seg i ruta, vil bli lagret som en egen kategori, mens hver observasjon får et internt kategorinummer.

as.factor(df$parti)
## [1] Ap  FrP H   KrF MDG Sp  SV  V  
## Levels: Ap FrP H KrF MDG Sp SV V

Vi kan omrokkere nivåene.

factor(df$parti, 
       levels=c("FrP", "H", "V", "KrF", "Sp", "Ap", "MDG", "SV"))
## [1] Ap  FrP H   KrF MDG Sp  SV  V  
## Levels: FrP H V KrF Sp Ap MDG SV

Om vi vil opprette en ordinal variabel, må vi oppgi rekkefølgen fra lav til høy verdi.

ordered(df$parti, 
        levels=c("SV", "MDG", "Ap", "Sp", "KrF", "V", "H", "FrP"))
## [1] Ap  FrP H   KrF MDG Sp  SV  V  
## Levels: SV < MDG < Ap < Sp < KrF < V < H < FrP

En felle

as.factor(df$hoyre)
## [1] 3.75 7.5  7.25 6    2.25 4    1.5  6.75
## Levels: 1.5 2.25 3.75 4 6 6.75 7.25 7.5

…er ikke det samme som…

as.numeric(as.factor(df$hoyre))
## [1] 3 8 7 5 2 4 1 6

Trikset er å gå via bokstavdefinisjonen

as.numeric(as.character(as.factor(df$hoyre)))
## [1] 3.75 7.50 7.25 6.00 2.25 4.00 1.50 6.75

Da blir tallverdiene i kategorinavnene lest, og ikke det interne registreringsnummeret.

Lister

Lister er objekter med ustrukturert format: I praksis objekter som er limt sammen.

  • vi bruker dem ikke i stor grad
  • R har mange av dem: f.eks modellobjektene våre.

De er svært nyttige når vi skal rapportere resultatene våre.

Opprette en liste

Vi kan starte med å opprette en liste.

liste<-list(hoyre=c("FrP", "H", "V"),
            sentrum=c("Sp", "KrF", "MDG"),
            venstre=c("Ap", "SV"))
liste
## $hoyre
## [1] "FrP" "H"   "V"  
## 
## $sentrum
## [1] "Sp"  "KrF" "MDG"
## 
## $venstre
## [1] "Ap" "SV"

Indekseringen er i prinsippet den samme som tidligere, men denne gangen kan flere objekttyper være limt sammen.

Indeksering

Hvert listeelement kan kalles opp ved navn ved hjelp av dollartegn eller med [[j]]. Internt i hvert element må vi bruke indekseringen som passer til elementets type.

Denne listen består av tre vektorer ved navn hoyre, sentrum og venstre.

liste[[1]]
## [1] "FrP" "H"   "V"
liste$hoyre 
## [1] "FrP" "H"   "V"

Observasjonene er partier. Disse kan hentes fram ved hjelp av et annet sett med indekser.

liste[[1]][1]
## [1] "FrP"
liste$hoyre[1]
## [1] "FrP"

Det er til og med mulig å lime sammen en datamatrise med en vektor.

nyliste<-list(datamatrise = df, 
              orientering = c("hoyre", "sentrum", "venstre"))
nyliste
## $datamatrise
##   parti hoyre hoyre_bin
## 1    Ap  3.75         0
## 2   FrP   7.5         1
## 3     H  7.25         1
## 4   KrF     6         1
## 5   MDG  2.25         0
## 6    Sp     4         0
## 7    SV   1.5         0
## 8     V  6.75         1
## 
## $orientering
## [1] "hoyre"   "sentrum" "venstre"

Lista mi har to objekter/forgreininger:

names(nyliste)
## [1] "datamatrise" "orientering"

Disse kan jeg gripe tak i på tre ulike vis

#take 1
nyliste$datamatrise
# take 2
nyliste[[1]]
# take 3
nyliste[["datamatrise"]]

Jeg kan indeksere matrisen som henger på lista på vanlig vis.

nyliste$datamatrise[1,1]
## [1] "Ap"
nyliste$datamatrise[,1]
## [1] "Ap"  "FrP" "H"   "KrF" "MDG" "Sp"  "SV"  "V"