Format tables
Translate codes to text with DST’s SAS format files
The format tables on the server are the authority, not this page. The examples and code lists here are a starting point. DST updates the format catalogue without this guide knowing, and a code can change meaning while keeping its number. Load the current table from E:/Formater/ and check the values in your own delivered data before you rely on a mapping.
Several of the same code lists are also published outside DST, as classifications with a CSV download, which is the version Find a variable shows.
Prerequisite: This page uses left_join() to attach format tables to your data. If you are not familiar with joins yet, read Phase 12 - Assemble and prepare the dataset first.
Many variables in DST registers are stored as codes - municipality numbers, education codes, employment codes. To give them meaningful labels you use DST’s format tables: SAS files on the server that translate codes to text.
A format table always has at least two columns:
| Column | Contents | Example |
|---|---|---|
START |
The raw code from your register | 101 (municipality number) |
| Label column | The corresponding text label | "København" |
You join the format table onto your dataset with START as the key - exactly like a dictionary lookup.
Rows in your dataset without a match in the format table get NA in the label column - no error message. Always check with sum(is.na(data$label_column)) after the join.
Where are the format tables?
E:/Formater/SAS formater i Danmarks Statistik/SAS_datasaet/
The Star (*) on the Windows desktop inside the DST server opens an HTML guide for finding the right table. DST’s written guides sit here:
E:/Formater/SAS formater i Danmarks Statistik/Vejledning mv/
The one worth reading is Brugen af SAS formater i Danmarks Statistik.pdf (~2 MB, DST Consulting). It explains the naming systematics used below and has a section per topic - Uddannelserne, Brancherne, DISCO, Sundhed, Geokoder, DREAM. The same folder holds dated older editions (2012-2019) and a set of Allokering af formatbibliotek (*).sas scripts for pointing SAS at the format catalog - those are for SAS users and are not needed from R.
Two more folders one level up are worth knowing:
E:/Formater/SAS formater i Danmarks Statistik/HTML_oversigter/ ← browsable code lists
E:/Formater/SAS formater i Danmarks Statistik/PDF_lister/ ← the same as PDF
They let you look up what a code means without loading anything into R - useful when you just need to check a single value.
Subfolders are organised by topic - including:
Geokoder/ ← municipalities, regions
Times_personstatistik/ ← employment (socio13)
Uddannelser/ ← education (hfaudd)
Brancher/ ← industries
Sundhed/ ← health
Disced/ ← education, SAS format catalog (see note)
... and more
Education sits in two places, and the naming is confusing. DST’s guide says the formats for the current classification, DISCED-15 (used from 2015), are in the format catalog called DISCED, while the catalog called Uddannelser holds the older classifications it replaced (Forspalte, ISCED1997 and DUN). Look in both SAS_datasaet/Disced/ and SAS_datasaet/Uddannelser/ before you pick a file; see Education level for how to tell them apart by name.
Find the specific filenames in the folder yourself. The base path and subfolder structure above are confirmed on DST. But the specific filenames and column names further down the page (e.g. c_kom_v4_t.sas7bdat, KOM_V4_T) are illustrative examples - naming varies, and you must find the right file in the relevant subfolder yourself. Use the Star guide / PDF guide to find the file, and names()/head() to see the actual column names.
Load a format table
Format tables are SAS files and are loaded with haven::read_sas():
library(haven) # read_sas - loading SAS files
mun_table <- read_sas(
# fetch format table as data frame
"E:/Formater/SAS formater i Danmarks Statistik/SAS_datasaet/Geokoder/c_kom_v4_t.sas7bdat"
)Format tables are loaded directly into R’s memory (not lazy evaluation). You must collect() DuckDB data before joining with them.
Two of these lookups already exist in R. The heaven package ships them as data, so you do not have to read a SAS format file at all:
data(edu_code): 4,621 education codes withhfauddmapped to categories in English and Danish, a 5-level grouping and ISCED codes.kommuneRegion(kommune): converts a numeric 3-digit municipality code straight to one of the five regions (valid from 2007). It gives you the region, not the municipality name - for the name you still need the format table below.
heaven is pre-installed on DST, and elsewhere it installs from GitHub with pak::pak("tagteam/heaven") (it is not on CRAN, so install.packages() will not find it). The format tables below are still the route for every code heaven does not cover, and they are what DST itself publishes.
The tables you will use most often
Municipality names
mun_table <- read_sas(
"E:/Formater/SAS formater i Danmarks Statistik/SAS_datasaet/Geokoder/c_kom_v4_t.sas7bdat"
)
# Columns: START (municipality number), KOM_V4_T (municipality name)
# Use for years after the 2007 municipal reform (V4)
bef_with_mun <- bef_data %>%
collect() %>% # collect BEFORE joining with SAS file
left_join(mun_table, by = c("kom" = "START")) # attach municipality name via the kom codeEmployment status (socio13)
To get the raw code labels from DST’s format table:
socio13_table <- read_sas(
# fetch label table from format folder
"E:/Formater/SAS formater i Danmarks Statistik/SAS_datasaet/Times_personstatistik/n_socio13_kt.sas7bdat"
)
# Columns: START (socio13 code), N_SOCIO13_KT (label)
akm_with_labels <- akm_data %>%
left_join(socio13_table, by = c("socio13" = "START")) # attach label per codeFor analysis you usually want SEPLINE’s four main groups rather than DST’s labels (students count as working there):
library(dplyr) # mutate, case_when
akm_categorised <- akm_data %>%
mutate(
occupation_cat = case_when(
# SEPLINE Table 6 main groups
socio13 %in% c(110:114, 120, 131:135, 139, 310) ~ "A: Working",
socio13 %in% c(210, 410) ~ "B: Unemployed",
socio13 %in% c(220, 321, 330) ~ "C: Outside workforce",
socio13 %in% c(322, 323) ~ "D: Retired",
TRUE ~ "Missing/other" # 0, 420 (children under 15), no record, any other code
)
)The full employment recipe, with the subgroups, is in Socioeconomic variables.
Education level (hfaudd)
hfaudd is the four-digit code for the specific education. Its level - the thing almost every analysis actually needs - lives in a separate lookup, and the filename tells you exactly which lookup you are getting.
These files are dictionaries, not data. A .sas7bdat in the format folders holds no people and no register records. It is a two-column translation table - one row per code, with the code and what it means - and they are tiny, a few hundred KB. You read one into R and left_join() it onto your own data, exactly like the municipality example above. Nothing about your cohort changes; you are only adding a column that says what a code stands for.
What on this page has not been checked on the server. The naming rules below come from DST’s own guide (Brugen af SAS formater i Danmarks Statistik) and DST’s published DISCED-15 classification. The exact filenames in the education folders have not been listed and checked for this section, so treat every filename here as a pattern to look for, not a path to copy. Run list.files() on the folder first, as in the code below.
How to read the file names. DST’s guide gives the name for turning hfaudd into the main area as AUDD_HOVED_L1L5_KT ($AUDD_HOVED_L1L5_KT in SAS, where $ marks the character version). The files you read from R carry the same parts, in lower case, with c_ or n_ in front and usually a version year:
| Part | Means | What to pick |
|---|---|---|
c_ / n_ |
the character or numeric version | the one matching the type of hfaudd in your data, or simply c_ after converting hfaudd to text (see Character or numeric) |
audd / udd |
afsluttet (completed) or igangværende/afbrudt (ongoing or interrupted) education | audd for hfaudd (see why) |
2015, 2023, … |
which version of the format | the newest only; it covers older years too (see Four things to check) |
hoved, type, niveau, fag |
which of the four DISCED-15 dimensions you group by (see below) | hoved for short/medium/long, as SEPLINE does (see The four dimensions) |
l1l5 |
from level 1 (the four-digit code) to level 5 (the coarsest grouping) | l1l5 to go from hfaudd to the two-digit main area (see The five levels) |
_k / _t / _kt |
what you get back: the code, the text, or both | _k for analysis, _t for table labels (see Which variant to pick) |
Why there are several files for what looks like one lookup. DST’s guide says it makes up to six variants of every format: character or numeric, each as _k, _t and _kt, “where possible”. Seeing five or six files with the same stem is therefore normal. They hold the same mapping and differ only in the type they expect and what they return.
Character or numeric: how to tell. Ask R what type hfaudd is in your delivery. head(0) returns the column types with zero rows, so no data is printed:
udda %>% head(0) %>% collect() %>% str()
# hfaudd: chr -> use a c_ file
# hfaudd: num or int -> use an n_ fileThe simpler rule that always works: convert hfaudd to text with as.character() and always use the c_ file, as the code further down does. Text is also the safer type for education codes, because some DISCED-15 codes start with a zero (0001 Ingen uddannelse), and stored as a number that code becomes 1 and no longer matches the lookup.
hfaudd is a completed education, so you want an audd file, not a udd one. DISCED-15 distinguishes between the code for an ongoing or interrupted education (UDD) and for a completed one (AUDD) - DST documents the two separately - and there are format files for both. The name gives it away: hfaudd is højest fuldførte AUDD, the highest completed education. A udd lookup joined to hfaudd will match some rows, miss others, and never tell you.
The four dimensions. DISCED-15 for completed education groups the same four-digit codes in four different ways, and each has its own format. Pick the one that answers your question:
| Dimension (Danish) | Groups education by | Top level, for example | Use it for |
|---|---|---|---|
Hovedområde (hoved) |
the Danish main areas of education | 10 Grundskole, 20 Gymnasiale uddannelser, 30 Erhvervsfaglige uddannelser, 40 KVU, 50 MVU, 60 Bachelor, 70 LVU, 80 Ph.d., 90 Uoplyst |
short/medium/long education; what SEPLINE uses |
Uddannelsesniveau (niveau) |
level, lined up with the international ISCED | 10 Grundskole til og med 6. klasse, 30 Gymnasiale og erhvervsfaglige, 50 KVU, 60 MVU / Bachelorer, 70 LVU |
comparing with ISCED-based studies; SEPLINE does not recommend its ISCED format for categorising, because e.g. 15 becomes ISCED 3 |
Fagområde (fag) |
subject, regardless of level | 25 Humanistisk, 35 Samfundsvidenskab, 45 Naturvidenskab |
what field someone is trained in |
Uddannelsestype (type) |
type of programme | 50 Bacheloruddannelser, 55 Professionsbachelor, 85 Voksenuddannelser og kurser |
ordinary vs adult education |
The same number means different things in different dimensions (50 is MVU in hoved and bachelor programmes in type), so a code is only meaningful together with the file it came from. Record the filename in your analysis script.
The five levels. Within a dimension, DST groups every education in a tree with five levels. Level 1 is the four-digit code you have in hfaudd; each level above it is a broader group, and level 5 is the two-digit main area. The l1l5 in a filename means “give me a level-1 code, and I return its level-5 group”. A file named l1l2 would instead return the level just above the code, which is still very detailed. For short, medium and long education you want the two-digit main areas, so an L1 → L5 file.
Sygeplejerske, prof.bach. in the Hovedområde dimension, from DST’s DISCED-15 AUDD classification:
| Level | Code | What it is |
|---|---|---|
| L1 | 5166 |
Sygeplejerske, prof.bach. - the value in hfaudd |
| L2 | 50893510 |
Sygepleje og sundhedspleje, MVU (eight digits) |
| L3 | 508935 |
Sygepleje og sundhedspleje, MVU (six digits) |
| L4 | 5089 |
a broader group within MVU (four digits) |
| L5 | 50 |
Mellemlange videregående uddannelser, MVU |
An l1l5 file turns 5166 into 50; an l1l2 file would turn it into 50893510. Note that the level-4 group has four digits, like an education code, so a four-digit value is not by itself proof that you are looking at hfaudd.
library(haven) # read_sas()
library(dplyr)
# Look first - the version and the exact names vary, so do not copy a filename
# from this page. Check both folders (see the note under "Where are the format
# tables?").
fmt_root <- "E:/Formater/SAS formater i Danmarks Statistik/SAS_datasaet/"
list.files(paste0(fmt_root, "Disced/"), pattern = "audd", ignore.case = TRUE)
list.files(paste0(fmt_root, "Uddannelser/"), pattern = "audd", ignore.case = TRUE)
# Then read the newest completed-education, main-area, level 1 -> 5 file in
# the type that matches hfaudd (c_ for character, n_ for numeric), code only.
# The filename below is the pattern to look for, not a confirmed name.
udda_level <- read_sas(paste0(fmt_root, "Disced/c_audd2023_hoved_l1l5_k.sas7bdat")) # (example) EDIT to the newest file
names(udda_level) # find the START column and the label column
head(udda_level)
# Join it on, then check how many found no match
udda_data <- udda_data %>%
mutate(hfaudd = as.character(hfaudd)) %>% # match the type in the format table
left_join(udda_level, by = c("hfaudd" = "START"))
udda_data %>% summarise(unmatched = sum(is.na(.data[[names(udda_level)[2]]])), total = n())Four things to check before you trust the result.
- Use the newest version, and only that one. The folders hold several versions side by side and it is tempting to think you need one per study year. You do not. DST’s guide says the education statistics are updated every year with the newest codes backwards in time as well, so the latest version already covers the older codes. One file, for the whole study period.
- DISCED-15 or an older classification. From 2015 DST uses DISCED-15, which replaced Forspalte, ISCED1997 and DUN. A file with no dimension in its name (for example
c_audd2015_l1l5_kt) may belong to one of the older classifications, whose levels and codes are not the ones in the table above. Check the top-level codes withhead()against the table before you use it. - Character or numeric.
c_expects a character variable,n_a numeric one. Pick the one matching your column, or convert - a type mismatch produces a join that matches nothing and fills the column withNAwithout any error. - The unmatched count. A high number means a type mismatch or the wrong file (
uddinstead ofaudd, or an old classification).
Paths on DST. The E:/Formater/... path above is the same for everyone working on a DST research machine, so it should work as written - but check it yourself before you rely on it, and expect it to differ if you are on a hosted machine. DST’s own guide gives the network paths as \\srvfsenas1\data\formater\SAS formater i Danmarks Statistik (researcher machine) and \\srvfsenas5\formater\SAS formater i Danmarks Statistik (hosted). The full guide is the PDF “Brugen af SAS formater i Danmarks Statistik” under E:/Formater/SAS formater i Danmarks Statistik/Vejledning mv/.
Why the level has to be looked up at all - and what it costs to guess it from the digits - is in Overview of registers. The full extraction, from opening UDDA to a categorised variable, is in Socioeconomic variables, which also covers the heaven shortcut.
Which variant to pick: _k, _t or _kt
Every format comes in up to three output variants. DST’s guide illustrates them with a municipality code turned into a region, using Copenhagen municipality (101):
| Suffix | Returns | Copenhagen (101) becomes |
|---|---|---|
_k |
the code only | 84 |
_t |
the text only | Hovedstaden |
_kt |
code and text together | 84 Hovedstaden |
Use _k for the analysis variable. The code is short, it sorts correctly, and it does not change if DST rewords a label in a later version. You can filter and group on it directly (%in% c("40", "50")), and a model or a case_when() written against codes keeps working.
Use _t for the labels in a finished table. Join it on at the end, when you build Table 1 or a figure, so the text never becomes something your code depends on.
Use _kt for looking, not for analysing. It is the most readable while you explore, because you see what each code means and the values still sort by code. But "40 Korte videregående uddannelser, KVU" is code and text glued into one value: filtering on it means matching the whole string, and it breaks if the wording changes.
Whichever you pick, the key is the same. All three join on START, the code in your own data. Only the returned column differs.
Tips for finding the right table
- Open the Star on the desktop or navigate to
E:/Formater/...in File Explorer - Use
list.files()on the subfolder, and expect several files per format (c_/n_times_k/_t/_kt) - Load the table and look at the columns with
names()andhead()
names(mun_table) # see what the columns are called - find START and the label column
head(mun_table) # see the first rows and understand the structureSee also
- Socioeconomic variables: SEPLINE approach with code examples for AKM, FAIK and UDDA