Package {uSDT}


Type: Package
Title: Hierarchical Signal Detection Theory Models for Unconscious Processing
Version: 0.1.0
Description: Fits hierarchical signal detection theory (SDT) models to paired direct and indirect measures, the design used to test for unconscious processing. Continuous indirect measures (typically response times) are dichotomized with the within-subject median split of Meyen et al. (2022) <doi:10.1037/xge0001065> so that both tasks are placed on a common sensitivity scale. The package estimates a binomial probit mixed model in which the two sensitivities are correlated random effects, and tests the three hypotheses of interest: the group-level difference between sensitivities, their latent correlation, and the latent regression of the indirect on the direct measure, whose intercept is the test for unconscious processing. Frequentist estimation uses 'lme4'.
License: GPL-3
Encoding: UTF-8
LazyData: true
LazyDataCompression: xz
Depends: R (≥ 4.1)
Imports: stats, utils, lme4, ggplot2, rlang
Suggests: testthat (≥ 3.0.0), knitr, rmarkdown
VignetteBuilder: knitr
Config/testthat/edition: 3
Config/Needs/website: pkgdown
URL: https://github.com/RicardoReySaez/uSDT, https://ricardoreysaez.github.io/uSDT/
BugReports: https://github.com/RicardoReySaez/uSDT/issues
Config/roxygen2/version: 8.1.0
NeedsCompilation: no
Packaged: 2026-09-18 17:31:16 UTC; 34653
Author: Ricardo Rey-Sáez ORCID iD [aut, cre], Francisco Garre-Frutos ORCID iD [aut], Alicia Franco-Martínez ORCID iD [aut], Miguel Vadillo ORCID iD [aut]
Maintainer: Ricardo Rey-Sáez <ricardoreysaez95@gmail.com>
Repository: CRAN
Date/Publication: 2026-09-29 13:40:02 UTC

uSDT: Hierarchical Signal Detection Theory for Unconscious Processing

Description

Fits hierarchical signal detection theory models to paired direct and indirect measures. This is the design used to test whether a stimulus is processed without awareness.

The workflow

  1. usdt_data_long() or usdt_data_tasks() prepare the data. Printing the result reports how every column was read, which tasks were split at the median, and what the model will do with all of it.

  2. sdt_moments() gives descriptive estimates for each subject.

  3. hsdt() fits the model and tests the three hypotheses.

  4. plot() and usdt_reliability() help to interpret the fit, and usdt_boot() adds intervals by simulation when the model needs them.

The three hypotheses

H1

The difference between the average sensitivities of the two tasks.

H2

The correlation between the two sensitivities across subjects.

H3

The regression of the indirect sensitivity on the direct one. Its intercept is the sensitivity expected in the indirect task from a subject whose direct sensitivity is zero, which is the test for unconscious processing.

Confidence intervals

H1 and the regression of H3 use Wald intervals. The correlation of H2 uses a Fisher-z interval, so its limits stay between -1 and 1. When the model reaches a boundary and an interval becomes unreliable, the package reports it as unavailable and explains why.

Author(s)

Maintainer: Ricardo Rey-Sáez ricardoreysaez95@gmail.com (ORCID)

Authors:

See Also

Useful links:


Fit a hierarchical signal detection theory model

Description

Fits a bivariate hierarchical SDT model using lme4::glmer() and evaluates the core unconscious processing hypotheses. The model estimates task-specific sensitivities (d') and response criteria (c) as fixed effects, while estimating their variation and correlation across participants via random effects.

Usage

hsdt(
  data,
  estimation = c("frequentist"),
  fix_criteria = c("auto", "none"),
  level = 0.95,
  optimizer = "bobyqa",
  ...
)

## S3 method for class 'hsdt'
summary(object, ...)

## S3 method for class 'hsdt'
print(x, ...)

Arguments

data

A usdt_data object from usdt_data_tasks() or usdt_data_long().

estimation

Estimation framework. Currently only "frequentist" (maximum likelihood via Laplace approximation) is supported.

fix_criteria

How to handle response criteria. "auto" fixes to zero any criterion that is zero by design (such as a task split at the median under deviation coding). "none" estimates all criteria.

level

Confidence level for Wald intervals (default is 0.95).

optimizer

Primary optimizer passed to lme4::glmerControl(). Alternative optimizers are automatically evaluated if the default fails to converge or produces a singular fit.

...

Additional arguments passed to lme4::glmer(). Model formula, family, and data inputs remain managed by the package.

object

An hsdt object.

x

An hsdt object.

Details

The model fits trial counts with a binomial probit link, directly mapping coefficients to standard Signal Detection Theory parameters. Fixed effects capture population sensitivities and criteria, while random effects estimate participant variation and the latent correlation between direct and indirect sensitivity.

Hypotheses evaluated by default:

When sample sizes or trial counts are low, variance components can reach singular boundaries. In these cases, the function issues a warning, and parametric bootstrap intervals can be calculated using usdt_boot().

Value

An object of class hsdt containing:

See Also

usdt_data_tasks(), usdt_tests(), usdt_boot(), plot.hsdt()

Examples

# Contextual cuing data from Vadillo et al. (2025)
d <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)

