Sesjon 9: Tabeller
R-workshop 10. oktober, OsloMet
R er utmerket for å lage tabeller. Dette foregår i to skritt:
Vi begynner med å lage en tabell i R. Resultatet er et lite datasett (et R-objekt). Til dette, er pakkene
dplyr
ogtidyr
veldig nyttige.Deretter lager vi en tabell som siden kan eksporteres. Her finnes det mange pakker som kan hjelpe oss. En slik pakke er
kableExtra
.
Vi begynner med å laste inn pakkene i arbeidsområdet vårt.
library(kableExtra); library(dplyr); library(tidyr)
Vi laster så inn ESS-dataene fra forrige sesjon (med mindre vi fortsatt har dem).
load(
url("https://siljehermansen.github.io/teaching/oslomet/kap7.rda")
)<- kap7 df
Krysstabeller
Vi husker fra forrige gang at vi kunne lage gruppegjennomsnitt med
funksjonen group_by
i dplyr.
Bivariat tabell
Her regner jeg ut gjennomsnitlig innvandringsskepsis i majoritetsbefolkningen og blant innvandrere, respektivt.
%>%
df group_by(Innvandrer) %>%
summarize(Innvandringsskepsis = mean(Skepsis, na.rm = TRUE))
Vi ser at huller i innvandrer-dataene (NA) blir til en egen kategori.
Om jeg ikke ønsker dette, må jeg filtrere ut disse observasjonene ved
hjelp av filter()
før jeg foretar resten av
operasjonene.
%>%
df filter(!is.na(Innvandrer)) %>%
group_by(Innvandrer) %>%
summarize(Innvandringsskepsis = mean(Skepsis, na.rm = TRUE))
Jeg kan lage flere slike sammendrag. Merk deg hvordan jeg pakker inn de nye kolonnenavnene i hermetegn hvis jeg ønsker å bruke mellomrom i navnene.
%>%
df filter(!is.na(Innvandrer)) %>%
group_by(Innvandrer) %>%
summarize("Innvandringsskepsis" = mean(Skepsis, na.rm = TRUE),
"Prekær arbeidslivstilknytning" = mean(Prekaritet, na.rm = T))
Her ser vi at innvandrere jevnt over er mindre skeptiske til innvandring, men også mer oftere i en usikker arbeidssituasjon.
Trivariat tabell
Om vi tror at en respondents arbeidssituasjon har en innvirkning på holdningen deres til innvandring, vil vi være interessert i å lage en trivariat tabell.
En måte å gjøre dette på, er å legge til enda en grupperingskategori. Da får vi gruppegjennomsnitt med to grupperingsvariabler.
%>%
df group_by(Innvandrer, Prekaritet) %>%
summarize("Innvandringsskepsis" = mean(Skepsis, na.rm = TRUE))
## `summarise()` has grouped output by 'Innvandrer'. You can override using the
## `.groups` argument.
Om vi ønsker å fjerne NA-kategoriene, må vi filtrere igjen. Denne
gangen legger jeg inn to betingelser: Jeg skal ikke ha NA i variabelen
for Innvandrer (!is.na()
) og (&
)
ikke i arbeidsvariabelen min.
%>%
df filter(!is.na(Innvandrer) & !is.na(Prekaritet)) %>%
group_by(Innvandrer, Prekaritet) %>%
summarize("Innvandringsskepsis" = mean(Skepsis, na.rm = TRUE))
## `summarise()` has grouped output by 'Innvandrer'. You can override using the
## `.groups` argument.
Nå har jeg fire verdier for innvandringsskepsis som jeg er interessert i. Allerede her kan vi se at innvandringsskepsis blant majoritetsbefolkningen er høyere hos de med en prekær tilknytning til arbeidslivet (de to første linjene). Det motsatte er tilfelle for innvandrere!
Hvor mange observasjoner er det i hver kategori? Da kan jeg bruke
n()
for å telle antall observasjoner. Her lagrer jeg også
tabellen i sitt eget objekt.
<-
tabell %>%
df filter(!is.na(Innvandrer) & !is.na(Prekaritet)) %>%
group_by(Innvandrer, Prekaritet) %>%
summarize("Innvandringsskepsis" = mean(Skepsis, na.rm = TRUE),
"Antall" = n())
## `summarise()` has grouped output by 'Innvandrer'. You can override using the
## `.groups` argument.
tabell
Det er relativt krevende å lese denne tabellen. Vanligvis ønsker vi å foreta sammenlikningen kolonnevis, ikke per linje, slik som her.
Jeg begyner med å transposere tabellen: Jeg flipper tabellen slik at
kolonner og linjer bytter plass med t()
.
<- t(tabell)
tabell tabell
## [,1] [,2] [,3] [,4]
## Innvandrer 0.000000 0.00000 1.000000 1.000000
## Prekaritet 0.000000 1.00000 0.000000 1.000000
## Innvandringsskepsis 4.388755 4.98325 4.303855 4.007576
## Antall 849.000000 204.00000 151.000000 47.000000
Nå kan jeg velge med de linjene jeg ønsker å bruke videre. He er det linje 3 og 4, som rapporterer innvandringsskepsis og antall observasjoner.
<- tabell[3:4,] tabell
Jeg bruker kableExtra
- pakken for a pimpe denne
tabellen. Basisfunksjonen er kbl()
som etablerer en
tabell.
kbl(tabell)
Innvandringsskepsis | 4.388755 | 4.98325 | 4.303855 | 4.007576 |
Antall | 849.000000 | 204.00000 | 151.000000 | 47.000000 |
Tabellen dukker opp i “Viewer”-vinduet i RStudio. Foreløpig er den vanskelig å lese.
Her ber jeg om maks to desimaler i resultatene. Siden dette er en norsk tabell, endrer jeg også desimalskilletegnet til komma ved et formateringsargument. Merk deg at svaret må presiseres i en liste.
I tillegg gir jeg tabellen min en tittel.
<-
tab_trivariat kbl(tabell,
#Desimaler
digits = 2,
#Komma i stedet for punktum
format.args = list(decimal.mark = ","),
#Tittel
caption = "Innvandringsskepsis og okonomisk utrygghet")
tab_trivariat
Innvandringsskepsis | 4,39 | 4,98 | 4,3 | 4,01 |
Antall | 849,00 | 204,00 | 151,0 | 47,00 |
Siden jeg er fornøyd med resultatet, tar jeg vare på det i et nytt objekt.
Nå er det på tide å gi noen kolonnenavn til tabellen. Jeg begynner med arbeidssituasjonen.
<-
tab_trivariat %>%
tab_trivariat #Kolonneoverskrifter: Antall kolonner + radnavn. Her spenner de to siste over to kolonner hver.
add_header_above(header = c("",
"Ikke-prekær", "Prekaer",
"Ikke-prekær", "Prekaer"))
tab_trivariat
Innvandringsskepsis | 4,39 | 4,98 | 4,3 | 4,01 |
Antall | 849,00 | 204,00 | 151,0 | 47,00 |
Dette ser lovende ut, men jeg har nå fire kolonner, med like kolonnenavn. Det er fordi vi ikke har lagt til den siste grupperingsvariabelen: Innvandrerstatus. Det gjør jeg nå.
Rekkefølgen i kodene er viktige. Det siste kolonnenavnet kommer øverst i tabellen.
kableExtra
gjør det mulig å slå sammen kolonnenavn. Det
er hva jeg er interessert i her.
<-
tab_trivariat %>%
tab_trivariat #Kolonneoverskrifter: Antall kolonner + radnavn. Her spenner de to siste over to kolonner hver.
add_header_above(header = c("" ,
"Norskfødt" = 2 ,
"Innvandrer" = 2))
tab_trivariat
Innvandringsskepsis | 4,39 | 4,98 | 4,3 | 4,01 |
Antall | 849,00 | 204,00 | 151,0 | 47,00 |
Jeg presiserer at den første kolonnen (med variabler) ikke trenger en overskrift. Deretter sier jeg at de to neste kolonnene skal ha én felles overskrift (“Norskfødt”), osv.
Nå kan jeg “style” tabellen. Her har jeg valgt en funksjon for “klassiske” design.
På dette tidspunktet, er det på tide å eksportere tabellen. Her presiserer jeg at jeg ønsker en html-tabell som skal skrives ut til arbeidsområdet mitt. Denne gangen lagerer jeg ikke utputtet i et objekt.
Jeg kan nå gå til arbeidsområdet mitt, og åpne tabellen i nettleseren min.
Om du har glemt hvor arbeidsområdet ditt er, kan du spørre R.
getwd()
## [1] "C:/Users/ssherman/Dropbox/Teaching/OsloMET"
Beskrivende statistikk
Vi ønsker ofte å se en beskrivende statistikk i forkant av en regresjonsanalyse. Dette er nyttig for å bli kjent med dataene, men også i tolkningen av resultatene.
Her finnes det en lett og en mer omstendelig måte å gjøre det på.
Enkle tabeller med beskrivende statistikk.
stargazer
-pakken genererer automatisk tabeller med
beskrivende statistikk for oss.
Om jeg ikke har hentet den fra biblioteket ennå, gjør jeg det nå.
library(stargazer)
##
## Please cite as:
## Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
## R package version 5.2.3. https://CRAN.R-project.org/package=stargazer
Tabellen bruker alle variablene i datasettet med mindre jeg oppgir noe annet. De fleste datasett er så store at vi ikke ønsker å bruke alt som finnes. Her velger jeg ut variablene jeg ønsker først og lagerer dem i et eget objekt.
<-
tabell_data %>%
df ::select(Skepsis, Prekaritet,
dplyr
Innvandrer, Inntekt, SubjektivInntekt)
Deretter putter jeg det nye dataobjektet direkte inn i stargazer. Funksjonen vil gi meg en latex-kode om jeg ikke presiserer annet, så her har jeg bedt om å se resultatet som tekst.
stargazer(tabell_data,
type = "text")
Statistic | N | Mean | St. Dev. | Min | Max |
Skepsis | 1,396 | 4.452 | 1.506 | 0.000 | 9.333 |
Prekaritet | 1,253 | 0.200 | 0.400 | 0 | 1 |
Innvandrer | 1,434 | 0.158 | 0.364 | 0 | 1 |
Inntekt | 1,371 | 5.322 | 2.830 | 1 | 10 |
SubjektivInntekt | 1,432 | 2.554 | 0.666 | 0 | 3 |
Jeg kan velge hvilken statistikk jeg ønsker å rapportere. Her har jeg valgt å se kvartilene, samt gjennomsntittet og antall observasjoner i hver variabel.
Statistic | Min | Pctl(25) | Mean | Median | Pctl(75) | Max | N |
Skepsis | 0.000 | 3.333 | 4.452 | 4.333 | 5.333 | 9.333 | 1,396 |
Prekaritet | 0 | 0 | 0.200 | 0 | 0 | 1 | 1,253 |
Innvandrer | 0 | 0 | 0.158 | 0 | 0 | 1 | 1,434 |
Inntekt | 1 | 3 | 5.322 | 5 | 8 | 10 | 1,371 |
SubjektivInntekt | 0 | 2 | 2.554 | 3 | 3 | 3 | 1,432 |
Hjemmelagede tabeller
Man kan også gjøre disse tingene “for hånd” med piper. Det er en god unskyldning for å lære å gå tur-retur fra bredt til langt dataformat.
I denne tabellen ønsker jeg å rapportere statistikk for hver variabel langs hver linje. Jeg begynner med å definere hvilke variabler og statistikk jeg skal bruke. Noter deg at jeg lager en vektor med variabelnavn, men en liste med funksjoner. Det er på grunn av hvordan funksjonen min vil lese informasjonen.
<- c("Skepsis",
variabler "Innvandrer",
"Prekaritet",
"Inntekt")
<- list(min, mean, median, max) funksjoner
Vanligvis vil summarize()
gi statistikk kun for én
variabel av gangen. Her finner jeg gjennomsnittet av
innvandringsskepsis.
%>%
df summarize(Skepsis = mean(Skepsis, na.rm = T))
Om vi ønsker statistikk for to variabler, vil vi vanligvis måtte gjenta koden ved hjelp av litt “copy-paste”.
%>%
df summarize(Skepsis = mean(Skepsis, na.rm = T),
Innvandrer = mean(Innvandrer, na.rm = T))
Det blir tungvint i lengden.
Sammendrag for flere variabler Vi kan gjøre den
samme operasjonen på flere variabler samtidig ved hjelp av
across()
. Den krever at vi forteller hvilke variabler som
skal behandles (.col =
), samt hvilken funksjon som skal
brukes (.fun =
). I dette tilfellet, må jeg også komme med
enda et tilleggsargument: Hvordan NA skal behandles.
%>%
df summarize(across(.cols = c(Skepsis, Innvandrer),
mean, na.rm = T))
I stedet for å liste opp variablene, kan jeg bruke objektet jeg lagde med alle variabelnavnene, samt objektet med alle funksjonene jeg skal bruke.
Siden jeg er fornøyd med resultatet, tar jeg vare på det i et objekt. Om du heller ønsker å fortsette pipen, kan du det.
<-
tabell %>%
df summarize(across(.cols = variabler,
funksjoner, na.rm = T))
## Note: Using an external vector in selections is ambiguous.
## ℹ Use `all_of(variabler)` instead of `variabler` to silence this message.
## ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
## This message is displayed once per session.
tabell
Den første kolonnen rapporterer resultatet fra den første funksjonen
(min()
) for den første variabelen (Skepsis), deretter den
andre funksjonen (mean()
) for den første variabelen
osv.
Dette er et datasett i bredt format. Ideelt sett har jeg lyst til å ha alle gjennomsnittene i en variabel, osv. Det vil si at jeg ønsker et datasett i langt format.
Her kommer tidyr
-pakken godt med: Den inneholder en
funksjon nettopp til dette: pivot_longer()
. Som et minimum,
må jeg oppgi hvilke variabler som skal settes sammen. Her har jeg valgt
alle variabler.
<-
tabell %>%
tabell pivot_longer(everything())
tabell
Resultatet er en tabell med to kolonner. R har selv valgt å legge
alle variabelnavn inn i en ny variabel (name
) hvor verdiene
for hver observasjon er i value
.
Nå kan jeg lage to nye variabler. En som presiserer hvilken funksjon jeg brukte for de ulike kategoriene, og en som forteller hvilken variabel jeg har laget sammendrag av.
Til dette bruker jeg gsub()
funksjonen: Første argument
oppgir hvilket regelmessig uttrykk som flagger funksjonen (her er det et
tall), deretter oppgir jeg hva jeg ønsker å erstatte verdien med.
<-
tabell %>%
tabell mutate(stat = gsub(".*_1", "Minimum", name),
stat = gsub(".*_2", "Gjennomsnitt", stat),
stat = gsub(".*_3", "Median", stat),
stat = gsub(".*_4", "Maksimum", stat),
Variabel = gsub("_.*", "", name))
tabell
Jeg plukker ut de variablene jeg ønsker å beholde, så er det på tide å reformatere til et bredt format igjen. Jeg ønsker en ny linje per variabel i datasettet, men en ny kolonne per nye funksjon jeg har brukt.
pivot_wider()
hjelper meg med dette. Denne gangen kan
jeg opplyse om hvilken variabel som har kategorier som også egner seg
som kolonner. I dette tilfellet, er det stat
-
variabelen.
<-
tabell %>%
tabell select(Variabel, value, stat) %>%
pivot_wider(names_from = stat)
tabell
Voilà! Nå har jeg en tabell med statistikken jeg ønsker, og jeg kan begynne med de estetiske tweaksene.
Jeg begynner med å omdefinere beskrivelsen av variablene.
$Variabel <- c("Innvandringsskepsis (0-10)",
tabell"Innvandrer (0 eller 1)",
"Utrygg tilknytning til arbeidslivet (0 eller 1)",
"Inntekt (desiler; 1-10)")
tabell
kbl(tabell,
digits = 1,
format.args = list(decimal.mark = ","),
caption = "Beskrivende statistikk") %>%
kable_classic(full_width = F,
html_font = "Cambria") %>%
column_spec(1,
italic = T)
Variabel | Minimum | Gjennomsnitt | Median | Maksimum |
---|---|---|---|---|
Innvandringsskepsis (0-10) | 0 | 4,5 | 4,3 | 9,3 |
Innvandrer (0 eller 1) | 0 | 0,2 | 0,0 | 1,0 |
Utrygg tilknytning til arbeidslivet (0 eller 1) | 0 | 0,2 | 0,0 | 1,0 |
Inntekt (desiler; 1-10) | 1 | 5,3 | 5,0 | 10,0 |
Om jeg er fornøyd med hva jeg har gjort, kan jeg nå skrive ut tabellen.
kbl(tabell,
digits = 1,
format.args = list(decimal.mark = ","),
caption = "Beskrivende statistikk") %>%
kable_classic(full_width = F,
html_font = "Cambria") %>%
column_spec(1,
italic = T) %>%
save_kable(file = "trivariat_tabell.html")
Her er hele pipa
%>%
df summarise(across(variabler,
(funksjoner), na.rm = T)) %>%
pivot_longer(everything(),
values_to = c("value"),
names_to = c("name")) %>%
mutate(stat = gsub(".*_1", "Min", name),
stat = gsub(".*_2", "Gjennomsnitt", stat),
stat = gsub(".*_3", "Median", stat),
stat = gsub(".*_4", "Maks", stat),
Variabel = gsub("_.*", "", name)) %>%
select(Variabel, value, stat) %>%
pivot_wider(names_from = stat)