Inspicér og forstå din data

De vigtigste kommandoer til at se hvad du faktisk har

Published

July 2, 2026

I Fase 6 lavede du dit første udtræk - med syntetiske data fra fakeregs. Nu har du data, og inden du analyserer det, skal du forstå hvad du har. Det er først her, disse kommandoer giver mening: de er værktøjer til at kigge på et datasæt, så de er meningsløse uden data at kigge på.

Denne side viser de kommandoer, du vil bruge igen og igen til at inspicere dine udtræk.

Note

$ - tilgå én kolonne i en tabel data$koen betyder: “kolonnen koen i tabellen data”. Erstat data med dit eget datasætnavn og koen med dit eget kolonnenavn. Du vil se $ overalt i R-kode.

Tip

Vil du øve kommandoerne i RStudio? Alle eksempler på siden bruger bef_data - et BEF-udtræk med kolonner som koen, alder og foed_dag. Du kan generere det med to linjer, forudsat du har kørt forberedelsesblokken i Fase 6:

# Fortsætter fra Fase 6 - bef_data er allerede åbnet som doven Arrow-forbindelse
bef_data <- bef_data %>% filter(year == 2015) %>% collect()   # filtrer og hent ind i R

Ser du en rød fejlbesked?

Inden du leder i koden - gør dette i rækkefølge:

  1. Læs fejlbeskeden: hvilken linje er nævnt? Hvilket objektnavn dukker op?
  2. Kør class(objektet): er det data ("data.frame") eller stadig en forbindelse ("tbl_duckdb_connection")?
  3. Kør names(objektet): hedder kolonnen præcis det, du tror? Et enkelt bogstav eller stor/lille skrift er nok til at fejle.
  4. Isolér den fejlende linje: kør den alene og se hvad der sker.
  5. Brug ?funktionsnavn: skriv fx ?colSums i konsollen for at åbne hjælpedokumentationen i Help-panelet (nederst til højre). Den viser hvad funktionen gør, hvilke argumenter den tager, og eksempler.
  6. Spørg en kollega eller søg på fejlbeskeden: se 2 - R: det allermest nødvendige for prioriteret hjælpeliste.

En oversigt over hyppige fejlbeskeder og hvad de typisk betyder finder du i Faldgruber på DST.


Se hvad du har

dim(bef_data)          # antal rækker og kolonner - fx "1200 rows, 8 columns"
nrow(bef_data)         # kun antal rækker
ncol(bef_data)         # kun antal kolonner
names(bef_data)        # kolonnenavne som en vektor

Vil du tjekke kolonnenavnene på et dovent objekt inden collect(), så brug colnames(bef) - det virker både på Arrow- og DuckDB-baserede dovne objekter. (names() virker på en data.frame efter collect(), men returnerer ikke nødvendigvis kolonnerne på et dovent DuckDB-/read_register()-objekt - brug colnames() for at være sikker.)

Eksempel med simpel data
df <- data.frame(
  pnr        = c("001", "002", "003"),
  koen       = c("M", "K", "M"),
  index_date = as.Date(c("2015-03-01", "2016-07-14", "2014-11-30"))
)

names(df)
# [1] "pnr"        "koen"       "index_date"

Forstå strukturen

glimpse(bef_data)      # kolonnenavn, type og første værdier - kompakt og læsbar (kræver dplyr)
str(bef_data)          # samme information, men mere verbose output
class(bef_data)        # objekttype - er data faktisk i R, eller er det stadig en forbindelse?
class(bef_data$alder)  # type for én kolonne: "numeric", "character", "Date" mv.

class(bef_data) fortæller om du har rigtige data ("data.frame"/"tbl_df") eller stadig bare en usendt forespørgsel. Du kan se tre forskellige returværdier:

  • "data.frame" / "tbl_df" - data er i R. Du kan bruge alle funktioner.
  • "tbl_duckdb_connection" / "Table" - doven Arrow/DuckDB-forespørgsel. Mangler collect().
  • "arrow_dplyr_query" - en Arrow-forespørgsel med et eller flere piped trin (fx filter() eller select()), men endnu ikke kørt. Mangler collect().
Tip

class() kan hjælpe dig med at fejlfinde Ser objektet ud som data men opfører sig mærkeligt, eller fejler din kode med en mystisk besked? Kør class(dit_objekt) - er det ikke "data.frame", mangler du formentlig et collect(). Den fulde tabel over hvad class() kan returnere - og hvorfor - står i Fase 5 - Udtræk trin for trin.