m <- hsdt(d)

# Full summary table with SDT parameters and hypothesis tests
summary(m)

# Inspect the model formula (indirect criterion omitted by default)
m$design$formula


Dichotomize response times into binary choices

Description

Splits response times (or other continuous measures) at each subject's overall median, following the preprocessing approach of Meyen et al. (2022). The median is calculated across all trials for each participant without distinguishing between stimulus conditions or other covariates. This produces a binary outcome that allows response times to be mapped onto a Signal Detection Theory sensitivity metric (d').

Usage

meyen_split(x, by, signal = c("faster", "slower"), ties = c("noise", "random"))

Arguments

x

Numeric vector of continuous values, typically response times.

by

Vector identifying the subject for each observation in x. Medians are computed independently for each participant.

signal

Character string specifying which side of the median will be treated as the "signal" response under an SDT framework. Use "faster" when the target condition speeds up responses (e.g., facilitatory priming, spatial cueing) or "slower" when it slows responses down (e.g., interference, Stroop-like effects).

ties

How to handle trials that match the subject's median exactly. "noise" assigns them to the noise category (0). "random" breaks ties at random, keeping cell proportions as balanced as possible.

Details

With an odd number of trials (n), a dataset cannot be split into two equal halves because the median falls exactly on an observed trial. Setting ties = "noise" assigns this middle trial to noise, producing a signal proportion of (n - 1) / (2\cdot n) and slightly shifting the response criterion. In practice, this difference (1 / (2\cdot n)) is negligible, but setting ties = "random" resolves ties probabilistically to avoid any systematic directional bias.

Value

An integer vector of 0s (noise response) and 1s (signal response) matching the length of x. Missing values (NA) are preserved.

References

Meyen, S., Zerweck, I. A., Amado, C., von Luxburg, U., & Franz, V. H. (2022). Advancing research on unconscious priming: When can scientists claim an indirect task advantage? Journal of Experimental Psychology: General, 151(1), 65–81. doi:10.1037/xge0001065

Examples

rt   <- c(320, 410, 295, 500, 380, 450)
subj <- rep(c("s1", "s2"), each = 3)
meyen_split(rt, by = subj)


Diagnostic and analytical plots for hierarchical SDT models

Description

Generates diagnostic visualizations for a fitted hierarchical SDT model, including observed versus latent regression (H3), shrinkage patterns, participant-level caterpillar intervals, and model-implied ROC curves.

Usage

## S3 method for class 'hsdt'
plot(
  x,
  type = c("regression", "shrinkage", "caterpillar", "roc"),
  subject_id = NULL,
  band = TRUE,
  population_reference = TRUE,
  observed_se = NULL,
  ...
)

Arguments

x

An hsdt object fitted by hsdt().

type

Character string indicating the plot type:

  • "regression": Compares the observed OLS regression with the latent regression line (H3).

  • "shrinkage": Connects observed d' to model-implied d' for each participant across bivariate contours.

  • "caterpillar": Compares observed d' and model-implied d' with confidence intervals for each participant alongside zero.

  • "roc": Shows model-implied ROC curves with estimated criteria.

subject_id

Identifier for a specific participant when type = "roc". If omitted, displays population-level curves.

band

Logical. Show confidence bands around regression lines or ROC curves (default is TRUE).

population_reference

