What this package is for
LASA (Longitudinal Aging Study Amsterdam) data is distributed as SPSS
(.sav) files. Each file covers one “file code” – a topic or
set of related questions – and one measurement “wave” – roughly, one
round of data collection. The same topic is usually asked again in later
waves, but with a wave-specific prefix on its variable names (e.g.
blphya01 in wave B), and sometimes with slightly different
numeric codes for the same answer categories. Reading a
.sav file with a generic tool like
haven::read_sav() gets you the raw numbers, but none of
that context: you’d have to look up LASA’s own documentation by hand to
know what column blphya01 even is, and repeat that work for
every wave.
CleanLASA does that lookup for you. It connects three
things that would otherwise all be separate, manual steps:
- finding the LASA topic, file code, and waves you need, and opening LASA’s own documentation for it;
-
importing a LASA
.savfile while automatically attaching the correct variable and value labels, for whichever specific file code and wave it is; and - checking that the labelling worked as expected, and fixing it by hand for the rare case where it didn’t.
The main function for step 2 is read_lasa_sav(). It
works out the LASA wave and file code from the file name, looks up the
matching metadata in the package’s built-in label database, and applies
it. If you’ve already imported the data some other way,
apply_lasa_labels() does the same labelling on its own.
A typical session works through these steps roughly in order:
find topic/file code -> inspect LASA documentation -> import .sav file
-> apply/reshape labels -> inspect matching audit -> analyse data
This vignette walks through all of it, one function at a time. The example code isn’t actually run when this vignette is built, because it needs real LASA data files and, for the documentation lookup, access to the live LASA website – neither of which is guaranteed to be available wherever the package happens to be installed.
1. Find topics, file codes, and waves
If you don’t already know the file code you need, start with
lasa_topics(). It searches the package’s built-in index of
LASA topics. Called with no arguments, it returns the whole index; you
can narrow it down by topic name, theme, subtheme, or file code
instead.
# Browse the complete LASA topic index
lasa_topics()
# Search by topic name; partial names and small typos are both fine
lasa_topics(topic = "physical act")
# Search a theme or subtheme
lasa_topics(theme = "cognitive")
lasa_topics(theme = "memory")
# Search by file code
lasa_topics("011")
# A 3-digit file code also matches processed/scaled variants that share
# the same final two digits -- this also returns the "245" row
lasa_topics("045")Matching a topic name is forgiving on purpose: it ignores
capitalization, accepts a partial name ("physical act"
matches "Physical activity"), and by default also tolerates
small typos through fuzzy matching. If you ever want exact matching
only, turn that off with fuzzy_match = FALSE;
max_edit_distance controls how many characters’ worth of
typo is still considered a match (default 2).
# Exact matching only, no typo tolerance
lasa_topics(topic = "physical activity", fuzzy_match = FALSE)
# Allow slightly bigger typos than the default
lasa_topics(topic = "phisical activty", max_edit_distance = 3)The result is a data frame with one row per topic/file-code
combination, with columns theme, subtheme,
topic, filecode, waves, and
has_varinfo (whether LASA links a variable-information PDF
for that row – see the next section). A topic that doesn’t have a file
code yet, or a file code with no linked PDF, still shows up in the
result rather than being left out – you’ll just see NA for
filecode, or FALSE for
has_varinfo.
This index is hand-maintained rather than scraped from LASA’s website
(see data-raw/lasa_topic_database.R in the package source),
so it only changes when the package itself is updated.
2. Open the matching LASA documentation
Once you know the topic or file code, lasa_var_info()
finds and opens LASA’s own documentation for it: either the official
variable-information PDF, or the topic’s page on the LASA website. Both
are looked up live, by briefly visiting the LASA topic overview – no
PDFs are bundled with the package itself.
# By file code
lasa_var_info("046")
lasa_var_info("LASA046") # common variations of a file code are recognized
lasa_var_info("LASA 046")
lasa_var_info("lasa_046")
# By topic name
lasa_var_info("Physical activity")
lasa_var_info("physical act") # fuzzy matching handles typos here too
# Open the topic's LASA webpage instead of the PDF (also accepts
# "webpage"/"website"/"browser"/"online", case-insensitively)
lasa_var_info("046", target = "web")
# Retrieve the URL without opening the document
url <- lasa_var_info("046", open = FALSE)
# Force a fresh lookup instead of using the cached link
lasa_var_info("046", refresh = TRUE)By default, in an interactive R session, the document opens straight
away in your system’s default web browser – not the RStudio Viewer,
which doesn’t reliably display hosted PDFs. Set
open = FALSE if you just want the URL back without opening
anything.
Not every file code has a linked PDF. When that happens,
lasa_var_info() automatically falls back to the topic’s
LASA webpage instead and prints a message explaining why – so asking for
the PDF never simply fails as long as some documentation
exists. This fallback only goes one way: if you explicitly ask for the
webpage (target = "web") and none can be found, that’s a
normal error.
Like lasa_topics(), topic-name matching here tolerates
typos through fuzzy_match (default TRUE) and
max_edit_distance (default 2). The underlying
link lookup itself is cached, both for the rest of your R session and on
disk between sessions, so lasa_var_info() doesn’t have to
re-fetch LASA’s website every time; use refresh = TRUE if
you suspect a link has changed since it was cached.
3. Import and label a LASA SPSS file
read_lasa_sav() is the function you’ll use most. It
reads the file with haven::read_sav(), figures out the LASA
wave and file code from the file name, looks up the matching metadata in
lasa_label_db(), and attaches variable and value
labels.
# Regular LASA wave: LASA + wave + file code
physical_activity_b <- read_lasa_sav("LASAB046.sav")
# Information stored in a Z file
sex <- read_lasa_sav("LASAZ004.sav")
# Replenishment/migrant-cohort waves use LAS2B/LAS3B/LAS4B/LASMB
physical_activity_3b <- read_lasa_sav("LAS3B046.sav")A Z file holds several waves side by side (boak,
coak, doak, … for the same question at waves
B, C, D, …). Add format_as_long = TRUE to get it in long
format instead – one row per respondent per wave – as described in section 9.
The file name is what tells read_lasa_sav() which wave
and file code it’s looking at, so it needs to follow LASA’s naming
convention (case doesn’t matter): regular single-letter waves are named
LASA[wave][filecode].sav, and the replenishment waves
2B, 3B, 4B, and MB
are named LAS[wave][filecode].sav. If your file doesn’t
follow this pattern, pass filecode/wave
yourself (see section 6).
read_lasa_sav() always reads the file with every code
intact, so CleanLASA – not whatever the .sav file happens
to declare – decides which codes are missing values (see Missing-value codes below). You can pass
any other haven::read_sav() argument through
read_sav_args:
data <- read_lasa_sav(
"LASAB046.sav",
read_sav_args = list(encoding = "UTF-8")
)(Don’t put file or user_na inside
read_sav_args – those are controlled by path
and keep_user_na directly, not by this list.)
Once imported, the result carries a few extra pieces of information about where it came from: which wave and file code it is, and the source file name. It also carries the full matching audit, described in section 5.
4. Choose how labelled variables are represented
By default, a single
read_lasa_sav()/apply_lasa_labels() call does
all of the following at once: categorical variables become R factors
(to_factor), count/continuous variables become plain
numbers (to_numeric), column names are standardized to one
name shared across every wave and a "Wave" column is added
(standardize, which also turns on
add_wavecode), and the wording that’s consistent across
every wave becomes each variable’s active label. Every one of these can
be switched off individually if you’d rather keep the raw SPSS-style
representation for some or all of them.
To see what changes, here’s the same file read the plain way first, with no CleanLASA processing at all:
data <- haven::read_sav("LASAB046.sav")
summary(data$blphya01)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> -5.000 4.000 4.000 3.476 4.000 4.000
attr(data$blphya01, "labels")
#> Na, interview terminated Na, short interview Na, wrong skip
#> -5 -4 -3
#> Na, see Na, asked Respondent bedridden
#> -2 -1 1
#> Respondent in elec. whee Respondent in mech. whee not 1 2 or 3
#> 2 3 4This is a plain SPSS-labelled numeric vector: the values are numbers, and the labels are stored as an attribute rather than actually applied. The rest of this section shows what each CleanLASA option changes about this.
Convert labelled categories to factors
to_factor defaults to TRUE, so this already
happened in the quick example above without any extra argument:
documented value labels become R factor levels.
data <- read_lasa_sav("LASAB046.sav")
summary(data$lphya01)
#> respondent bedridden respondent in elec. wheelchair
#> 20 5
#> respondent in mech. wheelchair not 1, 2 or 3
#> 20 2870
#> NA's
#> 192
attr(data$lphya01, "labels_wave")
#> Na, interview terminated Na, short interview Na, wrong skip
#> -5 -4 -3
#> Na, see Na, asked Respondent bedridden
#> -2 -1 1
#> Respondent in elec. whee Respondent in mech. whee not 1 2 or 3
#> 2 3 4The negative “Na, …” codes are missing values, so by default they
become NA (see Missing-value
codes below). If a non-negative value shows up in the data but
LASA’s documentation has no label for it, that value is kept as its own
factor level (shown as the number itself) rather than silently turned
into NA – so you’ll notice an undocumented code instead of
quietly losing data. Set to_factor = FALSE to leave the
variable as a plain numeric/character vector instead.
Variables that mean the same thing but were coded differently
A handful of variables were coded on genuinely different numeric
scales in different waves – for example a yes/no question where
0 meant “no” in one wave but 1 meant “no” in
another, or an income bracket that was redefined. There’s no single set
of value labels that correctly describes every wave for a variable like
this, so the label database marks it var_type == "text" and
doesn’t offer harmonized value labels for it at all.
to_factor handles this by converting the variable to its
wave-specific label text (a character vector, not a factor)
instead – so even though the underlying numeric codes disagree between
waves, the text itself ("no"/"yes") still
lines up, and the variable can still be compared or combined across
waves.
Restore count/continuous variables to plain numeric
Not every variable is categorical – some are genuine counts or
continuous measures, and the label database knows the difference (a
variable whose codebook has no positive/categorical codes, only negative
“missing”-reason ones, is classified as numeric).
to_numeric defaults to TRUE, so this also
already happened above: those variables come back as ordinary numbers,
with negative missing-reason codes converted to NA.
data <- haven::read_sav("LASAB046.sav")
summary(data$blphya09)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> -3.00 -1.00 20.00 32.58 45.00 480.00
data <- read_lasa_sav("LASAB046.sav")
summary(data$lphya09)
#> Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
#> 1.00 15.00 30.00 44.52 60.00 480.00 799to_factor and to_numeric work together, not
against each other: in the same call, categorical variables become
factors and numeric-classified variables become plain numbers.
Missing-value codes
LASA records why an answer is missing with negative codes,
such as -1 = “Na, asked” or -2 = “Na, see
BMOVED”. keep_user_na decides what happens to them, and it
does the same thing for every variable, whatever its type:
-
keep_user_na = FALSE(the default): every missing code becomesNA, in factors, label-text variables and numeric variables alike, and factors get no level for it. This is what you want for most analyses. -
keep_user_na = TRUE: every missing code is kept. Factors and label-text variables show the reason as text ("Na, see BMOVED"). A numeric variable can’t hold text, so it keeps the raw code (-2) as ahaven_labelled_spssvector, with the reason as its value label and the code declared missing:is.na()reports it as missing, andhaven::zap_missing()turns it intoNAwhen you need to compute with it.
data <- read_lasa_sav("LASAB014.sav", keep_user_na = TRUE)A code counts as missing when the label database flags it as one: every negative documented code, including labels such as “valid score” or “never had job” that explain why a value is missing. A negative value that isn’t documented at all counts as missing too, unless the variable is numeric and its genuine values can be negative, such as a speech-reception threshold in dB; then only its documented missing codes count, and every other negative value is kept as a measurement. Positive codes, such as “dropout” in the osteoarthritis files, are real answers and are never treated as missing.
Standardize variable names and separate the wave code
LASA variable names usually carry a wave-specific prefix – the same
question might be blphya01 in wave B but
clphya01 in wave C, even though it’s really the same
variable. The label database’s canonical name strips that
prefix off (lphya01 for both), and
.standardize_names (part of standardize,
described next) renames your columns to it, so the same variable has the
same name whichever wave you’re working with.
standardize (default TRUE) is really three
independent settings turned on together:
-
.standardize_names– rename every matched column to its canonical name, and rename the respondent-ID column (in any capitalization) to"respnr". This also inserts a"Wave"column right afterrespnr(the same thingadd_wavecodedoes on its own, described below). -
.standardize_var_labels– use the cross-wave-consistent wording as each variable’s active label, instead of that wave’s own wording. -
.standardize_val_labels– the same idea, but for value labels: use the harmonized codebook (where LASA’s documentation defines one) instead of the wave-specific one.
data <- read_lasa_sav("LASAB046.sav") # standardize = TRUE by default
names(data)[1:4]
#> "respnr" "Wave" "lphya01" "lphya02"
unique(data$Wave)
#> "B"If you’d rather keep the original, wave-specific column names but
still want to know which wave you’re looking at,
add_wavecode adds the "Wave" column on its
own, without renaming anything else:
data <- read_lasa_sav(
"LASAB046.sav",
.standardize_names = FALSE,
add_wavecode = TRUE
)
names(data)[1:4]
#> "respnr" "Wave" "blphya01" "blphya02"
unique(data$Wave)
#> "B"Correct a known source-column name
Occasionally a source file has a typo or a nonstandard column name
that doesn’t match either the wave-specific or canonical name CleanLASA
expects, so it wouldn’t be matched at all on its own. If you already
know which documented variable it actually is,
name_corrections tells CleanLASA directly, as
canonical_name = "actual name in the datafile":
data <- read_lasa_sav(
"LASAB046.sav",
name_corrections = c(lphya08 = "BLPYA08",
lphya09 = "BLPYA9")
)This mapping is applied before CleanLASA’s own automatic matching (exact, then case-insensitive, then fuzzy), so it always takes priority.
You often won’t need name_corrections at all:
fuzzy_matching (default TRUE) already catches
most typos on its own. When a documented variable can’t be found under
its expected name, CleanLASA tries a best-guess (edit-distance) match
against whichever of your data’s columns haven’t already been claimed by
something else. A unique close match is used, and recorded as
"fuzzy" in the matching audit (see the next section); if
there are two equally-good candidates, CleanLASA leaves it unmatched
rather than guessing.
Preserve the original coding for reference
Whatever
.standardize_var_labels/.standardize_val_labels
are set to, CleanLASA never throws away a matched variable’s
original, wave-specific label and value labels – they’re always
kept as the "wave_label" and "labels_wave"
attributes. This stays true even after converting to a factor or plain
number, so you can always check what R did against the original SPSS
coding:
5. Audit how variables were matched
Every read_lasa_sav() or
apply_lasa_labels() call keeps a record of exactly how each
variable was matched – or if it wasn’t. lasa_label_report()
returns that record:
report <- lasa_label_report(data)
reportBy default this returns the complete picture, in both directions:
variables that were matched successfully, documented variables that
LASA’s documentation says should be there but weren’t found in your data
(direction = "documented_not_in_data"), and columns in your
data that don’t correspond to anything documented
(direction = "data_not_documented"). The
method column records how each match was made –
exact, case-insensitive exact,
exact canonical, fuzzy,
manual correction, not found, or
undocumented column. Neither direction is ever treated as
an error: an unmatched variable is simply left as-is and reported here
for you to look at.
To skip straight to what might need your attention:
lasa_label_report(data, problems_only = TRUE)Run this before you start analysing the data. It confirms the
variables you expected were actually matched (and if not, is often
fixable with name_corrections), and flags columns in your
data that aren’t documented for this file code/wave – which might just
be analysis variables you added yourself, or something the label
database genuinely doesn’t cover yet. Either way, those columns were
never touched by the labelling engine, so nothing is lost.
6. Apply LASA labels to an existing data frame
apply_lasa_labels() is the same labelling engine
read_lasa_sav() uses, made available on its own. Use it
when you’ve already imported a .sav file some other way, or
need to re-apply labels after a transformation removed them.
raw <- haven::read_sav("LASAC046.sav", user_na = TRUE)
data <- apply_lasa_labels(
raw,
filecode = "046",
wave = "C"
) # to_factor/to_numeric/standardize all default to TRUEIf data already carries the file-code/wave information
that read_lasa_sav() (or an earlier
apply_lasa_labels() call) attached to it, you can usually
leave filecode/wave out entirely – CleanLASA
remembers:
data <- read_lasa_sav("LASAB046.sav")
# ...perform transformations that may remove column attributes...
data <- apply_lasa_labels(data)apply_lasa_labels() can also work out the wave from a
single-valued Wave column, if one is present (the same
column add_wavecode/ .standardize_names add).
Still, for a data frame that didn’t originate from
read_lasa_sav(), it’s most reliable to just tell it
filecode and wave directly.
7. Inspect the label database
lasa_label_db() returns the metadata database CleanLASA
is currently using – the bundled data shipped with the package, plus any
manual corrections you’ve made (see the next section). It’s a list with
four parts:
-
variables– one row per file-code/wave/variable combination: the variable’s name as it appears in that wave’s data, its canonical (shared-across-waves) name, its label for that specific wave, its cross-wave-consistent (“harmonized”) label, its type (numeric, categorical, or text), and whether its genuine values can be negative (can_be_negative). -
value_labels– the value/code labels (what each numeric code means) exactly as documented for that one wave, withis_missingmarking the missing-value codes. -
value_labels_harmonized– the same variable’s value labels, standardized across every wave that documents it (there’s nowavecolumn here – a row applies no matter which wave you’re looking at). Useful for combining data across waves that coded the same concept in slightly different words. A variable whose coding genuinely differs across waves (var_type == "text"invariables) has no rows here at all – see the earlier section on why. -
manual_overrides– your own hand-authored corrections, kept separately from the bundled data (see the next section).
db <- lasa_label_db()
# Which wave-B variables are currently known for file code 046?
subset(db$variables, filecode == "046" & wave == "B")
# Inspect the corresponding value-label rows
subset(db$value_labels, filecode == "046" & wave == "B")
# The cross-wave-standardized value labels for the same variable
subset(db$value_labels_harmonized, filecode == "046" & canonical_name == "lphya01")The package ships with a bundled snapshot of this database, hand-transcribed from LASA’s own variable-information documentation. Coverage grows, and mistakes get fixed, with package updates. If you record a manual correction (next section), CleanLASA writes a complete, updated copy of the database to a folder on your own computer, and uses that local copy in preference to the bundled one from then on – so your corrections stick around across R sessions without you having to touch the installed package itself.
8. Record a manual metadata correction
Sometimes LASA’s documentation is wrong, incomplete, or you just want
a clearer label for your own use.
manual_update_lasa_labels() is the escape hatch for that:
it records a correction in a separate override layer, so a later package
update won’t quietly wipe it out – the update only replaces the
bundled data your correction sits on top of.
By default, a supplied value label is merged into the existing set for that variable, leaving everything else untouched:
manual_update_lasa_labels(
filecode = "046",
wave = "B",
variable = "lphya01",
val_labels = c(`-5` = "NA, wrong, skip",
`-1` = "NA, not done")
)Set replace_val_labels = TRUE to replace the
entire value-label set for that variable instead of merging
into it:
manual_update_lasa_labels(
filecode = "046",
wave = "all",
variable = "lphya01",
val_labels = c(`-5` = "NA, wrong, skip",
`-1` = "NA, not done"),
replace_val_labels = TRUE
)The same function replaces a variable’s label (as opposed to
its value labels) through var_label:
manual_update_lasa_labels(
filecode = "046",
wave = "B",
variable = "lphya01",
var_label = "New label"
)var_label and val_labels can be combined in
one call, and wave accepts either "all" or a
vector of specific waves, so a single call can update several waves at
once:
manual_update_lasa_labels(
filecode = "046",
wave = "all", # update every wave currently covered (B-K)
variable = "lphya01",
var_label = "New label",
val_labels = c(`-5` = "NA, wrong, skip",
`-1` = "NA, not done")
)
manual_update_lasa_labels(
filecode = "046",
wave = c("B", "C", "D"), # only update these three waves
variable = "lphya01",
var_label = "New label",
val_labels = c(`-5` = "NA, wrong, skip",
`-1` = "NA, not done")
)Once recorded, a correction takes effect immediately: the next time
you call read_lasa_sav() or
apply_lasa_labels() on that file/variable, the corrected
label is what gets applied.
Undo a manual correction
manual_update_lasa_labels() writes a complete
local copy of the database, not just the one correction – so once that
local copy exists, it’s what CleanLASA actually uses, for every file
code, until you refresh it. restore_lasa_labels() is the
undo button: it can discard recorded corrections (either everything, or
just the filecode/wave/variable
you name), and/or rebuild that local copy from whatever the
currently-installed package version now ships, so a package update’s new
or corrected coverage actually reaches you instead of staying hidden
behind an old local copy.
# Undo one correction:
restore_lasa_labels(filecode = "046", variable = "lphya01")
# Undo every correction ever recorded locally:
restore_lasa_labels()
# Full reset: undo every correction AND rebuild the local snapshot from
# the currently-installed package's bundled database:
restore_lasa_labels(rebuild = TRUE)9. Reshape between long and wide format
LASA data comes in two shapes. A regular wave file such as
LASAB046.sav covers one wave, and
read_lasa_sav() returns it in long format: one row
per respondent, a "Wave" column, and column names without a
wave prefix (lphya01). A Z file such as
LASAZOA1.sav covers several waves in wide format:
one row per respondent and a separate column for every wave –
boak, coak, doak, … for knee
osteoarthritis at waves B, C, D, … – sometimes next to stable
variables that hold for every wave, like sex or year of birth in
LASAZ004.sav.
transform_lasa_data() converts between the two. Long
format is the default:
oa <- read_lasa_sav("LASAZOA1.sav")
names(oa)
#> "respnr" "Wave" "boak" "boah" "coak" "coah" "doak" "doah"
oa_long <- transform_lasa_data(oa)
names(oa_long)
#> "respnr" "Wave" "Time" "oak" "oah"
head(oa_long, 3)
#> # A tibble: 3 x 5
#> respnr Wave Time oak oah
#> <dbl> <chr> <int> <fct> <fct>
#> 1 1 B 0 no no
#> 2 1 C 1 dropout dropout
#> 3 1 D 2 dropout at previous waves dropout at previous wavesread_lasa_sav() does both steps at once with
format_as_long = TRUE; the default FALSE keeps
the file’s own layout:
oa_long <- read_lasa_sav("LASAZOA1.sav", format_as_long = TRUE)In long format, "Wave" holds the LASA wave code and
"Time" counts the waves since the respondent’s baseline,
the wave at which their cohort joined LASA. Every baseline is Time 0,
and each cohort counts its own later waves, skipping the other cohorts’
baselines:
| Cohort | Time 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| First | B | C | D | E | F | G | H | I | J | K | L |
| Second | 2B | F | G | H | I | J | K | L | |||
| Third | 3B | I | J | K | L | ||||||
| Migrant | MB | I | J | K | L | ||||||
| Fourth | 4B | L |
So wave F is Time 4 for the first cohort, but Time 1 for the second.
Wave A, the LSN pilot study, isn’t part of the main cohort study: its
rows keep their own "Wave" "A", without a
Time. "Time" counts waves, not years, and it isn’t LASA’s
own wave numbering, which LASAZ008.sav uses
(t2_dat is wave B there). For time itself, use the
interview dates or ages in LASAZ008.sav.
A row’s Time needs the respondent’s cohort, which the data have to
show: a value at the respondent’s baseline or at a wave only their
cohort takes part in (B to E for the first cohort), any row of a file of
such a wave – every respondent in LASAC046.sav belongs to
the first cohort, even one whose row holds nothing but missing-value
codes, and every respondent in LASMB046.sav to the migrant
cohort, whatever their number says – LASA’s cohort column,
as in LASAZ002.sav (“cohort 1 (1992)” has baseline B,
cohort 2 2B, cohort 3 3B), or the respondent number. LASA numbers its
respondents with five digits, the first of which says the cohort: 1, 2,
or 3 the first, 4, 5, or 6 the second, and 7, 8, or 9 the third. A 1
also numbers the LSN pilot study’s respondents, so it only counts for a
respondent the data show at another wave than A, by a value there other
than a missing-value code. The fourth and the migrant cohort’s numbers
aren’t known yet: if they have five digits too, they say one of the
first three cohorts until the rest of the data show their own. Where the
rest of the data show another cohort than the number, they decide, with
a warning.
The waves from F on are shared, so a file of wave F or later, read on
its own, can’t tell the cohorts apart by its waves: its rows get their
Time from the respondent numbers, and the rows of respondents whose
number doesn’t say get no Time, with a message saying so, until the file
is combined with files that do (see below), or with the
cohort column:
f030 <- read_lasa_sav("LASAF030.sav", format_as_long = TRUE) # Time by respondent number
z002 <- read_lasa_sav("LASAZ002.sav")
f030 <- transform_lasa_data(merge(f030, z002[c("respnr", "cohort")], by = "respnr"))A respondent gets a row for each wave at which they have a value –
anything but NA or blank text (which is how SPSS stores
missing text). Missing-value codes become NA by default;
kept with keep_user_na = TRUE, they count for the rows, but
not as showing a cohort. Stable variables are repeated on each of the
respondent’s rows. Each long column keeps the variable and value labels
its waves share, and each wave’s own wording in its
"wave_label" and "labels_wave" attributes, one
entry per wave:
attr(oa_long$oak, "wave_label")
#> B C D
#> "symptomatic knee OA at B" "symptomatic knee OA at C" "symptomatic knee OA at D"format = "wide" goes back to one row per respondent.
prefix chooses how the wave-specific columns are named: by
the wave’s letter ("Wave", the default) or by its
"Time":
names(transform_lasa_data(oa_long, format = "wide"))
#> "respnr" "boak" "boah" "coak" "coah" "doak" "doah"
names(transform_lasa_data(oa_long, format = "wide", prefix = "Time"))
#> "respnr" "cohort" "t0_oak" "t0_oah" "t1_oak" "t1_oah" "t2_oak" "t2_oah"With prefix = "Time", the cohorts share columns:
t0_oak holds each respondent’s value at their own baseline,
t1_oak at their cohort’s next wave, and so on. The
cohort column says which cohort each respondent belongs to,
so the columns convert back to the right waves. It is LASA’s own cohort
variable, coded as read_lasa_sav() reads
LASAZ002.sav’s ("cohort 1 (1992)",
"cohort 2 (2002)", "cohort 3 (2012)";
"4" for the fourth cohort, whose code the label database
doesn’t document yet, and "migrant cohort" for the cohort
that file doesn’t have), so the result merges with that file by
respnr and cohort, as long as the file is read
with to_factor = TRUE, the default. Data sets merged by
respnr alone get cohort.x and
cohort.y, which transform_lasa_data() combines
again, as it combines cohort with COHORT, the
name haven::read_sav() gives that file’s column:
oa_time <- transform_lasa_data(oa_long, format = "wide", prefix = "Time")
oa_time <- merge(oa_time, z002, by = c("respnr", "cohort"))
oa_long <- transform_lasa_data(oa_time)A value needs a Time to get a Time-prefixed name, so
prefix = "Time" stops with an error naming a respondent
with values (other than missing-value codes) on rows without one, such
as a respondent seen only at waves the cohorts share (F and later) whose
number doesn’t say their cohort; prefix = "Wave" works for
any data. The cohort column also needs its name: rename a
cohort column of your own (birth cohorts, say) first.
Columns that came from a wide LASA file get their original type and
labels back, and with prefix = "Wave" their original name
too, as long as that name says which wave the column holds. LASA’s wave
files give the baselines of the later cohorts (2B, 3B, 4B, and MB) the
same b prefix as wave B, so in wide format these waves get
the prefixes b2, b3, b4, and
mb instead, as LASA’s own
zoa2/zoa3 files do: the b_dm
column of LASAZDC2.sav, which holds the second cohort’s
baseline, becomes b2dm.
transform_lasa_data() also works after merging files –
for example all 046 and 030 files of the first two cohorts. Combining
files in long format is the most reliable: read each file with
format_as_long = TRUE, stack the files of each file code,
merge the stacks by respondent and wave, and transform the result once
more, so that every row gets its Time now that the baselines show each
respondent’s cohort:
read_long <- function(file) read_lasa_sav(file, format_as_long = TRUE)
waves <- c("LASAB", "LASAC", "LAS2B", "LASAF")
# The files of different waves hold different variables, so they are
# stacked with dplyr::bind_rows(); rbind() needs files with the same columns
physical_activity <- dplyr::bind_rows(lapply(paste0(waves, "046.sav"), read_long))
adl <- dplyr::bind_rows(lapply(paste0(waves, "030.sav"), read_long))
long <- merge(physical_activity, adl, by = c("respnr", "Wave"), all = TRUE)
long <- transform_lasa_data(long)
wide <- transform_lasa_data(long, format = "wide")Reading each file with format_as_long = TRUE matters:
each file’s rows get their Time while the file still shows the
respondents’ cohort (every row of LASAB046.sav is a
first-cohort baseline), which the stacked files may no longer show for a
respondent whose rows hold nothing but missing-value codes. Merged by
respondent and wave, the two "Time" columns become
Time.x and Time.y, which
transform_lasa_data() combines into one "Time"
again. Stacking and merging also drop the columns’ labels, or keep one
file’s for every row; transform_lasa_data() gives each
variable the label database knows its labels back, with each wave’s own
wording in "wave_label" and "labels_wave".
Files can also be merged in wide format, read without standardizing
and merged by respondent – with all = TRUE, as above, or
respondents missing from one of the files (such as those who dropped out
after wave B) are left out:
wide <- Reduce(
function(x, y) merge(x, y, by = "respnr", all = TRUE),
list(
read_lasa_sav("LASAB046.sav", standardize = FALSE),
read_lasa_sav("LASAC046.sav", standardize = FALSE),
read_lasa_sav("LASAB030.sav", standardize = FALSE),
read_lasa_sav("LASAC030.sav", standardize = FALSE)
)
)
long <- transform_lasa_data(wide)merge() drops the attributes that say which file the
data came from, though, so the values in b columns of a
respondent the data show nowhere else, such as one who dropped out after
wave B, are placed by their respondent number, or else by a guess (see
below), which gives them no Time. Combine the baseline files of the
migrant and the fourth cohort (LASMB046.sav,
LAS4B046.sav), whose respondent numbers aren’t known yet,
in long format, so their rows keep their wave.
To add a Z file to long data, merge its wide columns by
respnr (without its "Wave" placeholder column,
or read it with standardize = FALSE);
transform_lasa_data() then moves each value to the row of
its own wave, adding rows for waves the long data didn’t have yet – also
for respondents the long data lack, whom a merge with
all = TRUE gives a row without a wave. Merging data sets
that each have a "Wave" column by respnr alone
gives copies such as Wave.x and Wave.y, which
transform_lasa_data() refuses: merge long data by
respnr and "Wave". A file of stable variables
only, such as LASAZ004.sav or the migrant cohort’s
LASMB004.sav, has no wave dimension, so with
format_as_long = TRUE it has no "Wave" column:
merge it by respnr alone, and its variables are filled in
on each of the respondent’s rows.
Some LASA names stand for several waves: every cohort’s baseline (B,
2B, 3B, MB, and 4B) uses the same b names, so a wide column
such as blphya01 may belong to any of them.
transform_lasa_data() places each respondent’s value at the
wave their data show – the row it is on, the respondent’s cohort (also
by their respondent number), or the file it was read from. If nothing
shows it, it guesses, with a message naming the column, and a guessed
wave doesn’t count as showing the respondent’s cohort (their row gets a
Time only from something else, such as their respondent number) – also
when the result is transformed again, as long as its columns keep their
attributes (rbind(), merge(),
dplyr::bind_rows(), sorting or subsetting the rows of a
data frame rather than a tibble, and saving to a .sav file
can drop them). To avoid guesses, combine such files in long format,
where each row carries its own wave, rather than in wide format (such as
LASAB046.sav and LAS2B046.sav read with
standardize = FALSE).
10. A complete analysis-oriented workflow
Putting it all together, most analyses only need a handful of calls:
library(CleanLASA)
# 1. Find the relevant LASA topic and file code
lasa_topics(topic = "physical activity")
# 2. Inspect the official variable-information document if needed
lasa_var_info("046")
# 3. Import the LASA file -- to_factor/to_numeric/standardize (names,
# variable labels, value labels) all default to TRUE, and missing-value
# codes become NA (keep_user_na = FALSE)
data <- read_lasa_sav("LASAB046.sav")
# 4. Check only matching problems before analysis
problems <- lasa_label_report(data, problems_only = TRUE)
problemsIf a file code isn’t covered by the active label database yet,
read_lasa_sav() doesn’t guess or invent metadata for it –
there’s no per-file fallback function it quietly switches to. Either add
a deliberate correction with manual_update_lasa_labels(),
or wait for a package update that adds coverage, then import (or
re-apply labels to) the data again.
Function summary
| Function | Main purpose |
|---|---|
lasa_topics() |
Search LASA topics, themes/subthemes, file codes, and available waves |
lasa_var_info() |
Resolve and open the LASA variable-information PDF or webpage for a file code or topic |
read_lasa_sav() |
Read a LASA .sav file, identify wave/file code, and
apply database-backed labels |
apply_lasa_labels() |
Apply the same LASA labelling engine to an existing data frame |
transform_lasa_data() |
Reshape LASA data between wide format (Z files) and long format with
Wave and Time columns |
lasa_label_report() |
Inspect the full matching audit or only entries requiring attention |
lasa_label_db() |
Inspect the active bundled + user-local label metadata database |
manual_update_lasa_labels() |
Add or correct metadata in a persistent manual override layer |
restore_lasa_labels() |
Undo manual corrections (entirely, or scoped) and/or rebuild the local database cache from the bundled one |
Together, these functions make LASA’s own documentation part of your R workflow: topics and source documents stay easy to find, imported variables keep their documented meaning, every transformation stays auditable, and the metadata layer can be refreshed without you having to maintain separate labelling code for every file.