Tutorials › Generalized Linear Mixed Models (GLMM) › Auditing Cluster and Subject Identifiers

Software and Case Studies · Tutorial 656 of 698

Auditing Cluster and Subject Identifiers

Use design-defined checks in R to identify identifier problems, decide which require correction, and document a defensible audit before model fitting.

Intermediate 12 min read

What You'll Learn

  • Define the observation key that determines whether an identifier combination should be unique.
  • Find missing cluster, subject, and observation identifiers with explicit R checks.
  • Detect duplicate keys while distinguishing data errors from legitimate repeated observations.
  • Check whether subjects are assigned consistently to clusters when the design requires nesting.
  • Identify suspicious differences in identifier spelling, spacing, capitalization, and storage type.
  • Correct confirmed coding problems transparently and report the audit results.

Why identifiers need an audit

A GLMM can only represent the intended grouping structure if the identifiers supplied to the analysis correspond to the study’s actual clusters and subjects. A missing cluster label may leave an observation’s grouping unclear; a duplicated identifier may combine different people; and small coding differences can split one real cluster into multiple recorded levels. Conversely, repeated subject labels or repeated rows are not automatically errors: whether they are valid depends on the observation unit and study design.

The goal of an identifier audit is therefore not to make every identifier unique or to delete every duplicate. It is to compare the recorded labels with rules derived from the design, flag records that violate those rules, and resolve discrepancies against source records or documented study procedures. The checks below use base R and a hypothetical data frame named dat.

Definition: An identifier audit is a set of checks that compares recorded cluster and subject labels with the study’s declared grouping structure and observation key. The observation key is the combination of variables that should identify one record uniquely under that design. For example, if each subject contributes one record per occasion within a cluster, the key might be cluster ID, subject ID, and occasion. A key is not defined by the data alone; it must follow the observation unit and sampling structure.

Set the design rules before checking data

Before running code, write down what one row represents, which identifiers should be present on every row, and which combinations should be unique. Also determine whether subjects are nested in clusters, can belong to more than one cluster, or may have cluster membership that changes over time. In a simple nested design, each subject belongs to one cluster and a subject label may be unique only within that cluster. In that case, the subject’s identity is the pair of cluster ID and subject ID, not necessarily the subject ID alone.

For instance, subject 12 in cluster A and subject 12 in cluster B may be different people if the study assigned local subject numbers within each cluster. They are not duplicate subjects merely because the local numbers match. If the protocol instead guarantees globally unique subject IDs, the same labels across clusters require investigation. The audit must reflect the protocol rather than impose a convenient assumption.

A useful sequence is to inspect variable names and types, check missing identifiers, check the design-defined key, assess subject-to-cluster assignments, and then inspect coding inconsistencies. Resolve missing values before interpreting duplicate-key results: an incomplete key cannot establish which observation a record belongs to.

Worked Example: Missing identifiers and repeated observation keys

Suppose a hypothetical study records one response per subject at each scheduled occasion within a cluster. The declared observation key is cluster ID, subject ID, and occasion. Consider this data:

R code: dat <- data.frame(cluster_id = c("A","A","A","B","B"), subject_id = c("01","01","02","01",NA), occasion = c(1,1,1,1,1), response = c(0,1,0,1,0))

The first check identifies incomplete keys:

R code: key_vars <- c("cluster_id", "subject_id", "occasion"); missing_key <- !complete.cases(dat[key_vars]); which(missing_key)

The result is row 5, because its subject ID is missing. The count of rows with any missing key component is sum(missing_key) = 1. This flags an unresolved record; it does not tell us which subject produced it.

Among complete keys, rows 1 and 2 share the key (A, 01, 1). Check both appearances of every repeated key with:

R code: complete_dat <- dat[!missing_key, ]; is_dup <- duplicated(complete_dat[key_vars]) | duplicated(complete_dat[key_vars], fromLast = TRUE); complete_dat[is_dup, ]

The output contains rows 1 and 2. There are two records for the same cluster, subject, and occasion, although the design says there should be one. This is a duplicate-key warning, not proof of which record is correct. The response values differ, so the analyst should check source records or data-entry history; neither record should be discarded solely because the code found a duplicate.