Logical. When plotting an individual ROC curve, add population-average curves as dashed reference lines.

observed_se

Variance formulation for empirical intervals in "caterpillar" plots: "gg" (Gourevitch & Galanter, 1967, default) or "miller" (Miller, 1996).

...

Additional arguments passed to underlying plotting methods.

Value

A ggplot object. Its underlying data frame is stored in ⁠$data⁠.

Regression plot (type = "regression")

Compares observed and latent associations across two panels sharing axes. The left panel shows observed d' values and an ordinary least-squares line. When direct task reliability is low, trial-level sampling noise attenuates this observed slope toward zero. The right panel plots the latent regression line (d'_I on d'_D) from H3, correcting for measurement error. The value of each line at d'_D = 0 marks the intercept testing for unconscious processing, and both panels display it the same way: an open circle at the point estimate with a vertical line spanning its confidence interval. The observed marker is the least-squares intercept and the latent marker is its measurement-error-corrected counterpart, so the two panels place the same hypothesis side by side. Confidence bands are computed via the delta method or bootstrap replicates when usdt_boot() is present.

Shrinkage plot (type = "shrinkage")

Connects each participant's observed d' (from sdt_moments() with Hautus correction) to their model-implied d'. The lower the reliability of the measures, the higher the shrinkage of observed estimates toward the group-level mean.

Caterpillar plot (type = "caterpillar")

Plots observed d' and model-implied d' with confidence intervals for every participant. Empirical intervals use normal approximations based on observed_se. Model-implied intervals incorporate uncertainty from population means, variance components, and participant random effects.

ROC plot (type = "roc")

Displays model-implied ROC curves for an average participant or a specific individual, with points marking the estimated response criteria.

See Also

hsdt(), sdt_moments(), usdt_boot()

Examples

# Contextual cuing data from Vadillo et al. (2025)
d <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)

fit <- hsdt(d)

# 1. Observed vs. latent regression (H3)
plot(fit, type = "regression")

# 2. Bivariate shrinkage toward the group mean
plot(fit, type = "shrinkage")

# 3. Participant-level intervals (observed vs. model-implied)
plot(fit, type = "caterpillar")

# 4. Model-implied ROC curves
plot(fit, type = "roc")
plot(fit, type = "roc", subject_id = "2001", population_reference = TRUE)


Signal detection measures for each subject

Description

Computes empirical hit rates, false-alarm rates, sensitivity (d'), and response criteria for each participant without fitting a model. It can also calculate sampling variances, standard errors, and expected values for d'.

Usage

sdt_moments(
  data,
  subject_col = NULL,
  condition_col = NULL,
  condition_levels = NULL,
  response_col = NULL,
  response_levels = NULL,
  coding = c("deviation", "treatment"),
  correction = c("hautus", "none"),
  variances = FALSE
)

Arguments

data

A usdt_data object or a standard trial-level data frame. When given a usdt_data object, the function processes both tasks and includes a task column in the output.

subject_col, condition_col, response_col

Column names for subject, condition, and response variables. Only required when data is a plain data frame.

condition_levels, response_levels

Named vectors mapping condition and response labels, like c(signal = "old", noise = "new"). Required for a plain data frame. A usdt_data object supplies its own roles and needs neither.

coding

Criterion definition to report: "deviation" measures the criterion from the midpoint between the signal and noise distributions, whereas "treatment" measures it from the noise distribution. A usdt_data object supplies its own coding.

correction

Handling of extreme rates (0 or 1) that make d' infinite. "hautus" adds 0.5 to all four cell counts for affected participants. "none" leaves infinite values in place.

variances

Logical. If TRUE, computes the sampling variance of d' from Gourevitch and Galanter (1967) and Miller (1996), each with its own standard error, as well as the expected value of d' under Miller's distribution.

Details

Edge corrections apply only to participants with extreme rates (0 or 1) rather than the whole sample, leaving well-defined rates unchanged.

When requested, the sampling variance of d' is estimated using the asymptotic approximation of Gourevitch and Galanter (1967) and the binomial-distribution approach of Miller (1996). See Suero et al. (2017) for a comparison between the two approaches.

Value

A data frame with one row per subject (or per subject and task for usdt_data inputs) containing:

References

Gourevitch, V., & Galanter, E. (1967). A significance test for one parameter isosensitivity functions. Psychometrika, 32(1), 25–33. doi:10.1007/BF02289402

Hautus, M. J. (1995). Corrections for extreme proportions and their biasing effects on estimated values of d'. Behavior Research Methods, Instruments, & Computers, 27(1), 46–51. doi:10.3758/BF03203619

Miller, J. (1996). The sampling distribution of d'. Perception & Psychophysics, 58(1), 65–72. doi:10.3758/BF03205476

Suero, M., Privado, J., & Botella, J. (2017). Methods to estimate the variance of some indices of the signal detection theory: A simulation study. Psicologica, 38(1), 77–109.

See Also

hsdt()

Examples

# 1. From a prepared usdt_data object (both tasks at once)
d <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)

