Socioøkonomiske variable
Uddannelse, indkomst og beskæftigelse efter SEPLINE-tilgangen
Du kender nu mønstret: open_dataset → filter → collect → left_join. Det er præcis det, du bruger her. To ting er nye i denne fase:
- “Hent for året inden index-dato”: SES-registrene er årsbaserede snapshots. Du kan ikke bare filtrere på
pnr; du skal også matche på hvilket år der er relevant per person (year(index_date) - 1). Det gøres ved at beregne baseline-året per person og bruge det som join-nøgle. - FAIK via
familie_id: Indkomst er knyttet til husstanden, ikke personen direkte. Du skal bruge BEF som bro: hentfamilie_idfra BEF for baseline-året, og join derefter til FAIK påfamilie_id.
Resten er kategorisering per SEPLINE-guidelines.
Socioøkonomisk position (SEP) måles i registerbaserede studier via tre dimensioner: uddannelse (UDDA), indkomst (FAIK) og beskæftigelse (AKM). Denne side viser, hvordan du udtrækker og kategoriserer dem efter SEPLINE-guidelinen.
SEPLINE-artikel: Hjorth et al. Clinical Epidemiology 2025 - doi:10.2147/CLEP.S520772. Se artiklen for fuld begrundelse, anbefalede referencegrupper og kategoriseringer.
Under udvikling - kode-eksempler, ikke valideret kode. Kategoriseringerne nedenfor er ikke valideret og bør ikke bruges direkte i analyser uden gennemgang. De vises som strukturelle eksempler på, hvordan man kan kode variablerne - ikke som en godkendt implementering. Har du kode der allerede virker, eller input til kategoriseringerne, så hører jeg meget gerne fra dig: Sara Schwartz - saras@clin.au.dk
Adgang til registrene - vejene:
- fastreg (anbefalet): Brug
read_register("akm")- læser via navn, fastreg kender stien (se Fase 4). - Parquet via
open_dataset()(alle projekter): Brugopen_dataset("E:/workdata/[projektnummer]/cleaned-data/parquet-registers/akm/")+rename_with(tolower). Eksemplerne nedenfor bruger dette mønster; skift tilread_register("akm"), hvis du bruger fastreg. - SAS-filer (hvis parquet ikke er klar): Brug
haven::read_sas("sti/akm.sas7bdat")- men dette læser hele filen ind i RAM. Anbefalet: konvertér til parquet én gang og arbejd derfra (se Fase 4 - Konvertér SAS til parquet).
Dine kolonner kan hedde noget andet - tjek med names(din_data).
Hent variablen for året INDEN index - ikke index-året selv. Alle tre registre er årsbaserede: hver person har én værdi per kalenderår. Hvornår på året værdien gælder, afhænger af registret - fx er indkomst (FAIK) en sum for hele kalenderåret, mens uddannelse (UDDA) og beskæftigelse (AKM) opgøres med et referencetidspunkt i løbet af året. Slå det op for det konkrete register, hvis den præcise timing er afgørende for dig.
Uanset det eksakte tidspunkt gælder pointen: tager du værdien fra index-året, risikerer du at måle status, der ligger efter din eksponering. Eksempel: index = operationsdato 15. juni 2015. 2015-værdien kan afspejle status efter operationen og være påvirket af selve eksponeringen (fx tab af job efter sygdom) - det giver omvendt kausalitet, mens du vil have en baseline-værdi fra før eksponeringen.
Derfor henter du værdien for year(index_date) - 1 (her 2014). Det er en sikker, ens regel for hele kohorten, uanset hvornår på året index falder.
De tre dimensioner
| Dimension | Register | Variabel |
|---|---|---|
| Uddannelse | UDDA | hfaudd - højeste afsluttede uddannelse (ISCED-kode) |
| Indkomst | FAIK | famaekvivadisp_13 - husstandsækvivaleret disponibel indkomst |
| Beskæftigelse | AKM | socio13 - arbejdsmarkedsklassifikation |
SEPLINE specificerer både hvordan disse variable kategoriseres og hvornår i forløbet de måles.
Beskæftigelse - AKM (socio13)
library(arrow) # open_dataset()
library(dplyr) # filter, select, mutate, left_join, collect
library(lubridate) # year() til at trække årstal ud af datoer
# Erstat stien med din projekts parquet-sti - DARTER: read_register("akm")
akm <- open_dataset("E:/workdata/[projektnummer]/cleaned-data/parquet-registers/akm/") %>%
rename_with(tolower) # standardisér kolonnenavne
# Hent beskæftigelsesstatus for året inden index-dato
# (antager at kohort har kolonnerne pnr og index_date)
index_aar <- unique(lubridate::year(kohort$index_date) - 1) # baseline-år = index-år minus 1
akm_data <- akm %>%
semi_join(tibble(pnr = kohort$pnr), by = "pnr") %>% # kun kohortens pnr'er
filter(aar %in% !!index_aar) %>% # baseline-år
select(pnr, aar, socio13) %>% # kun de kolonner vi bruger
collect() # hent ind i R
# Tilknyt til kohort med index-år som join-nøgle
kohort_akm <- kohort %>%
mutate(aar_baseline = lubridate::year(index_date) - 1) %>% # beregn baseline-år
left_join(akm_data, by = c("pnr", "aar_baseline" = "aar")) # join på pnr og år
# Kategorisér per SEPLINE
kohort_akm <- kohort_akm %>%
mutate(occupation_cat = case_when(
socio13 %in% c(110, 111, 112, 113, 114, 120, 131, 132, 133, 134, 135, 139) ~ "Beskæftiget",
socio13 == 310 ~ "Studerende",
socio13 %in% c(210, 410) ~ "Ledig",
socio13 %in% c(220, 321, 330) ~ "Udenfor arbejdsmarked", # sygedagpenge, førtidspension, fleksjob
socio13 %in% c(322, 323) ~ "Pensionist",
TRUE ~ "Ukendt" # 0, 420 eller manglende
))Uddannelse - UDDA (hfaudd)
Kategoriseres ud fra ISCED-koden i hfaudd: kort (10/15), mellemlang (20/30/35), lang (40–80), ukendt (90 eller manglende).
# DARTER: read_register("udda")
udda <- open_dataset("E:/workdata/[projektnummer]/cleaned-data/parquet-registers/udda/") %>%
rename_with(tolower) # standardisér kolonnenavne
udda_data <- udda %>%
semi_join(tibble(pnr = kohort$pnr), by = "pnr") %>% # kun kohortens pnr'er
filter(aar %in% !!index_aar) %>% # baseline-år
select(pnr, aar, hfaudd) %>% # kun de kolonner vi bruger
collect() # hent ind i R
# Tag den seneste post hvis en person optræder flere gange
udda_data <- udda_data %>%
group_by(pnr) %>% # gruppér for at finde nyeste post per person
arrange(desc(aar)) %>% # nyeste år først
slice(1) %>% # behold kun den nyeste post
ungroup() # frigiv gruppering
# Kategorisér per SEPLINE
udda_data <- udda_data %>%
mutate(education_cat = case_when(
substr(as.character(hfaudd), 1, 2) %in% c("10", "15") ~ "Kort",
substr(as.character(hfaudd), 1, 2) %in% c("20", "30", "35") ~ "Mellemlang",
as.numeric(substr(as.character(hfaudd), 1, 2)) >= 40 ~ "Lang",
is.na(hfaudd) | substr(as.character(hfaudd), 1, 2) == "90" ~ "Ukendt",
TRUE ~ "Ukendt"
))Indkomst - FAIK via BEF (famaekvivadisp_13)
Indkomst er knyttet til husstanden, ikke personen. Du skal bruge familie_id fra BEF som bro. SEPLINE anbefaler 3-årsgennemsnit inddelt i kvintiler stratificeret på køn × 5-årsaldersgruppe × referenceår.
# DARTER: read_register("bef") og read_register("faik")
bef <- open_dataset("E:/workdata/[projektnummer]/cleaned-data/parquet-registers/bef/") %>% rename_with(tolower)
faik <- open_dataset("E:/workdata/[projektnummer]/cleaned-data/parquet-registers/faik/") %>% rename_with(tolower)
# Hent familie_id fra BEF for baseline-år
bef_familie <- bef %>%
semi_join(tibble(pnr = kohort$pnr), by = "pnr") %>% # kun kohortens pnr'er
filter(aar %in% !!index_aar) %>% # baseline-år
select(pnr, aar, familie_id) %>% # familie_id er broen til FAIK
collect() # hent ind i R
# Hent indkomst fra FAIK for baseline-år
faik_data <- faik %>%
filter(aar %in% !!index_aar) %>% # kun baseline-år
select(familie_id, aar, famaekvivadisp_13) %>% # kun de kolonner vi bruger
collect() # hent ind i R
# Join: pnr → familie_id → indkomst
indkomst <- bef_familie %>%
left_join(faik_data, by = c("familie_id", "aar")) # to-nøgle join: husstand og år3-årsgennemsnit og kvintiler (SEPLINE-anbefaling)
SEPLINE anbefaler 3-årsgennemsnit af indkomst og kvintiler stratificeret på køn × 5-årsaldersgruppe × år. Her er en forenklet version med kvintiler per år:
Hvad denne kode ikke gør: ntile(mean_indkomst, 5) beregner kvintilgrænser ud fra kohortens egne værdier. Den korrekte SEPLINE-tilgang bruger grænseværdier (Q20/Q40/Q60/Q80) beregnet fra den fulde BEF-befolkning for hvert referenceår, stratificeret på køn × 5-årsaldersgruppe. Det kræver et ekstra BEF-udtræk uden pnr-filtrering og er ikke implementeret her.
library(dplyr) # filter, select, left_join, group_by, summarise, mutate
# Hent 3 år: index-år samt de to foregående
aar_3 <- c(index_aar, index_aar - 1, index_aar - 2) # 3-års vindue til gennemsnit
bef_3aar <- bef %>%
semi_join(tibble(pnr = kohort$pnr), by = "pnr") %>% # kun kohortens pnr'er
filter(aar %in% !!aar_3) %>% # de 3 år
select(pnr, aar, familie_id) %>% # familie_id er broen til FAIK
collect() # hent ind i R
faik_3aar <- faik %>%
filter(aar %in% !!aar_3) %>% # kun de 3 baseline-år
select(familie_id, aar, famaekvivadisp_13) %>% # kun de kolonner vi bruger
collect() # hent ind i R
# Beregn 3-årsgennemsnit per person
indkomst_mean <- bef_3aar %>%
left_join(faik_3aar, by = c("familie_id", "aar")) %>% # kobl indkomst på via husstand og år
group_by(pnr) %>% # gruppér for at beregne gennemsnit per person
summarise(
mean_indkomst = mean(famaekvivadisp_13, na.rm = TRUE), # gennemsnitlig disponibel indkomst
.groups = "drop" # frigiv gruppering automatisk
)
# Inddel i kvintiler
indkomst_kvintil <- indkomst_mean %>%
mutate(income_cat = ntile(mean_indkomst, 5)) # ntile(x, 5): 5 grupper - 1 = lavest, 5 = højestSaml alle SES-variable på kohorten
kohort_ses <- kohort %>%
left_join(kohort_akm %>% select(pnr, occupation_cat), by = "pnr") %>% # tilknyt beskæftigelse
left_join(udda_data %>% select(pnr, education_cat), by = "pnr") %>% # tilknyt uddannelse
left_join(indkomst_kvintil %>% select(pnr, income_cat), by = "pnr") # tilknyt indkomstkvintilSe også
- SEPLINE-artiklen (Hjorth et al. 2025): fuld metodebeskrivelse og anbefalede referencegrupper
- Formateringstabeller: DST’s SAS-filer til label-oversættelse
- Register-overblik: bekræftede kolonnenavne for AKM, FAIK og UDDA
Næste skridt
Du har nu SES-kovariater. Næste skridt afhænger af hvad du mangler:
- Specialpakker (OSDC til diabetesklassifikation, NMI til comorbiditetsscore, analyseværktøjer)? → Algoritmer & specialpakker
- Klar til at eksportere resultater? → Fase 14 - Eksport og hjemsendelse