If multiple records per subject and occasion are actually allowed—for example, because the observation unit is a trial—the key is incomplete. The correct response is to revise the key using the design, not to force the data to satisfy an incorrect uniqueness rule.

Check missingness and uniqueness in R

A first-pass report can make missingness explicit. The following code reports missing counts for the two grouping identifiers:

R code: colSums(is.na(dat[c("cluster_id", "subject_id")]))

This returns a separate count for each column. If blank strings may have been used for missing labels, R does not treat "" as NA. Check for them separately with sum(dat$cluster_id == "", na.rm = TRUE) and the analogous subject-ID check. Do not silently convert blanks to a valid identifier or assume that every blank has the same explanation.

For a key with several columns, duplicated() can be applied to the corresponding data-frame subset. To inspect all records participating in repeated keys, use the two-direction check shown above: one call identifies later occurrences, while the call with fromLast = TRUE identifies earlier occurrences too. This is more informative than reporting only the number of duplicated rows, because it displays the full records for review.

A record can be duplicated in one sense but valid in another. Two observations from the same subject are expected in a repeated-observation design; two observations with the same full observation key may violate the design. Thus, test the declared key, not merely whether a subject ID appears more than once.

Check subject-to-cluster consistency

When the design requires each subject to belong to exactly one cluster, check that rule using complete identifier pairs. This check is not appropriate for designs in which subjects can move between clusters or be observed under multiple clusters; those designs need a rule based on their actual membership structure.

For a nested design, the following code counts the number of distinct recorded clusters per subject ID after missing labels are excluded:

R code: pairs <- unique(dat[complete.cases(dat[c("subject_id", "cluster_id")]), c("subject_id", "cluster_id")]); n_cluster <- vapply(split(pairs$cluster_id, pairs$subject_id), function(x) length(unique(x)), integer(1)); n_cluster[n_cluster > 1]

Any returned subject has been recorded in more than one cluster. That can indicate a mistyped ID, an incorrect cluster label, or a design feature that invalidates the nesting assumption. If subject identifiers are only unique within clusters, use the pair as the subject identity and first confirm that the protocol really intends local rather than global numbering. The code above deliberately groups by subject label alone, so it is a diagnostic for global subject labels under a one-cluster-per-subject rule.

Worked Example: Finding a subject assigned to two clusters

Suppose a hypothetical nested study has these complete ID pairs: (A, 01), (A, 02), (B, 03), and (C, 03), where each pair is written as (cluster ID, subject ID). The protocol says each globally identified subject belongs to one cluster.

R code: pairs <- data.frame(cluster_id = c("A","A","B","C"), subject_id = c("01","02","03","03")); n_cluster <- vapply(split(pairs$cluster_id, pairs$subject_id), function(x) length(unique(x)), integer(1)); n_cluster[n_cluster > 1]

For subject 03, the distinct cluster labels are B and C, so the count is length(unique(c("B","C"))) = 2. The output flags subject 03. It does not establish that B or C is the erroneous label. The analyst should compare the records with the enrollment list or other authoritative source. If the protocol allowed movement between clusters, the finding would need interpretation under that design rather than automatic correction.

Look for inconsistent coding

A label may be present and unique yet still be coded inconsistently. For character identifiers, spaces at the beginning or end and differences in capitalization are common warning signs. Inspect the original values before changing them:

R code: sort(unique(dat$cluster_id)); sort(unique(trimws(dat$cluster_id)))

If both "North" and "North " occur, trimming whitespace may be justified, but only after confirming that the difference is accidental. A case-folded comparison can reveal labels that differ only by capitalization: sort(unique(tolower(trimws(dat$cluster_id)))). This is a diagnostic, not a replacement for checking whether case carries meaning in the study’s identifier system.

Storage type also matters. Character ID "01" and numeric ID 1 may refer to the same code, or they may not. Converting a character ID to numeric can erase leading zeros, and it can make distinct labels unusable if IDs contain letters. Keep identifiers in a representation that preserves their intended labels; do not treat them as measured quantities just because they contain digits.