head(sdt_moments(d))

# 2. From raw trials with sampling variances and standard errors
head(sdt_moments(vadillo_awareness,
                 subject_col      = "subj",
                 condition_col    = "condition",
                 condition_levels = c(signal = "old", noise = "new"),
                 response_col     = "judged.old",
                 response_levels  = c(signal = 1, noise = 0),
                 variances        = TRUE))


Parametric bootstrap intervals for hierarchical SDT models

Description

Simulates new datasets from a fitted model using lme4::bootMer(), refits the model to each replicate, and computes bootstrap confidence intervals for the three core hypotheses (H1, H2, H3). This is especially useful when asymptotic Wald intervals are unreliable due to singular or boundary fits.

Usage

usdt_boot(
  object,
  nsim = 1000,
  ncores = 1L,
  max_attempts = 2 * nsim,
  seed = NULL,
  progress = interactive(),
  level = 0.95,
  type = c("perc", "norm", "basic")
)

Arguments

object

An hsdt object fitted by hsdt().

nsim

Target number of successful replicates (at least 500).

ncores

Number of CPU cores for parallel processing. Values above 1 create a temporary cluster that works across Windows, macOS, and Linux, and automatically stops when finished.

max_attempts

Maximum number of refits to attempt. Defaults to 2 * nsim.

seed

Random seed for reproducibility. With the default NULL, no seed is set and the bootstrap continues the current random number stream.

progress

Logical. Display a progress bar during fitting (defaults to TRUE in interactive sessions).

level

Confidence level for intervals (default is 0.95).

type

Type of bootstrap interval: "perc" (percentile), "norm" (normal approximation with bias correction), or "basic" (empirical basic). For correlations, "norm" and "basic" operate on the Fisher-z scale; if the sample correlation lies on the boundary (-1 or 1), these types return NA, whereas "perc" remains available.

Details

Replicates are dropped only if the model fails to fit or does not converge. Singular fits and boundary estimates are intentionally retained because discarding them artificially narrows intervals in constrained settings.

When an attempted run finishes with fewer than 500 usable replicates, the original Wald intervals are preserved, a warning is issued, and raw attempt diagnostics are stored in ⁠$boot⁠.

Point estimates remain identical to the original model fit. Two-sided bootstrap p-values compare the observed test statistic against the centered bootstrap distribution using standard finite-sample adjustment ((k + 1) / (B + 1)), ensuring p-values never equal zero.

Value

An updated hsdt object where interval columns in ⁠$tests⁠ are replaced by bootstrap estimates. A new ⁠$boot⁠ element contains:

See Also

hsdt(), usdt_tests()

Examples

# Contextual cuing data from Vadillo et al. (2025)
d <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)

m <- hsdt(d)


# Run parametric bootstrap with 500 replicates. Refitting this model 500
# times takes several minutes
b <- usdt_boot(m, nsim = 500, seed = 1)

# Inspect updated summary with bootstrap intervals and p-values
summary(b)

# Check fit diagnostics across bootstrap replicates
b$boot[c("usable", "attempted", "retained", "failures")]



Prepare data for hierarchical SDT models

Description

Formats direct and indirect task data into a standardized structure for hsdt(). Use usdt_data_tasks() when tasks are stored in separate data frames, or usdt_data_long() when both tasks are kept in a single data frame with a column that identifies each task.

Usage

