library(tidyverse)
library(gtsummary)
8 Deskriptive tabeller
Det kan være ulike grunner til å lage deskriptiv statistikk, og hva du skal bruke tabellene til kan ha betydning for hvordan du lager dem. Noen ganger skal du bare sjekke noen tall, og da er det ingen grunn til å bruke tid på å gjøre tabellen spesiell pen. Andre ganger skal tabellen publiseres i en rapport, på en nettside eller i en vitenskapelig artikkel - eller mest aktuelt på kort sikt: i en masteroppgave. Da må tabellene se ordentlige ut. Nedenfor skal vi se på begge mulighetene.
8.1 Quick-and-dirty oppsummeringer
Først og fremst har vi funksjonen summary()
. Når denne brukes på et objekt vil hva slags output du får avhenge av objekttypen. Derfor vil summary()
gi forskjellig output om det er en vektor, et datasett eller et regresjonsobjekt etc. Vi avgrenser oss til datasett her.
Her er output for hele datasettet.
summary(abu89)
io_nr time89 ed age
Min. : 3 Min. : 25.00 Min. : 0.00 Min. :16.00
1st Qu.:1542 1st Qu.: 71.00 1st Qu.: 1.00 1st Qu.:30.00
Median :3093 Median : 83.33 Median : 3.00 Median :39.00
Mean :3105 Mean : 90.15 Mean : 2.69 Mean :39.65
3rd Qu.:4644 3rd Qu.:102.56 3rd Qu.: 3.00 3rd Qu.:48.00
Max. :6258 Max. :343.75 Max. :11.00 Max. :74.00
NA's :368
female klasse89 promot fexp
Min. :0.0000 I Øvre serviceklasse : 328 NEI:2568 Min. :0.0000
1st Qu.:0.0000 II Nedre serviceklasse :1181 JA :1559 1st Qu.:0.2000
Median :0.0000 III Rutinefunksjonærer :1248 Median :0.7000
Mean :0.4686 V-VI Faglærte arbeidere : 648 Mean :0.9451
3rd Qu.:1.0000 VIIa Ufaglærte arbeidere: 637 3rd Qu.:1.4000
Max. :1.0000 NA's : 85 Max. :4.9000
private
Public :1602
Private:2525
Merk at summary()
rapporterer forskjellig basert på om variabelen er kontinuerlig eller kategorisk. For kontinuerlige variable gis min/max, kvartiler, median og gjennomsnitt. For kategoriske variable gis det antall i hver kategori. Hvis det er manglende verdier på en variabel står det oppført nederst som antall NA's
.
Merk her at variabelen female er definert som kontinuerlig selv om det bare er to verdier. Det ville være mer hensiktsmessig å gjøre om denne variabelen til kategorisk.
Man kan også bruke summary()
på enkeltvariable med bruk av $
som følger:
summary(abu89$time89)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
25.00 71.00 83.33 90.15 102.56 343.75 368
Da får man altså bare tallene for den variabelen man har angitt etter dollartegnet.
8.1.1 Enkeltfunksjoner
Man kan hente ut hvert av disse tallene spesifikt fremfor å bruke summary()
. Det er egne funksjoner for dette, og de kan også brukes når man gjør databearbeiding for litt andre formål. Vi ser her på de viktigste.
Hva om man vil ha en kvartil som ikke er oppgitt i forvalget? Da kan man bruke funksjonen quantile()
. Argumentene i denne funksjonen er hvilken variabel og hvilket prosentil. Som vi ovenfor inneholder time89 noen NA. Vi må i tillegg bestemme hva vi ønsker å gjøre med NA i beregningen, og vi vil her se bort fra disse ved å angi na.rm = TRUE
. Ellers får man feilmelding.
Her er eksempel med første kvartil som skal gi samme svar som ovenfor:
quantile(abu89$time89, .25, na.rm = TRUE)
25%
71
Her er en variant der man ber om 95-prosentilen:
quantile(abu89$time89, .95, na.rm = TRUE)
95%
148.0362
Man kan også be om flere prosentiler. Da listes disse opp innenfor en c()
som følger. Her gis prosentilene for 5, 10, 90 og 95 prosent.
quantile(abu89$time89, c(.05, .10, .90, .95), na.rm = TRUE)
5% 10% 90% 95%
54.91651 61.00000 127.77778 148.03618
Gjennomsnittet av en variabel gis ved funksjonen mean()
:
mean(abu89$time89, na.rm = TRUE)
[1] 90.14948
Standardavviket gis ved sd()
:
sd(abu89$time89, na.rm = TRUE)
[1] 30.31473
Medianen kan angis med quantile()
, men enklere med median()
:
median(abu89$time89, na.rm = TRUE)
[1] 83.33333
Vi trenger også ofte antall. nrow()
gir antall rader, dvs. antall observasjoner i datasettet
nrow(abu89)
[1] 4127
Tilsvarende gir ncol()
antall kolonner, mens dim()
gir begge deler:
ncol(abu89)
[1] 9
dim(abu89)
[1] 4127 9
8.2 Professjonelle tabeller med gtsummary
For å lage ordentlig professjonelle tabeller kreves det mer. For det første skal de se ordentlige ut, men de skal også kunne eksporteres til andre formater på en hensiktsmessig måte.
I R finnes det en hel rekke slike funksjoner. Her har vi vektlagt pakken gtsummary
fordi den gir gode tabeller fra helt enkle til ganske avanserte relativt lett. Det er også mange muligheter for å justere tabellene slik du vil. Dessuten kan resultatene eksporteres lett til de fleste aktuelle formater (Word, html, pdf, Excel, latex).
Avanserte brukere vil muligens se begrensningene i denne pakken og foretrekke noe annet. De fleste vil kunne lage det aller meste med denne pakken.
Vi starter med en enkel oversiktstabell med alle variablene i datasettet. Men vi fjerner løpenummeret for person, nemlig variabelen io_nr fordi den ikke inneholder noe analyserbar informasjon.
%>%
abu89 select(-io_nr) %>%
tbl_summary()
Characteristic | N = 4,1271 |
---|---|
Gjennomsnittlig timelønn 1989 | 83 (71, 103) |
Unknown | 368 |
År utdanning | |
0 | 839 (20%) |
1 | 1,156 (28%) |
3 | 1,121 (27%) |
5 | 483 (12%) |
7 | 308 (7.5%) |
9 | 205 (5.0%) |
11 | 15 (0.4%) |
Alder | 39 (30, 48) |
Respondentens kjønn | 1,934 (47%) |
Goldthorpe klasse 1989 | |
I Øvre serviceklasse | 328 (8.1%) |
II Nedre serviceklasse | 1,181 (29%) |
III Rutinefunksjonærer | 1,248 (31%) |
V-VI Faglærte arbeidere | 648 (16%) |
VIIa Ufaglærte arbeidere | 637 (16%) |
Unknown | 85 |
Noen gang forfremmet | |
NEI | 2,568 (62%) |
JA | 1,559 (38%) |
Bedriftserfaring | 0.70 (0.20, 1.40) |
Privat sektor | |
Public | 1,602 (39%) |
Private | 2,525 (61%) |
1 Median (IQR); n (%) |
Legg merke til at tbl_summary
gjør en del ting automatisk. Først og fremst er bruker den variabel label og factor levels i sidespalten. Ofte vil ikke variable ha slike labler, og da vil det vises variabelnavnene. Variabelen kjønn har ikke angitt factor levels, og variabelen har bare verdiene 0 og 1, og da rapporteres kun den ene kategorien (dvs. verdien 1). Vi kan legge til annen tekst hvis vi ønsker.
Dernest er det en forhåndsinnstilling som angir at det for kontinuerlige variable skal rapporteres median og interquartile range (IQR), dvs. nedre og øvre kvartil i parentes. Det gir en god beskrivelse av variablene, men vi skal endre dette nedenfor. For kategoriske variable rapporteres det antall observasjoner og andelen i prosent i parentes.
Men merk at for antall år utdanning og kjønn, så er det rapportert som kategoriske variable selv om variabeltypen er kontinuerlig. tbl_summary
gjør dette fordi det er relativt få kategorier slik at median og IQR ikke er så interessant uansett.
La oss først endre slik at det rapporteres gjennomsnitt og standardavvik i stedet. Det er mer vanlig å gjøre selv om det ikke er noen regel for dette. Funksjonen theme_gtsummary_mean_sd()
endrer standardvalget for tbl_summary
i alle etterfølgende tabeller. Dermed slipper du endre neste gang. Flere themes finner du på pakkens hjemmeside. For å gå tilbake til opprinnelig theme brukes funksjonen reset_gtsummary_theme()
.
Vi kan endre andre ting ved tabellen med noen enkle grep. Alle variable kan endre navn i forspalten med å legge til argumentet label =
. Nedenfor er to variable endret for å vise hvordan man endrer flere variable. Når det er flere variable må de spesifiseres innenfor argumentet list()
som nedenfor. Her endrer vi også label for variabelen female og klasse89.
Noen ganger kan man også ønske å endre hvordan en variabel presenteres. Et vanlig behov er å presisere hvilken type en variabel er. I dette tilfellet er utdanning antall år etter obligatorisk skolenivå, så det er egentlig en kontinuerlig variabel selv om antall verdier er få. Vi kan velge å presisere at denne er av typen continuous. Nedenfor presiserer vi også at female er kategorisk, dichotomous, selv om denne ble presentert riktig uansett. Vi bruker argumentet type =
og flere variable må oppgis innenfor list()
.
En siste ting vi kan endre er å ikke rapportere NA. Det er ikke oppgitt timelønn for alle, så antall NA er rapportert for seg. Det kan være fint, men kan også hende vi ikke ønsker det. Nedenfor er det derfor også lagt til missing = "no"
.
theme_gtsummary_mean_sd()
%>%
abu89 select(-io_nr) %>%
tbl_summary(label = list(female ~ "Kjønn", klasse89 = "Klasse"),
type = list(ed ~ "continuous", female ~ "dichotomous"),
missing = "no")
Characteristic | N = 4,1271 |
---|---|
Gjennomsnittlig timelønn 1989 | 90 (30) |
År utdanning | 2.69 (2.56) |
Alder | 40 (12) |
Kjønn | 1,934 (47%) |
Klasse | |
I Øvre serviceklasse | 328 (8.1%) |
II Nedre serviceklasse | 1,181 (29%) |
III Rutinefunksjonærer | 1,248 (31%) |
V-VI Faglærte arbeidere | 648 (16%) |
VIIa Ufaglærte arbeidere | 637 (16%) |
Noen gang forfremmet | |
NEI | 2,568 (62%) |
JA | 1,559 (38%) |
Bedriftserfaring | 0.95 (0.91) |
Privat sektor | |
Public | 1,602 (39%) |
Private | 2,525 (61%) |
1 Mean (SD); n (%) |
Ofte vil vi ha en tabell som ikke bare viser univariat fordeling, men bi-variate, altså fordelt på to eller flere grupper. Det er f.eks. ganske vanlig å vise tabeller fordelt på kjønn. Det kan vi også gjøre her ved å legge til argumentet by = female
. Nedenfor er det også forenklet argumentene for label =
og type =
. I slike tilfeller vil vi ofte ha totalen i tillegg til per gruppe, og det gjør vi ved å legge til funksjonen add_overall()
.
For de kontinuerlige variablene får vi ikke antallet som inngår i beregningene. Vi vil gjerne vise antall ikke-missing verdier - særlig fordi vi tok vekk NA som egen rad ovenfor. Dette gjør vi ved å legge til funksjonen add_n()
.
%>%
abu89 select(-io_nr) %>%
mutate(female = ifelse(female == 0, "Menn", "Kvinner")) %>%
tbl_summary(by = female,
label = list(klasse89 = "Klasse"),
type = list(ed ~ "continuous"),
missing = "no") %>%
add_overall() %>%
add_n()
Characteristic | N | Overall, N = 4,1271 | Kvinner, N = 1,9341 | Menn, N = 2,1931 |
---|---|---|---|---|
Gjennomsnittlig timelønn 1989 | 3,759 | 90 (30) | 79 (24) | 100 (32) |
År utdanning | 4,127 | 2.69 (2.56) | 2.38 (2.40) | 2.96 (2.66) |
Alder | 4,127 | 40 (12) | 40 (13) | 40 (12) |
Klasse | 4,042 | |||
I Øvre serviceklasse | 328 (8.1%) | 74 (3.9%) | 254 (12%) | |
II Nedre serviceklasse | 1,181 (29%) | 555 (29%) | 626 (29%) | |
III Rutinefunksjonærer | 1,248 (31%) | 986 (52%) | 262 (12%) | |
V-VI Faglærte arbeidere | 648 (16%) | 46 (2.4%) | 602 (28%) | |
VIIa Ufaglærte arbeidere | 637 (16%) | 244 (13%) | 393 (18%) | |
Noen gang forfremmet | 4,127 | |||
NEI | 2,568 (62%) | 1,308 (68%) | 1,260 (57%) | |
JA | 1,559 (38%) | 626 (32%) | 933 (43%) | |
Bedriftserfaring | 4,127 | 0.95 (0.91) | 0.83 (0.81) | 1.05 (0.97) |
Privat sektor | 4,127 | |||
Public | 1,602 (39%) | 1,016 (53%) | 586 (27%) | |
Private | 2,525 (61%) | 918 (47%) | 1,607 (73%) | |
1 Mean (SD); n (%) |
Men vi kan lage mer kompliserte tabeller også. La oss si at vi ønsker å lage den samme tabellen som over, men fordelt på to grupper. Det kan være relevant å sammenligne offentlig og privat sektor. En mulighet er å lage en ny grupperingsvariabel ved å slå sammen kjønn og sektor slik at vi får fire kategorier. Men vi får et bedre resultat ved å lage en stratifisert tabell med funksjonen tbl_strata()
. Det er litt kryptisk syntaks, men det viktige er å angi hvilken variabel det skal stratifiseres etter med argumentet strata =
etterfulgt av .tbl_fun = ~ .x %>%
, så kommer tble_summary
etter dette. Her er det også lagt til en ekstra header med antall observasjoner.
%>%
abu89 select(-io_nr) %>%
mutate(female = ifelse(female == 0, "Menn", "Kvinner")) %>%
tbl_strata(strata = private,
.tbl_fun =
~ .x %>%
tbl_summary(by = female,
label = list(klasse89 = "Klasse"),
type = list(ed ~ "continuous"),
missing = "no") %>%
add_n(),
.header = "**{strata}**, N = {n}"
)
Characteristic | Public, N = 1602 | Private, N = 2525 | ||||
---|---|---|---|---|---|---|
N | Kvinner, N = 1,0161 | Menn, N = 5861 | N | Kvinner, N = 9181 | Menn, N = 1,6071 | |
Gjennomsnittlig timelønn 1989 | 1,403 | 82 (23) | 100 (28) | 2,356 | 76 (24) | 100 (33) |
År utdanning | 1,602 | 2.88 (2.67) | 4.22 (3.12) | 2,525 | 1.82 (1.92) | 2.50 (2.31) |
Alder | 1,602 | 42 (12) | 43 (11) | 2,525 | 37 (13) | 39 (12) |
Klasse | 1,592 | 2,450 | ||||
I Øvre serviceklasse | 55 (5.4%) | 150 (26%) | 19 (2.1%) | 104 (6.7%) | ||
II Nedre serviceklasse | 340 (34%) | 196 (34%) | 215 (24%) | 430 (28%) | ||
III Rutinefunksjonærer | 507 (50%) | 64 (11%) | 479 (54%) | 198 (13%) | ||
V-VI Faglærte arbeidere | 5 (0.5%) | 114 (20%) | 41 (4.6%) | 488 (31%) | ||
VIIa Ufaglærte arbeidere | 104 (10%) | 57 (9.8%) | 140 (16%) | 336 (22%) | ||
Noen gang forfremmet | 1,602 | 2,525 | ||||
NEI | 696 (69%) | 318 (54%) | 612 (67%) | 942 (59%) | ||
JA | 320 (31%) | 268 (46%) | 306 (33%) | 665 (41%) | ||
Bedriftserfaring | 1,602 | 0.93 (0.85) | 1.15 (0.94) | 2,525 | 0.72 (0.74) | 1.01 (0.98) |
1 Mean (SD); n (%) |
Det går også an å lage langt mer avanserte tabeller enn dette, og alle deler kan modifiseres. Men vi går ikke inn på dette her. Ved behov finner du instruksjoner på pakkens hjemmeside.
8.2.1 Eksport av tabeller
Du skal aldri bruke “klipp og lim” for å få en tabell over i et tekstbehandlingsprogram. Trikset er å konvertere tabellen til gt-format som har en eksportfunksjon til MS Word.
Først lagres tabellen i et eget objekt.
<- abu89 %>%
fintabell select(-io_nr) %>%
mutate(female = ifelse(female == 0, "Menn", "Kvinner")) %>%
tbl_strata(strata = private,
.tbl_fun =
~ .x %>%
tbl_summary(by = female,
label = list(klasse89 = "Klasse"),
type = list(ed ~ "continuous"),
missing = "no") %>%
add_n(),
.header = "**{strata}**, N = {n}"
)
Så kan tabellen eksporteres til Word, og evt. redigeres videre der hvis det trengs. På dette nivået kan det være mer tidsbesparende å gjøre siste justeringer i Word fremfor å lære alle triks for å lage tabellen fiks ferdig i R. (Skal du lage mange tabeller kan det likevel lønne seg å gjøre mest mulig i R).
%>%
fintabell as_gt() %>%
::gtsave(filename = "output/fintabell.docx") gt
Merk at eksport til docx-formatet krever at du har en relativt ny installasjon av pakkene {gt} og {gtsummary}. Filhalen “.docx” innebærer at filen lagres i dette formatet. Tilsvarende kan du lagre i .html, .pdf, .rtf, .png, .tex eller .ltex bare ved å endre filhalen.
En tilsvarende variant som noen av dere har lært på sosgeo1120 er å bruke as_flextable
og en tilsvarende eksportfunksjon. Det er selvsagt også helt ok. En tidligere versjon av {gt} kunne som sagt ikke eksportere til Word, så da var {flextable} beste løsning. Men pakken {flextable} har vist seg å være litt trøblete å installere på noen pc’er, så da er det bedre å bruke {gt}.
8.3 Manuelle tabeller
Noen ganger trenger man å lage ganske spesifikke ting.
8.3.1 For datasettet totalt
8.3.2 Grupperte statistikker
8.4 Oppgaver
Slå opp i boken R for data science hvis du står fast eller ikke skjønner hva koden betyr.
Exercise 8.1 Bruk datasettet abu89 og lag de samme tabellene som vist her, gjør noen endringer på kodene for å endre utseendet på tabellene. Det er et mål at du skal forstå hva hver enkelt kommando gjør.
Exercise 8.2 Last inn datasettet NorLAG i R. Velg noen variable som du selv tenker kan være informative å se nærmere på. Bruk de samme teknikkene på disse variablene.