If the source confirms that leading or trailing spaces are accidental, a cleaned copy can be created while retaining the original for traceability:

R code: dat$cluster_id_original <- dat$cluster_id; dat$cluster_id <- trimws(dat$cluster_id_original)

This example changes whitespace only. It does not automatically merge different capitalization, recode IDs, or resolve conflicting assignments. After any correction, rerun the missingness, uniqueness, and membership checks. A successful check after transformation is not a substitute for documenting why the transformation was valid.

Worked Example: Auditing whitespace without hiding the change

A hypothetical data extract has cluster labels "East", "East ", and "West". Assume the codebook confirms that trailing spaces are accidental and that labels are otherwise case-sensitive. The initial inspection is:

R code: unique(c("East", "East ", "West"))

It returns three distinct strings. After preserving the original values, use trimws():

R code: cluster_original <- c("East", "East ", "West"); cluster_clean <- trimws(cluster_original); unique(cluster_clean)

The cleaned vector contains two distinct labels: East and West. The operation has consolidated two spellings into one label because the codebook established that the space was not meaningful. Record the affected rows and the rule applied, then rerun the relevant identifier checks. If the codebook had not confirmed this, the apparent discrepancy should remain a query for resolution rather than being silently merged.

Audit workflow, alternatives, and limitations

1
Write the design rules.
State the observation unit, required identifiers, and the combination that must uniquely identify a record.
2
Flag missing and incomplete keys.
Count missing values and inspect blank strings before interpreting duplicate-key results.
3
Test key uniqueness and membership.
Inspect all records with repeated keys and check subject-to-cluster assignments only when the design specifies the relevant rule.
4
Investigate coding discrepancies.
Compare suspicious labels with the codebook or source records; make only supported corrections, preserve the original, and rerun checks.
5
Document the outcome.
Report the checks performed, records flagged, resolutions, and any unresolved identifier uncertainty before fitting a model.

For a small data set, direct inspection with base R functions such as unique(), duplicated(), is.na(), and table() is transparent and easy to reproduce. For a larger project, the same rules can be implemented in a scripted validation report or a data-validation framework. The alternative is not a different statistical test: identifier errors are usually resolved by design documentation and source-data review, not by model fit statistics.

Automated checks have limits. They can show that a label is missing, repeated, or inconsistent with a stated rule, but they cannot determine the intended identity when records conflict. A duplicate key may reflect a data-entry error, an omitted variable in the key, or a genuinely repeated record. A subject appearing in two clusters may be a coding mistake or a valid feature of the design. No model diagnostic can repair an incorrectly defined grouping variable after the fact.

Common pitfall: “Subject ID 01 appears many times, so those rows are duplicates.” This reasoning confuses repeated observations with duplicated observation keys. In a longitudinal or otherwise repeated-observation design, multiple rows per subject are expected. Define the full key from the observation unit and flag only repeated combinations that the design says should occur once.

Check Your Understanding

Answer each question, then compare your reasoning with the solutions.

  1. A study has one row per subject per occasion. Which key would you check if subject numbers are unique only within clusters?
  2. In R, how can you display every row participating in a repeated key, rather than only later occurrences?
  3. A subject label occurs under two clusters. What must be established before calling this an error?
  4. Why should character IDs with leading zeros generally not be converted to numeric values as an automatic cleaning step?
  5. A label differs from another only by a trailing space. What should happen before merging the labels?

Solutions. (1) Check the combination of cluster ID, subject ID, and occasion; the local subject number alone is not globally identifying. (2) Combine duplicated(dat[key_vars]) with duplicated(dat[key_vars], fromLast = TRUE) using OR, then subset the data by that logical result. (3) Establish the protocol’s membership rule and whether subject IDs are globally unique; a design allowing cluster changes may make the observation valid. (4) Numeric conversion can remove leading zeros and destroy the intended identifier label, so preserve the representation specified by the codebook. (5) Inspect the source or codebook to confirm that whitespace is accidental, preserve the original value, apply a documented correction only when justified, and rerun the checks.

Clinical Biostats