usdt_data_tasks(
  direct,
  indirect,
  subject_col,
  condition_col = NULL,
  condition_levels = NULL,
  response_col = NULL,
  response_levels = NULL,
  successes_col = NULL,
  trials_col = NULL,
  successes_type = c("auto", "counts", "proportions"),
  sdt_cols = NULL,
  dichotomize = c("none", "indirect", "direct", "both"),
  ties = c("noise", "random"),
  coding = c("deviation", "treatment"),
  labels = c(direct = "Direct", indirect = "Indirect")
)

usdt_data_long(
  data,
  task_col,
  task_levels,
  subject_col,
  condition_col = NULL,
  condition_levels = NULL,
  response_col = NULL,
  response_levels = NULL,
  successes_col = NULL,
  trials_col = NULL,
  successes_type = c("auto", "counts", "proportions"),
  sdt_cols = NULL,
  dichotomize = c("none", "indirect", "direct", "both"),
  ties = c("noise", "random"),
  coding = c("deviation", "treatment"),
  labels = NULL
)

## S3 method for class 'usdt_data'
print(x, ...)

Arguments

direct, indirect

Data frames for each task (used in usdt_data_tasks()).

subject_col

Name of the column that identifies participants.

condition_col

Name of the column for signal and noise conditions. Not needed when using sdt_cols.

condition_levels

Named vector mapping condition labels, like c(signal = "old", noise = "new"). Required: which label is the signal and the noise.

response_col

Name of the column with responses. Can be binary choices or continuous values (like response times) to split at the median.

response_levels

Named vector mapping responses, like c(signal = 1, noise = 0). For a task named in dichotomize it names the side of the median instead, as c(signal = "faster", noise = "slower") or the reverse. Required in both cases.

successes_col, trials_col

Names of columns with pre-calculated counts or proportions of signal responses and total trials. Use these instead of response_col.

successes_type

Format of successes_col: "counts", "proportions", or "auto".

sdt_cols

Named vector for SDT table columns, like c(hit = "H", miss = "M", fa = "FA", cr = "CR").

dichotomize

Which tasks to split at the median using meyen_split(). Use "none", "direct", "indirect", "both", or a list like list(direct = FALSE, indirect = TRUE).

ties

How to handle trials that fall exactly on the median. See meyen_split().

coding

How condition is coded in the model: "deviation" (-0.5, 0.5) or "treatment" (0, 1). See Details.

labels

Optional names for the tasks in printed output.

data

A single data frame with both tasks (used in usdt_data_long()).

task_col

Name of the column that identifies the task in data.

task_levels

Named vector mapping task labels, like c(direct = "D", indirect = "I").

x

A usdt_data object.

...

Ignored.

Details

The functions count responses for each subject and condition, check that the same subjects appear in both tasks, and print a summary table so you can verify the column settings before fitting the model.

Value

An object of class usdt_data. The ⁠$agg⁠ table contains the counts used by hsdt(), and ⁠$meta⁠ contains setup details and summaries.

Settings per task

Arguments for columns and levels take either a single value (used for both tasks) or a list with separate settings for each task:

condition_col    = list(direct = "cond", indirect = "cue")
condition_levels = list(direct   = c(signal = "old", noise = "new"),
                        indirect = c(signal = "congruent", noise = "incongruent"))
dichotomize      = list(direct = FALSE, indirect = TRUE)

This works for all column and level arguments, so you can combine trial-level data in one task with summary tables in the other.

Condition coding

Under "deviation" coding (-0.5 vs. +0.5), the intercept is -c, the criterion measured from the point between the two distributions. Under "treatment" coding (0 vs. 1), the intercept is z(\mathrm{FAR}), the criterion measured from the noise distribution.

When a task is split at the median, deviation coding sets the group criterion to zero in balanced designs, so the model does not need to estimate it.

See Also

hsdt(), meyen_split(), sdt_moments()

Examples

# 1. Tasks in separate data frames
# Direct task: binary choices (old/new)
# Indirect task: response times (split at the median)
d_separate <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)
d_separate

# 2. Tasks combined in a single long data frame
long <- rbind(
  data.frame(task      = "D",
             subj      = vadillo_awareness$subj,
             condition = vadillo_awareness$condition,
             response  = vadillo_awareness$judged.old),
  data.frame(task      = "I",
             subj      = vadillo_cuing$subj,
             condition = vadillo_cuing$condition,
             response  = vadillo_cuing$rt)
)