Se de første og sidste rækker

head(bef_data)         # de første 6 rækker - ser kolonnerne og typerne rigtige ud?
head(bef_data, 10)     # de første 10 rækker
tail(bef_data)         # de sidste 6 rækker - nyttigt til at opdage ufuldstændige datasæt

Udforsk indholdet

Important

To ting skal være på plads, før kommandoerne i resten af afsnittet virker

1. Giv dit udtræk et navn. Skriver du bare open_dataset(...) (eller read_register(...) i DARTER) uden at gemme det i et objekt, printer R kun et hurtigt kig og smider resultatet væk - der er intet at inspicere bagefter. Tildel det et navn med <- (se Fase 2 - Hvad er et objekt?), så du kan bruge det igen.

2. Hent data ind i R med collect() først. Alle kommandoer der bruger $ (fx table(bef$koen), unique(), summary(), min()/max()/median(), hist(), colSums(is.na())) kræver rigtige data i R - $ kan ikke trække en kolonne ud af en doven Arrow/DuckDB-forbindelse. Tjek med class(bef): står der ikke "data.frame"/"tbl_df", mangler du et collect().

Du skal ikke skrive read_register()/open_dataset() forfra for at hente data ind - arbejd videre med det objekt, du allerede har lavet. Genbrug navnet og gem resultatet, typisk under samme navn, så bef overskrives og nu er rigtige data. Du kan hente alt ind som det er, eller reducere med filter()/select() først:

bef <- read_register("bef")              # doven forbindelse - kun et navn endnu

bef <- bef %>% collect()                 # hent ALT ind som det er - overskriver bef

# ELLER reducér først (anbefalet på store registre):
bef <- bef %>% filter(year == 2015) %>% select(pnr, koen, alder) %>% collect()

# nu virker bef$koen, table(bef$koen), summary(bef) osv.

(Vil du beholde den dovne forbindelse, så giv det hentede data et nyt navn i stedet: bef_2015 <- bef %>% filter(year == 2015) %>% collect().)

colnames(), glimpse(), head() og dplyr-verber (count(), filter(), select()) virker dog fint på den dovne forbindelse. Så peg lazily, filter()/select() ned, og collect() et lille udsnit, før du graver i værdierne.

Som nævnt øverst på siden er $-tegnet R’s måde at sige “denne kolonne i denne tabel”: data$koen er kolonnen koen i tabellen data.

unique(bef_data$koen)                     # hvilke unikke værdier findes i kolonnen koen?
table(bef_data$koen)                      # frekvenstabel: hvor mange rækker har hver værdi?
table(bef_data$koen, bef_data$civst)      # krydstabel: fordeling af køn på tværs af civilstatus
table(bef_data$koen, useNA = "ifany")     # tæl NA'er med - ellers skjules de (se advarsel nedenfor)
Warning

table() skjuler NA som standard. Uden useNA = "ifany" tæller table() kun de “rigtige” værdier og udelader manglende værdier helt - så fordelingen ser komplet ud, selvom en del af kolonnen er NA. Skriv altid useNA = "ifany" (vis kun NA-rækken hvis der findes NA’er) eller useNA = "always" (vis altid NA-rækken), når du inspicerer en kolonne, så du opdager manglende værdier i stedet for at overse dem.

Eksempel med simpel data
# Et lille eksempel med tre patienter og to variable:
df <- data.frame(
  koen         = c("M", "K", "M", "K", "M"),
  alder_gruppe = c("18-40", "18-40", "41-60", "41-60", "41-60")
)

table(df$koen)
#  K  M
#  2  3       # 2 kvinder, 3 mænd

table(df$koen, df$alder_gruppe)
#    18-40  41-60
# K      1      1    # 1 kvinde i 18-40, 1 kvinde i 41-60
# M      1      2    # 1 mand i 18-40, 2 mænd i 41-60

Opsummering af data

Note

Hvad er NA? NA (Not Available) er R’s betegnelse for en manglende eller ukendt værdi. En celle kan have NA fordi oplysningen ikke er registreret, ikke er indberettet eller ikke findes for den person. De fleste beregningsfunktioner returnerer NA hvis der er én NA i data - medmindre du skriver na.rm = TRUE (“remove NAs”). is.na(x) returnerer TRUE for NA-værdier og FALSE for alt andet.