d_long <- usdt_data_long(
  long,
  task_col         = "task",
  task_levels      = c(direct = "D", indirect = "I"),
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = "response",
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)
d_long


Test the three core hypotheses of a hierarchical SDT model

Description

Evaluates the difference between average task sensitivities (H1), their correlation across subjects (H2), and the regression of indirect sensitivity on direct sensitivity (H3). usdt_tests() computes all three together, while individual functions compute them separately.

Usage

usdt_tests(fit, direct = "d_D", indirect = "d_I", level = 0.95)

sensitivity_diff(fit, direct = "d_D", indirect = "d_I", level = 0.95)

latent_cor(fit, direct = "d_D", indirect = "d_I", level = 0.95)

latent_regression(fit, direct = "d_D", indirect = "d_I", level = 0.95)

Arguments

fit

A fitted model: an hsdt object or a glmerMod from lme4::glmer().

direct, indirect

Character strings naming the sensitivity terms in the model. Defaults match the internal naming of hsdt(). For custom models, both terms must be fixed effects and share a common random-effects grouping by subject.

level

Confidence level for intervals (default is 0.95).

Details

These tests run automatically inside hsdt() and appear in its summary. Calling them directly is especially useful when fitting custom models with lme4::glmer(), allowing you to test these hypotheses while controlling for additional covariates (e.g., set size, experimental groups).

Value

A data frame with columns term, estimate, se, statistic, p.value, conf.low, conf.high, and ci_method. Columns status and reason flag unsupported estimates (e.g., singular fits). usdt_tests() includes an extra hypothesis column (H1, H2, H3).

The three hypotheses

Because both the correlation (H2) and regression slope (H3) are zero if and only if the covariance between sensitivities is zero, they evaluate the same association and share identical test statistics.

Custom models with covariates

To adjust tests for additional factors, specify the model directly using lme4::glmer(). As long as the two sensitivity terms are included as fixed effects and correlated across subjects via random slopes, usdt_tests() will compute the latent tests conditional on those covariates.

See Also

hsdt(), usdt_boot()

Examples

# 1. Standard model via hsdt()
d <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)

m <- hsdt(d)

# All three tests at once
usdt_tests(m)

# Or one test at a time
sensitivity_diff(m)   # H1
latent_cor(m)         # H2
latent_regression(m)  # H3


# 2. Custom model with covariates via glmer()
# Controlling for display set size across both tasks
trials <- rbind(
  data.frame(vadillo_awareness[c("subj", "condition", "set.size")],
             task = "D", resp = vadillo_awareness$judged.old),
  data.frame(vadillo_cuing[c("subj", "condition", "set.size")],
             task = "I", resp = meyen_split(vadillo_cuing$rt,
                                            by = vadillo_cuing$subj))
)

# Deviation contrasts (-0.5 vs 0.5); `direct` flags the direct task
trials <- within(trials, {
  cond   <- ifelse(condition == "old", 0.5, -0.5)
  size   <- ifelse(set.size == "set size 16", 0.5, -0.5)
  direct <- as.integer(task == "D")
})

# Standard glmer formula: indirect criterion is omitted (fixed at 0
# by the median split). Random effects estimate the direct criterion
# and correlated task sensitivities across subjects.
fit <- lme4::glmer(
  resp ~ 0 + direct + task:size + task:cond +
    (0 + direct | subj) + (0 + task:cond | subj),
  data = trials, family = binomial("probit"),
  control = lme4::glmerControl(optimizer = "bobyqa")
)

# Check the names lme4 assigned to the sensitivity terms
names(lme4::fixef(fit))

# Evaluate hypotheses conditional on set size
usdt_tests(fit, direct = "taskD:cond", indirect = "taskI:cond")



Reliability of direct and indirect task measures

Description

Estimates the reliability of sensitivity (d') by separating true variance across participants from sampling noise caused by finite trial counts. Values close to 1 indicate that the measure reliably separates participants, whereas values near 0 indicate that observed differences are mostly measurement error.

Usage

usdt_reliability(object)

## S3 method for class 'usdt_reliability'
summary(object, ...)

## S3 method for class 'usdt_reliability'
print(x, ...)

Arguments

object

An hsdt object from hsdt(), which may also contain bootstrap results from usdt_boot().

...

Ignored.

x

A usdt_reliability object.

Details

For participant i in task j, reliability is defined as:

\frac{\tau_j^2}{\tau_j^2 + v_{ij}}

where \tau_j^2 is the true variance in sensitivity across participants from the model's random effects, and v_{ij} is the squared standard error of d' for that participant. This error variance reflects how precisely their trials determine sensitivity, accounting for trial count, performance level on the probit curve, and uncertainty in the criterion.

This variance is calculated using the large-sample Fisher information formula from Gourevitch and Galanter (1967). Unlike sdt_moments(), which evaluates that formula at raw empirical proportions (var_gg), usdt_reliability() evaluates it at the response probabilities predicted by the fitted hierarchical model.

Overall task reliability averages v_{ij} across participants, representing the expected proportion of true variance for a participant drawn at random from the sample.

When object includes bootstrap replicates from usdt_boot(), confidence intervals for reliability are computed automatically across all retained samples.

Value

An object of class usdt_reliability containing:

References

Gourevitch, V., & Galanter, E. (1967). A significance test for one parameter isosensitivity functions. Psychometrika, 32(1), 25–33. doi:10.1007/BF02289402

See Also

hsdt(), usdt_boot(), sdt_moments()

Examples

# Contextual cuing data from Vadillo et al. (2025)
d <- usdt_data_tasks(
  direct   = vadillo_awareness,
  indirect = vadillo_cuing,
  subject_col      = "subj",
  condition_col    = "condition",
  condition_levels = c(signal = "old", noise = "new"),
  response_col     = list(direct = "judged.old", indirect = "rt"),
  response_levels  = list(direct   = c(signal = 1, noise = 0),
                          indirect = c(signal = "faster", noise = "slower")),
  dichotomize      = list(direct = FALSE, indirect = TRUE)
)

r <- usdt_reliability(hsdt(d))
r

# Participant-level estimates (one row per subject and task)
head(r$subjects)


Awareness data from a probabilistic cuing experiment

Description

Trial-level direct-awareness data from Experiment 2 of Vadillo et al. (2025). The object reproduces the source CSV file without filtering or recoding and can be used as the direct input to usdt_data_tasks().

Usage

vadillo_awareness

Format

A data frame with 6,656 rows, 104 participants, and 10 variables:

subj

Participant identifier.

file

Original participant file name.

trial

Trial number.

pattId

Pattern identifier.

judgm

Awareness judgement on the original response scale.

judged.old

Whether the pattern was judged old (1) or new (0).

condition

Whether the pattern was old or new.

offset

Spatial-offset condition.

color

Colour condition.

set.size

Set-size condition.

Source

Vadillo, M. A., Malejka, S., & Shanks, D. R. (2025). Mapping the reliability multiverse of contextual cuing. Journal of Experimental Psychology: Learning, Memory, and Cognition. doi:10.1037/xlm0001410. Data retrieved from https://osf.io/jp3gx/.

Examples

data(vadillo_awareness)
str(vadillo_awareness)

Cuing data from a probabilistic cuing experiment

Description

Trial-level indirect-task data from Experiment 2 of Vadillo et al. (2025). The object reproduces the source CSV file without filtering or recoding and can be used as the indirect input to usdt_data_tasks().

Usage

vadillo_cuing

Format

A data frame with 39,936 rows, 104 participants, and 13 variables:

experiment

Experiment label.

subj

Participant identifier.

file

Original participant file name.

trial

Trial number.

block

Block number.

epoch

Epoch number.

pattId

Pattern identifier.

acc

Response accuracy (1 = correct, 0 = incorrect).

rt

Response time in milliseconds.

condition

Whether the pattern was old or new.

offset

Spatial-offset condition.

color

Colour condition.

set.size

Set-size condition.

Source

Vadillo, M. A., Malejka, S., & Shanks, D. R. (2025). Mapping the reliability multiverse of contextual cuing. Journal of Experimental Psychology: Learning, Memory, and Cognition. doi:10.1037/xlm0001410. Data retrieved from https://osf.io/jp3gx/.

Examples

data(vadillo_cuing)
str(vadillo_cuing)

mirror server hosted at Truenetwork, Russian Federation.