Hvornår er NA et problem? Det afhænger af hvilken kolonne der mangler:

  • NA i en nøglevariabel (index-dato, pnr, udfald) er alvorligt - disse personer kan ikke indgå korrekt i analysen, og du skal beslutte om de skal ekskluderes.
  • NA i en kovariat (fx indkomst) kan ofte håndteres - fx med en separat “ukendt”-kategori eller imputation.
  • NA fra et join betyder ofte, at en person ikke fandtes i det højre datasæt - fx ingen receptregistrering. Her er NA reelt et “nej/ingen”, ikke en fejl.

Tjek altid colSums(is.na(data)) lige efter et udtræk eller et join, så du opdager uventede huller, før de forplanter sig stille gennem analysen.

summary(bef_data)              # min, max, median, gennemsnit og kvartiler for alle kolonner
summary(bef_data$foed_dag)     # opsummering af én kolonne

For kontinuerte variable:

min(bef_data$alder, na.rm = TRUE)    # mindste værdi (na.rm fjerner NA'er - rm = remove)
max(bef_data$alder, na.rm = TRUE)    # største værdi
mean(bef_data$alder, na.rm = TRUE)   # gennemsnit
median(bef_data$alder, na.rm = TRUE) # median
sd(bef_data$alder, na.rm = TRUE)     # standardafvigelse
IQR(bef_data$alder, na.rm = TRUE)    # interkvartilbredde (Q3 - Q1)

Tjek manglende værdier

sum(is.na(bef_data$koen))    # antal NA'er i kolonnen koen - erstat med dit eget kolonnenavn
colSums(is.na(bef_data))     # antal NA'er per kolonne - giver overblik over hele datasættet

colSums(is.na(bef_data)) returnerer én linje med en tæller per kolonne:

#  pnr  koen  alder  foed_dag  year  civst  opr_land  reg
#    0     0      0         3     0      0         0    0

Her er foed_dag manglende for 3 - alt andet er komplet. En kolonne med 0 er helt uden NA’er.

Warning

colSums(is.na()) tæller kun ægte NA. I registerdata er “manglende” ofte gemt som en tom eller blank streng (whitespace) eller en sentinel-kode - ikke som NA. En kolonne kan derfor vise 0 NA’er og alligevel være fuld af tomme værdier: fx et fast-bredde nøglefelt fyldt med blanktegn (et 13-tegns felt der ser tomt ud, men er 13 mellemrum, ikke NA). Tjek derfor også for blanke værdier - fx sum(trimws(bef_data$dw_ek_forloeb) == "") - og kig på nchar()/unique() på et udsnit. Det er især vigtigt for join-nøgler: en blank-men-ikke-NA nøgle giver tavse fejl i joins (rækker, der ser udfyldte ud, men ikke matcher).


Tjek datoer

Datokolonner kan indeholde umulige værdier - datoer langt uden for studieperioden er et tegn på en konverteringsfejl eller forkert kolonne.

min(bef_data$foed_dag, na.rm = TRUE)   # er den tidligste fødselsdato plausibel?
max(bef_data$foed_dag, na.rm = TRUE)   # er den seneste fødselsdato plausibel?

# Tjek om der er datoer FØR et forventet interval:
sum(bef_data$foed_dag < as.Date("1900-01-01"), na.rm = TRUE)   # erstat datoen med din nedre grænse

# Tjek om der er datoer EFTER et forventet interval:
sum(bef_data$foed_dag > as.Date("2015-12-31"), na.rm = TRUE)   # erstat datoen med din øvre grænse

Se Faldgruber på DST for de hyppigste datokonverteringsfejl og hvordan du retter dem.


Mere udforskning: tæl, sortér og hurtige plots

Tæl og sortér (dplyr):

Erstat bef_data med dit eget datasætnavn og kolonnenavne med dine egne.

bef_data %>% count(koen)               # antal rækker per kategori
bef_data %>% count(koen, civst)        # per kombination af to variable
bef_data %>% arrange(foed_dag)         # sortér stigende efter fødselsdato
bef_data %>% arrange(desc(foed_dag))   # sortér faldende

Hurtige visualiseringer - til at danne sig et overblik, ikke til publikation:

Erstat bef_data med dit eget datasætnavn og kolonnenavne med dine egne.

# Kontinuerte variable:
hist(bef_data$alder)                            # histogram
boxplot(bef_data$alder)                         # boksplot
boxplot(alder ~ koen, data = bef_data)          # boksplot opdelt på køn

# Kategoriske variable:
barplot(table(bef_data$koen))                   # søjlediagram

Næste skridt

Du kan nu inspicere et datasæt. Næste skridt er at vide, hvilke registre der indeholder hvad:

Back to top