| Title: | Statistical Inference on Lineup Fairness |
|---|---|
| Description: | Computes statistical measures for laboratory and police lineups in forensic psychology, including lineup bias, functional and effective size, diagnosticity, confidence-accuracy curves, signal detection models, and simulation tools. Optional face-similarity functions use numeric face representations computed by Python's 'deepface' library; optional lineup-memory model fitting uses a separately installed, pinned 'pyWitness' engine. |
| Authors: | Colin Tredoux [aut, cre] (Co-author of the original package), Tamsyn Naylor [aut] (Co-author of the original package) |
| Maintainer: | Colin Tredoux <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 2.3.0 |
| Built: | 2026-11-02 18:39:38 UTC |
| Source: | https://github.com/CGTZA2/r4lineups |
Function to compute ci high for each foil in a lineup
allfoil_cihigh(linetabprops, sumlineup)allfoil_cihigh(linetabprops, sumlineup)
linetabprops |
A dataframe of bootstrapped lineup proportions |
sumlineup |
Number of mock-witness observations used to estimate each proportion. |
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
# Upper CI bounds for the choice proportions of three lineup members, # based on 20 mock witnesses allfoil_cihigh(c(0.5, 0.3, 0.2), 20)# Upper CI bounds for the choice proportions of three lineup members, # based on 20 mock witnesses allfoil_cihigh(c(0.5, 0.3, 0.2), 20)
Function to compute bias for each lineup member (assuming foil is suspect, from Malpass, 1981)
allfoilbias(lineup_tab, target_pos, k)allfoilbias(lineup_tab, target_pos, k)
lineup_tab |
A table of lineup choices |
target_pos |
A scalar, representing target position in lineup. Must be declared by user |
k |
Nominal size (i.e., total number of members in lineup). Must be specified by user (scalar). |
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) x <- allfoilbias(lineup_tab, 5, 6)lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) x <- allfoilbias(lineup_tab, 5, 6)
Computes lineup proportion for each member in a lineup
allprop(lineup_vec, k)allprop(lineup_vec, k)
lineup_vec |
A numeric vector of lineup choices |
k |
Number of members in lineup. Must be specified by user (scalar). |
Returns a vector containing lineup proportion for each lineup member
Wells, G. L., Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: x <- allprop(lineup_vec, k = 6)#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: x <- allprop(lineup_vec, k = 6)
Returns a character vector of available face detection backends.
available_detectors()available_detectors()
Available detectors (in rough order of accuracy/speed tradeoff):
retinaface - Most accurate, recommended for research
mtcnn - Good accuracy, moderate speed
mediapipe - Fast, good for real-time
opencv - Fastest, less accurate
ssd - Fast CNN-based
dlib - Classic HOG-based
yolov8 - YOLO-based detection
centerface - Anchor-free detection
skip - Skip detection (use full image)
Character vector of detector names.
available_detectors()available_detectors()
Returns a character vector of available face recognition models that can be used for embedding extraction.
available_models()available_models()
Available models with their embedding dimensions:
VGG-Face (4096-d) - Legacy model
Facenet (128-d) - Google's FaceNet
Facenet512 (512-d) - FaceNet with larger embeddings
OpenFace (128-d) - Open source
DeepFace (4096-d) - Facebook's model
DeepID (160-d) - Compact model
ArcFace (512-d) - State-of-the-art, recommended
Dlib (128-d) - Lightweight
SFace (128-d) - Lightweight
GhostFaceNet (512-d) - Efficient
Character vector of model names.
available_models()available_models()
Efficiently extracts face embeddings from multiple images.
batch_embeddings( img_paths, model = "ArcFace", detector = "retinaface", align = TRUE, enforce_detection = TRUE, progress = TRUE )batch_embeddings( img_paths, model = "ArcFace", detector = "retinaface", align = TRUE, enforce_detection = TRUE, progress = TRUE )
img_paths |
Character vector of paths to image files. |
model |
Character. The face recognition model to use. Default is "ArcFace". |
detector |
Character. Face detection backend. Default is "retinaface". |
align |
Logical. Whether to align faces. Default is TRUE. |
enforce_detection |
Logical. Error if face not detected. Default is TRUE. |
progress |
Logical. Show progress bar. Default is TRUE. |
A tibble with columns:
img_path - Path to the image
embedding - List column containing embedding vectors
facial_area - List column with face bounding box
face_confidence - Detection confidence
success - Logical indicating if extraction succeeded
error - Error message if extraction failed
## Not run: # Get embeddings for all images in a folder img_files <- list.files("faces/", pattern = "\\.jpg$", full.names = TRUE) embeddings <- batch_embeddings(img_files) # Access individual embeddings embeddings$embedding[[1]] ## End(Not run)## Not run: # Get embeddings for all images in a folder img_files <- list.files("faces/", pattern = "\\.jpg$", full.names = TRUE) embeddings <- batch_embeddings(img_files) # Access individual embeddings embeddings$embedding[[1]] ## End(Not run)
Computes bootstrap confidence intervals for the parameters of the 2-HT model by resampling the observed data with replacement.
boot_winter_2ht( x, nboot = 1000, conf_level = 0.95, lineup_size = 6, method = c("percentile", "bca"), parallel = FALSE, ncpus = 2, seed = NULL, ... )boot_winter_2ht( x, nboot = 1000, conf_level = 0.95, lineup_size = 6, method = c("percentile", "bca"), parallel = FALSE, ncpus = 2, seed = NULL, ... )
x |
A winter_2ht object from fit_winter_2ht(), OR count data/data frame as in fit_winter_2ht(). |
nboot |
Number of bootstrap samples. Default is 1000. |
conf_level |
Confidence level for intervals. Default is 0.95. |
lineup_size |
Lineup size (only needed if x is count data, not a winter_2ht object). |
method |
Type of bootstrap CI: "percentile" (default) or "bca" (bias-corrected and accelerated). |
parallel |
Logical indicating whether to use parallel processing. Default is FALSE. |
ncpus |
Number of CPUs to use if parallel = TRUE. Default is 2. |
seed |
Random seed for reproducibility. Default is NULL. |
... |
Additional arguments passed to fit_winter_2ht(). |
The bootstrap procedure resamples within TP and TA strata with replacement to keep both experimental totals fixed. For each bootstrap sample, the 2-HT model is re-fitted, and the distribution of parameter estimates across bootstrap samples is used to construct confidence intervals.
Two types of confidence intervals are available:
percentile: Uses quantiles of the bootstrap distribution
bca: Bias-corrected and accelerated intervals (Efron, 1987). The bias correction counts bootstrap ties with the estimate as half, and the acceleration comes from the jackknife over individual observations, centered and scaled within TP/TA strata, using at most six distinct leave-one-out response-count fits.
An object of class "winter_2ht_boot" containing:
boot_estimates |
Matrix of bootstrap parameter estimates (nboot x 4) |
ci |
Matrix of confidence intervals for each parameter |
original_fit |
The original model fit |
nboot |
Number of bootstrap samples |
conf_level |
Confidence level used |
Efron, B. (1987). Better bootstrap confidence intervals. Journal of the American Statistical Association, 82(397), 171-185.
counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) # Bootstrap CIs boot_fit <- boot_winter_2ht(fit, nboot = 200, seed = 123) print(boot_fit) plot(boot_fit)counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) # Bootstrap CIs boot_fit <- boot_winter_2ht(fit, nboot = 200, seed = 123) print(boot_fit) plot(boot_fit)
Computes bootstrap confidence intervals around ANRI estimates using percentile method.
bootstrap_anri( data, confidence_bins, choosers_only = TRUE, lineup_size = 6, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )bootstrap_anri( data, confidence_bins, choosers_only = TRUE, lineup_size = 6, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )
data |
Dataframe with standard lineup format |
confidence_bins |
Numeric vector of bin edges |
choosers_only |
Logical. Whether to analyze only suspect IDs (default = TRUE) |
lineup_size |
Integer. Lineup size (default = 6) |
n_bootstrap |
Integer. Number of bootstrap replications (default = 1000) |
conf_level |
Numeric. Confidence level for CIs (default = 0.95) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
confidence_scale |
How the confidence scale is interpreted: "auto" (default),
"0-1", or "0-100". See |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
Bootstrap resampling procedure: 1. Resample observations with replacement 2. Compute ANRI for each bootstrap sample 3. Construct percentile-based confidence interval
The percentile method uses the empirical quantiles of the bootstrap distribution. For a 95
A list containing:
anri: Point estimate of ANRI
nri: Point estimate of NRI
ci_lower: Lower bound of bootstrap CI
ci_upper: Upper bound of bootstrap CI
conf_level: Confidence level used
n_bootstrap: Number of bootstrap replications
bootstrap_distribution: Vector of bootstrap ANRI values
Efron, B., & Tibshirani, R. J. (1994). An Introduction to the Bootstrap. Chapman & Hall/CRC.
set.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) boot_result <- bootstrap_anri(data, confidence_bins = seq(0, 100, 20), n_bootstrap = 200, seed = 1) c(boot_result$ci_lower, boot_result$ci_upper)set.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) boot_result <- bootstrap_anri(data, confidence_bins = seq(0, 100, 20), n_bootstrap = 200, seed = 1) c(boot_result$ci_lower, boot_result$ci_upper)
Computes Bayesian posterior distributions for the calibration statistic C, over/underconfidence (O/U), and the Normalized Resolution Index (NRI) by placing independent Beta priors on the accuracy rate in each confidence bin.
calibration_bayes( data, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, alpha = 0.5, S = 10000, credible_mass = 0.95, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )calibration_bayes( data, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, alpha = 0.5, S = 10000, credible_mass = 0.95, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )
data |
A dataframe with columns |
confidence_bins |
Numeric vector of bin edges for confidence (e.g.,
|
choosers_only |
Logical. If |
lineup_size |
Integer. Number of lineup members (default 6). |
alpha |
Prior concentration for the Beta prior on per-bin accuracy.
Default 0.5 (Jeffreys). Named shortcuts: |
S |
Number of posterior draws per bin. Default 10000. |
credible_mass |
Width of equal-tailed credible intervals. Default 0.95. |
confidence_scale |
How the confidence scale is interpreted: "auto" (default),
"0-1", or "0-100". See |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
For each confidence bin with responses and correct
identifications, the posterior accuracy is:
For each posterior draw , the calibration statistics are computed:
where is the mean confidence (proportion scale) in bin and
is the overall mean confidence.
This model assumes exchangeable Bernoulli correctness within each bin.
In experiments with fixed TP and TA totals, conditioning on the number of
identifications in a bin does not generally give a binomial correct count.
These intervals are therefore conditional working-model summaries, rather
than automatically exact posterior inference for that fixed-arm design.
If innocent IDs are estimated from fillers, the fractional effective counts
produce a working Beta posterior, not the exact likelihood for observed
designated-suspect counts. Its intervals do not propagate all uncertainty
in that estimation. Bin frequencies and mean confidence are conditioned on.
An object of class "calibration_bayes" containing:
Posterior draws of each statistic.
Posterior mean, median, and credible interval for C.
Same for O/U.
Same for NRI.
Frequentist C, OU, NRI from make_calibration_data().
Per-bin summary (n, n_correct, mean_confidence, accuracy).
Metadata.
Juslin, P., Olsson, N., & Winman, A. (1996). Calibration and diagnosticity of confidence in eyewitness identification. Journal of Experimental Psychology: Learning, Memory, and Cognition, 22(5), 1304-1316.
Brewer, N., & Wells, G. L. (2006). The confidence-accuracy relationship in eyewitness identification. Journal of Experimental Psychology: Applied, 12(1), 11-30.
make_calibration_data, esize_T_bayes
set.seed(42) n <- 200 conf <- sample(c(50, 70, 90), n, replace = TRUE) tp <- sample(c(TRUE, FALSE), n, replace = TRUE) ident <- ifelse(tp, "suspect", sample(c("suspect","filler","reject"), n, replace=TRUE)) df <- data.frame(target_present = tp, identification = ident, confidence = conf) res <- calibration_bayes(df, confidence_bins = c(0, 60, 80, 100)) print(res) plot(res)set.seed(42) n <- 200 conf <- sample(c(50, 70, 90), n, replace = TRUE) tp <- sample(c(TRUE, FALSE), n, replace = TRUE) ident <- ifelse(tp, "suspect", sample(c("suspect","filler","reject"), n, replace=TRUE)) df <- data.frame(target_present = tp, identification = ident, confidence = conf) res <- calibration_bayes(df, confidence_bins = c(0, 60, 80, 100)) print(res) plot(res)
Verifies that all required Python packages are installed and reports their versions.
check_python_deps(verbose = TRUE)check_python_deps(verbose = TRUE)
verbose |
Logical. If TRUE, prints detailed information. |
A tibble with package names, installed status, and versions. Its
"face_mesh" attribute says whether get_face_landmarks() can
run: "solutions", "tasks" or "unavailable".
## Not run: check_python_deps() ## End(Not run)## Not run: check_python_deps() ## End(Not run)
Reports whether Python and pyWitness are available without initializing Python by default.
check_pywitness_deps( envname = .pywitness_default_env, initialize = FALSE, verbose = TRUE )check_pywitness_deps( envname = .pywitness_default_env, initialize = FALSE, verbose = TRUE )
envname |
Name or path of the intended virtual environment. |
initialize |
Logical. If |
verbose |
Logical. Print a concise status report. |
A one-row tibble with environment and engine status, including
whether the imported version encodes the audited revision when
initialize = TRUE.
check_pywitness_deps() ## Not run: check_pywitness_deps(initialize = TRUE) ## End(Not run)check_pywitness_deps() ## Not run: check_pywitness_deps(initialize = TRUE) ## End(Not run)
Function for getting chi-squared value for homogeneity of diagnosticity ratios
chi_diag(df)chi_diag(df)
df |
A dataframe containing: ln(d), variance of ln(d), d weights |
To compute linedf, use the diag_param helper function.
To compute df, apply ln_diag_ratio, var_lnd & d_weights functions to linedf, then bind results into one dataframe (see Examples)
The order in which the estimates are bound together (i.e., their position in the dataframe) is important, and should always be as follows: row 1: var, row 2: lnd, row 3: wi.
Chi squared estimate of homogeneity of diagnosticity ratios for k independent lineups
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L., Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Get ln(d), variance of ln(d) & d weights: ratio <- ln_diag_ratio(linedf, correction = FALSE) var <- var_lnd(linedf) wi <- d_weights(linedf) #Bind estimates into one df of 3 rows & x observations #(see Details above) df <- t(cbind(var, ratio, wi)) #Call: chi_diag(df)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Get ln(d), variance of ln(d) & d weights: ratio <- ln_diag_ratio(linedf, correction = FALSE) var <- var_lnd(linedf) wi <- d_weights(linedf) #Bind estimates into one df of 3 rows & x observations #(see Details above) df <- t(cbind(var, ratio, wi)) #Call: chi_diag(df)
Derives an identification outcome — suspect identification, foil identification, or rejection — from the raw record of which lineup member each witness actually chose.
classify_lineup_choices( data, member_col = "lineup_member", suspect_col = "suspect_pos", into = "identification", k = NULL, overwrite = FALSE )classify_lineup_choices( data, member_col = "lineup_member", suspect_col = "suspect_pos", into = "identification", k = NULL, overwrite = FALSE )
data |
A data frame of lineup trials, one row per witness. |
member_col |
Name of the column holding the position of the lineup
member chosen, with |
suspect_col |
Either the name of a column holding the suspect's position
for each trial, or a single number giving a suspect position common to all
trials. Defaults to |
into |
Name of the column to write the derived outcome into. Defaults to
|
k |
Optional lineup size. When supplied, choices are checked to lie in
|
overwrite |
Logical. Whether to overwrite |
Lineup data should record which member a witness chose, not merely whether the choice happened to fall on the suspect. Foils are not interchangeable: they differ in how plausible they are, and a foil that no witness ever chooses is not a functioning alternative to the suspect. Storing only a collapsed "suspect / filler / reject" label discards the information needed to see that.
This function treats the collapsed label as derived. Raw data keeps
the chosen position; classify_lineup_choices() produces the label on
demand, so both are available and the two cannot drift apart.
The outcome is "suspect" when the chosen member is the suspect,
"reject" when no member was chosen, and "filler" otherwise.
These are the labels used throughout the package, so the result can be passed
straight to make_roc, make_cac and the other
analysis functions.
data with the derived outcome column added. If k was
supplied, the "null_foils" attribute gives the positions that no
witness chose.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
lineup_table for a tabulation that shows null foils,
and simulate_lineup_data, which records lineup_member
directly.
# Raw choices from an 8-person lineup in which the suspect stands at # position 3; 0 records a rejection trials <- data.frame( lineup_member = c(3, 0, 5, 3, 1, 0, 3, 7), suspect_pos = 3 ) classify_lineup_choices(trials, k = 8) # A suspect position common to every trial can be given as a number classify_lineup_choices( data.frame(lineup_member = c(2, 0, 4)), suspect_col = 2 )# Raw choices from an 8-person lineup in which the suspect stands at # position 3; 0 records a rejection trials <- data.frame( lineup_member = c(3, 0, 5, 3, 1, 0, 3, 7), suspect_pos = 3 ) classify_lineup_choices(trials, k = 8) # A suspect position common to every trial can be given as a number classify_lineup_choices( data.frame(lineup_member = c(2, 0, 4)), suspect_col = 2 )
Computes ANRI for two groups and tests whether they differ using bootstrap confidence intervals for the difference.
compare_anri( data, group_var, confidence_bins, choosers_only = TRUE, lineup_size = 6, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )compare_anri( data, group_var, confidence_bins, choosers_only = TRUE, lineup_size = 6, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )
data |
Dataframe with standard lineup format plus a grouping variable |
group_var |
Character. Name of grouping variable (must have exactly 2 levels) |
confidence_bins |
Numeric vector of bin edges |
choosers_only |
Logical. Whether to analyze only suspect IDs (default = TRUE) |
lineup_size |
Integer. Lineup size (default = 6) |
n_bootstrap |
Integer. Number of bootstrap replications (default = 1000) |
conf_level |
Numeric. Confidence level for CIs (default = 0.95) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
confidence_scale |
How the confidence scale is interpreted: "auto" (default),
"0-1", or "0-100". See |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
This function: 1. Computes ANRI separately for each group 2. Bootstraps each group independently 3. Computes bootstrap distribution of the difference 4. Tests H0: ANRI_1 = ANRI_2 using CI for difference
**Interpretation**:
Positive difference: Group 1 has higher resolution
Negative difference: Group 2 has higher resolution
CI excludes 0: Statistically significant difference
A list containing:
anri_group1: ANRI for first group
anri_group2: ANRI for second group
difference: Point estimate of difference (group1 - group2)
ci_lower: Lower bound of CI for difference
ci_upper: Upper bound of CI for difference
significant: Whether difference is significant (CI excludes 0)
group_names: Names of the two groups
bootstrap_results_group1: Bootstrap object for group 1
bootstrap_results_group2: Bootstrap object for group 2
set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), condition = rep(c("sequential", "simultaneous"), times = n / 2) ) cmp <- compare_anri(data, group_var = "condition", confidence_bins = seq(0, 100, 20), n_bootstrap = 100, seed = 1) cmp$difference c(cmp$ci_lower, cmp$ci_upper)set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), condition = rep(c("sequential", "simultaneous"), times = n / 2) ) cmp <- compare_anri(data, group_var = "condition", confidence_bins = seq(0, 100, 20), n_bootstrap = 100, seed = 1) cmp$difference c(cmp$ci_lower, cmp$ci_upper)
Computes average DPP for two lineup procedures and tests their difference with a nonparametric bootstrap, as Smith et al. (2019) did.
compare_dpp( data_proc_a, data_proc_b, lineup_size = 6, alpha = NULL, beta = NULL, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, innocent_suspect = "auto" )compare_dpp( data_proc_a, data_proc_b, lineup_size = 6, alpha = NULL, beta = NULL, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, innocent_suspect = "auto" )
data_proc_a |
Dataframe for procedure A (standard lineup format) |
data_proc_b |
Dataframe for procedure B (standard lineup format) |
lineup_size |
Integer. Lineup size (default = 6) |
alpha, beta
|
Optional Equation 3 weights; see |
n_bootstrap |
Integer. Number of bootstrap samples (default = 1000). Use 0 to skip the bootstrap. |
conf_level |
Numeric. Confidence level for the interval (default = 0.95) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
Lower DPP indicates better performance, so a negative difference means procedure A is better. Each bootstrap replicate resamples target-present and target-absent trials separately within each procedure, so every replicate keeps the original design, and the two procedures are treated as independent samples. The innocent-suspect design and the confidence criteria are determined once on each full dataset and reused in every replicate, so every replicate averages over the same criteria.
An object of class "lineup_dpp_comparison", a list containing:
dpp_a: Average DPP for procedure A
dpp_b: Average DPP for procedure B
dpp_difference: DPP_A - DPP_B (negative = A is better)
se_difference: Bootstrap standard error of the difference
ci_difference: Percentile bootstrap interval for the difference
boot_differences: Bootstrap distribution of the difference
dpp_obj_a, dpp_obj_b: Full make_dpp() objects
n_bootstrap, conf_level: Settings used
Smith, A. M., Lampinen, J. M., Wells, G. L., Smalarz, L., & Mackovichova, S. (2019). Deviation from perfect performance measures the diagnostic utility of eyewitness lineups but partial area under the ROC curve does not. Journal of Applied Research in Memory and Cognition, 8(1), 50-59. doi:10.1016/j.jarmac.2018.09.003
data(lineup_example) # Compare two halves of the example data (odd vs even rows) odd <- seq(1, nrow(lineup_example), by = 2) # 50 resamples keep the example quick; use 1000 or more in an analysis cmp <- compare_dpp(lineup_example[odd, ], lineup_example[-odd, ], n_bootstrap = 50, seed = 1) cmpdata(lineup_example) # Compare two halves of the example data (odd vs even rows) odd <- seq(1, nrow(lineup_example), by = 2) # 50 resamples keep the example quick; use 1000 or more in an analysis cmp <- compare_dpp(lineup_example[odd, ], lineup_example[-odd, ], n_bootstrap = 50, seed = 1) cmp
A row-resampling statistic for two columns of lineup choices.
compare_eff_sizes.boot(linedf, d)compare_eff_sizes.boot(linedf, d)
linedf |
A dataframe of lineup data. Must consist of 2 columns, each containing data for 2 independent lineups |
d |
Indices for bootstrap sample. Argument used by boot function to select samples for bootstrapping |
This statistic applies the same row indices to both columns. Using
it with an ordinary row bootstrap therefore resamples paired observations.
For independent lineup samples use effsize_compare, which
resamples each sample independently. Bootstrap intervals remain approximations;
inclusion or exclusion of zero does not establish calibration in small or
nearly uniform samples.
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
# Two independent lineups of 6 members, 50 mock witnesses each set.seed(1) linedf <- data.frame(lineup1 = sample(1:6, 50, replace = TRUE), lineup2 = sample(1:6, 50, replace = TRUE)) # Difference in effective size for the observed data compare_eff_sizes.boot(linedf, seq_len(nrow(linedf))) # Resample the independent lineups separately effsize_compare(linedf, R = 100)# Two independent lineups of 6 members, 50 mock witnesses each set.seed(1) linedf <- data.frame(lineup1 = sample(1:6, 50, replace = TRUE), lineup2 = sample(1:6, 50, replace = TRUE)) # Difference in effective size for the observed data compare_eff_sizes.boot(linedf, seq_len(nrow(linedf))) # Resample the independent lineups separately effsize_compare(linedf, R = 100)
Performs a chi-squared difference test comparing a less constrained (free) MAX SDT model to a more constrained (nested) model. Both models must be fitted to the same data.
compare_max_sdt(free, constrained)compare_max_sdt(free, constrained)
free |
A |
constrained |
A |
The chi-squared difference statistic is:
with degrees of freedom equal to the difference in the number of free parameters. A significant result indicates that the constraint worsens fit. The fits must use identical counts and lineup sizes, must have converged, and the constrained model must impose every constraint in the free model plus at least one additional constraint.
An object of class "max_sdt_compare" with the test result.
# Each two-condition fit takes a few seconds fit_free <- fit_max_sdt( n_hit=69, n_tp_choose=82, n_fa=64, N_tp=96, N_ta=106, n_hit_2=68, n_tp_choose_2=78, n_fa_2=42, N_tp_2=96, N_ta_2=96 ) fit_eqd <- fit_max_sdt( n_hit=69, n_tp_choose=82, n_fa=64, N_tp=96, N_ta=106, n_hit_2=68, n_tp_choose_2=78, n_fa_2=42, N_tp_2=96, N_ta_2=96, constrain_d = TRUE ) compare_max_sdt(fit_free, fit_eqd)# Each two-condition fit takes a few seconds fit_free <- fit_max_sdt( n_hit=69, n_tp_choose=82, n_fa=64, N_tp=96, N_ta=106, n_hit_2=68, n_tp_choose_2=78, n_fa_2=42, N_tp_2=96, N_ta_2=96 ) fit_eqd <- fit_max_sdt( n_hit=69, n_tp_choose=82, n_fa=64, N_tp=96, N_ta=106, n_hit_2=68, n_tp_choose_2=78, n_fa_2=42, N_tp_2=96, N_ta_2=96, constrain_d = TRUE ) compare_max_sdt(fit_free, fit_eqd)
Fits and compares multiple models (2-HT, EIG, Full ROC) to the same lineup identification dataset, providing a side-by-side descriptive table.
compare_models( data, models = c("2ht", "eig", "fullroc"), lineup_size = 6, prior_guilt = 0.5, confidence_bins = NULL, show_warnings = FALSE, ... )compare_models( data, models = c("2ht", "eig", "fullroc"), lineup_size = 6, prior_guilt = 0.5, confidence_bins = NULL, show_warnings = FALSE, ... )
data |
A dataframe with columns: target_present, identification, confidence |
models |
Character vector of models to fit. Options:
Default = c("2ht", "eig", "fullroc") fits all models. |
lineup_size |
Integer. Number of people in the lineup (default = 6) |
prior_guilt |
Numeric. Prior probability of guilt for EIG (default = 0.5) |
confidence_bins |
Numeric vector of confidence bin edges (optional). Used for EIG and Full ROC if specified. |
show_warnings |
Logical. Whether to show model fitting warnings (default = FALSE) |
... |
Additional arguments passed to individual model functions |
This function provides a unified interface for fitting and comparing multiple eyewitness identification models. It automatically handles different data requirements and output formats across models.
**Models Compared:**
**2-HT (Winter et al., 2022)**: Multinomial processing tree model with parameters for detection (dP, dA), bias (b), and guessing (g). Provides AIC/BIC for comparison only with other likelihood models fit to the same observations and outcome representation.
**EIG (Starns et al., 2023)**: Information-theoretic measure of evidentiary value. Higher values indicate more diagnostic procedures.
**Full ROC (Smith & Yang, 2020)**: Uses ALL responses to compute investigator discriminability. AUC ranges from 0 to 1; 0.5 represents chance under a fixed ordering.
**Interpretation:**
AIC/BIC describe the 2-HT likelihood fit but cannot be compared with EIG or full-ROC AUC
Use EIG for comparing procedure diagnosticity (higher is better)
Use Full ROC AUC for investigator discriminability (higher is better)
An object of class "model_comparison" containing:
comparison_table: Dataframe comparing model fits
fitted_models: List of fitted model objects
best_model: Always NULL. The included methods estimate different quantities and cannot be ranked by a common information criterion.
data: The input data
models_fit: Character vector of models successfully fit
Winter, K., Menne, N. M., Bell, R., & Buchner, A. (2022). Experimental validation of a multinomial processing tree model for analyzing eyewitness identification decisions. Scientific Reports, 12, 15571.
Starns, J. J., Cohen, A. L., & Rotello, C. M. (2023). A complete method for assessing the effectiveness of eyewitness identification procedures: Expected information gain. Psychological Review, 130(3), 677–719.
Smith, A. M., Yang, Y., & Wells, G. L. (2020). Distinguishing between investigator discriminability and eyewitness discriminability. Perspectives on Psychological Science, 15(3), 589-607.
data(lineup_example) # Fit all models comparison <- compare_models(lineup_example) print(comparison) summary(comparison) # Fit specific models comparison <- compare_models(lineup_example, models = c("2ht", "eig")) # Access individual fitted models comparison$fitted_models$`2ht` comparison$fitted_models$eigdata(lineup_example) # Fit all models comparison <- compare_models(lineup_example) print(comparison) summary(comparison) # Fit specific models comparison <- compare_models(lineup_example, models = c("2ht", "eig")) # Access individual fitted models comparison$fitted_models$`2ht` comparison$fitted_models$eig
Computes the difference (x minus y) on every Multi-d' index,
for example simultaneous versus sequential lineups, or high- versus
low-similarity lineups. When both objects carry bootstrap draws, standard
errors, percentile intervals and normal-approximation p-values are given
for each difference. The two conditions are treated as independent samples:
the standard error is the square root of the sum of the two bootstrap
variances, and the interval is taken from differences between randomly
re-paired draws, so the result does not depend on whether the two fits used
the same seed.
compare_multid(x, y, labels = c("x", "y"), seed = NULL)compare_multid(x, y, labels = c("x", "y"), seed = NULL)
x, y
|
Objects of class |
labels |
Names for the two conditions. |
seed |
Optional random seed for re-pairing the bootstrap draws. |
A data frame of class "multid_comparison".
# Simultaneous vs sequential lineups, Lee & Penrod's (2019) Table 3 sim <- make_multid(counts = c(G = 52, Fp = 25, Rp = 24, I = 28, Fa = 26, Ra = 46), correction = "none") sqn <- make_multid(counts = c(G = 44, Fp = 19, Rp = 39, I = 15, Fa = 17, Ra = 68), correction = "none") compare_multid(sim, sqn, labels = c("simultaneous", "sequential"))# Simultaneous vs sequential lineups, Lee & Penrod's (2019) Table 3 sim <- make_multid(counts = c(G = 52, Fp = 25, Rp = 24, I = 28, Fa = 26, Ra = 46), correction = "none") sqn <- make_multid(counts = c(G = 44, Fp = 19, Rp = 39, I = 15, Fa = 17, Ra = 68), correction = "none") compare_multid(sim, sqn, labels = c("simultaneous", "sequential"))
Statistically compares partial Area Under the Curve (pAUC) values between two lineup conditions using bootstrap-based standard errors and z-tests. Follows the methodology from pyWitness (Mickes et al., 2024).
compare_pauc( data1, data2, lineup_size = 6, max_false_id_rate = NULL, n_bootstrap = 2000, conf_level = 0.95, seed = NULL, label1 = "Condition 1", label2 = "Condition 2", innocent_suspect = "auto" )compare_pauc( data1, data2, lineup_size = 6, max_false_id_rate = NULL, n_bootstrap = 2000, conf_level = 0.95, seed = NULL, label1 = "Condition 1", label2 = "Condition 2", innocent_suspect = "auto" )
data1 |
First dataset (dataframe with target_present, identification, confidence) |
data2 |
Second dataset (same format as data1) |
lineup_size |
Integer. Number of people in lineup (default = 6) |
max_false_id_rate |
Numeric. Upper limit of the false-ID rate region over
which partial AUC is computed. Default |
n_bootstrap |
Integer. Number of bootstrap samples (default = 2000) |
conf_level |
Numeric. Confidence level for intervals (default = 0.95) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
label1 |
Character. Label for first condition (default = "Condition 1") |
label2 |
Character. Label for second condition (default = "Condition 2") |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
This function implements an approximate statistical test for comparing ROC curves between two conditions (e.g., simultaneous vs. sequential lineups, different retention intervals, etc.).
**Method:**
Computes pAUC for each condition (optionally up to a maximum false ID rate)
Uses bootstrap resampling to estimate standard errors
Computes Z-statistic: Z = (pAUC1 - pAUC2) / SE(pAUC1 - pAUC2)
Calculates p-value from standard normal distribution
The two datasets are treated as independent conditions. This function does not implement a paired or clustered bootstrap.
**Interpretation:**
Positive pAUC difference: Condition 1 has better discriminability
Negative pAUC difference: Condition 2 has better discriminability
p < 0.05: Significant difference between conditions
The independent-sample bootstrap estimates uncertainty in an empirical partial-area functional; the final comparison uses a normal/Wald interval. It is approximate and can be liberal in finite samples. A data-selected cutoff, sparse high-confidence cells, or resamples that do not reach the cutoff require additional caution. It is not an exact discrimination test.
An object of class "pauc_comparison" containing:
pauc1: pAUC for condition 1
pauc2: pAUC for condition 2
pauc_diff: Difference in pAUC (pauc1 - pauc2)
se_pauc1: Bootstrap standard error for pAUC1
se_pauc2: Bootstrap standard error for pAUC2
se_diff: Standard error of the difference
z_score: Z-statistic for difference test
p_value: Two-tailed p-value
ci_diff: Confidence interval for difference
roc1: ROC object for condition 1
roc2: ROC object for condition 2
max_false_id_rate: Cutoff used for pAUC
label1, label2: Condition labels
n_bootstrap: Number of bootstrap samples used
Mickes, L., Seale-Carlisle, T. M., Chen, X., & Boogert, S. (2024). pyWitness 1.0: A python eyewitness identification analysis toolkit. Behavior Research Methods, 56, 1533-1550.
Wixted, J. T., & Mickes, L. (2012). The field of eyewitness memory should abandon probative value and embrace receiver operating characteristic analysis. Perspectives on Psychological Science, 7(3), 275-278.
# Compare two lineup procedures (halves of the example data) data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) comparison <- compare_pauc( lineup_example[odd, ], lineup_example[-odd, ], label1 = "Procedure A", label2 = "Procedure B", n_bootstrap = 200, seed = 123 ) print(comparison) plot(comparison)# Compare two lineup procedures (halves of the example data) data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) comparison <- compare_pauc( lineup_example[odd, ], lineup_example[-odd, ], label1 = "Procedure A", label2 = "Procedure B", n_bootstrap = 200, seed = 123 ) print(comparison) plot(comparison)
Performs a large-sample z-test comparing d' (or criterion or ln beta) between two independent conditions using summary counts only.
compare_sdt_summary( hits_a, fas_a, misses_a, cr_a, hits_b, fas_b, misses_b, cr_b, metric = c("dprime", "c", "ln_beta"), method = c("miller", "gourevitch", "bootstrap"), correction = c("loglinear", "half", "none"), nboot = 1000, seed = NULL )compare_sdt_summary( hits_a, fas_a, misses_a, cr_a, hits_b, fas_b, misses_b, cr_b, metric = c("dprime", "c", "ln_beta"), method = c("miller", "gourevitch", "bootstrap"), correction = c("loglinear", "half", "none"), nboot = 1000, seed = NULL )
hits_a |
Hits in condition A. |
fas_a |
False alarms in condition A. |
misses_a |
Misses in condition A. |
cr_a |
Correct rejections in condition A. |
hits_b |
Hits in condition B. |
fas_b |
False alarms in condition B. |
misses_b |
Misses in condition B. |
cr_b |
Correct rejections in condition B. |
metric |
Which metric to compare: "dprime", "c", or "ln_beta". |
method |
Variance method: "miller", "gourevitch", or "bootstrap". |
correction |
Correction for extreme rates ("loglinear", "half", or "none"). |
nboot |
Number of bootstrap samples when method = "bootstrap". |
seed |
Optional random seed for bootstrap variance. |
A list with estimates, standard error, z, and p-value.
cmp <- compare_sdt_summary(hits_a = 70, fas_a = 20, misses_a = 30, cr_a = 80, hits_b = 60, fas_b = 35, misses_b = 40, cr_b = 65, metric = "dprime") cmp$z cmp$p_valuecmp <- compare_sdt_summary(hits_a = 70, fas_a = 20, misses_a = 30, cr_a = 80, hits_b = 60, fas_b = 35, misses_b = 40, cr_b = 65, metric = "dprime") cmp$z cmp$p_value
Main wrapper function to compute and plot utility comparison between two lineup procedures.
compare_utility( data_proc_a, data_proc_b, base_rate_grid = seq(0.01, 0.99, 0.01), utility_matrix = c(tp = 1, fn = -0.5, fp = -2, tn = 0.5), utility_type = c("max", "avg", "all"), show_plot = TRUE, innocent_suspect = "auto", ... )compare_utility( data_proc_a, data_proc_b, base_rate_grid = seq(0.01, 0.99, 0.01), utility_matrix = c(tp = 1, fn = -0.5, fp = -2, tn = 0.5), utility_type = c("max", "avg", "all"), show_plot = TRUE, innocent_suspect = "auto", ... )
data_proc_a |
Dataframe for procedure A |
data_proc_b |
Dataframe for procedure B |
base_rate_grid |
Numeric vector of base rates (default: seq(0.01, 0.99, 0.01)) |
utility_matrix |
Named vector of utilities (default: c(tp=1, fn=-0.5, fp=-2, tn=0.5)) |
utility_type |
Character. "max", "avg", or "all" (default = "max") |
show_plot |
Logical. Whether to display plot (default = TRUE) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
... |
Additional arguments passed to plot_utility_difference() |
A list containing difference curve data and plot
data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) comparison <- compare_utility(lineup_example[odd, ], lineup_example[-odd, ], base_rate_grid = seq(0.1, 0.9, 0.1), utility_type = "max") print(comparison)data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) comparison <- compare_utility(lineup_example[odd, ], lineup_example[-odd, ], base_rate_grid = seq(0.1, 0.9, 0.1), utility_type = "max") print(comparison)
Computes ANRI, a bias-corrected version of NRI following Yaniv et al. (1991). ANRI adjusts NRI for the number of confidence bins and sample size, providing more accurate estimates especially when J (bins) is small relative to N.
compute_anri( data, confidence_bins, choosers_only = TRUE, lineup_size = 6, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )compute_anri( data, confidence_bins, choosers_only = TRUE, lineup_size = 6, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )
data |
A dataframe with the following columns:
|
confidence_bins |
Numeric vector of bin edges (required for ANRI) |
choosers_only |
Logical. Whether to analyze only suspect IDs (default = TRUE) |
lineup_size |
Integer. Lineup size (default = 6) |
confidence_scale |
How the confidence scale is interpreted: "auto" (default),
"0-1", or "0-100". See |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
ANRI corrects NRI for small-sample and small-bin-count bias:
Where:
N = total sample size
J = number of bins
n_j = sample size in bin j
a_j = accuracy (proportion correct) in bin j
a_bar = overall accuracy
**When to use ANRI vs NRI:**
Use ANRI when J is small (e.g., 3-5 bins)
Use ANRI when N is modest (e.g., < 200)
Use ANRI when comparing across different numbers of bins
NRI is asymptotically unbiased as N approaches infinity
ANRI provides a less biased estimate of the population resolution, making it
more appropriate for hypothesis testing and group comparisons.
When target-absent suspect identifications are estimated from filler choices,
N and the bin counts are effective (possibly fractional) counts from
make_calibration_data(), so the correction is an approximation. See
make_calibration_data for the designated-suspect distinction.
A list containing:
anri: Adjusted Normalized Resolution Index
nri: Original Normalized Resolution Index (unadjusted)
n_total: Total sample size
n_bins: Number of confidence bins
calibration_data: Per-bin accuracy and sample sizes
overall_accuracy: Mean accuracy
Yaniv, I., Yates, J. F., & Smith, J. E. K. (1991). Measures of discrimination skill in probabilistic judgment. Psychological Bulletin, 110(3), 611-617.
Juslin, P., Olsson, N., & Winman, A. (1996). Calibration and diagnosticity of confidence in eyewitness identification. Journal of Experimental Psychology: Learning, Memory, and Cognition, 22(5), 1304-1316.
set.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) result <- compute_anri(data, confidence_bins = seq(0, 100, 20)) result$anriset.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) result <- compute_anri(data, confidence_bins = seq(0, 100, 20)) result$anri
Main wrapper function to compute and plot DPP analysis.
compute_dpp( data, lineup_size = 6, show_plot = TRUE, alpha = NULL, beta = NULL, innocent_suspect = "auto", ... )compute_dpp( data, lineup_size = 6, show_plot = TRUE, alpha = NULL, beta = NULL, innocent_suspect = "auto", ... )
data |
Dataframe with standard lineup format |
lineup_size |
Integer. Lineup size (default = 6) |
show_plot |
Logical. Whether to display plot (default = TRUE) |
alpha, beta
|
Optional Equation 3 weights; see |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
... |
Additional arguments passed to plot_dpp() |
An object of class "lineup_dpp": the make_dpp()
result plus the plot.
data(lineup_example) dpp_result <- compute_dpp(lineup_example, show_plot = FALSE) print(dpp_result)data(lineup_example) dpp_result <- compute_dpp(lineup_example, show_plot = FALSE) print(dpp_result)
Computes Expected Information Gain for lineup identification procedures following Starns et al. (2023). EIG quantifies the evidentiary value of witness responses using information theory.
compute_eig( eig_data, prior_guilt = 0.5, confidence_bins = NULL, lineup_size = 6, innocent_suspect = "auto" )compute_eig( eig_data, prior_guilt = 0.5, confidence_bins = NULL, lineup_size = 6, innocent_suspect = "auto" )
eig_data |
List output from make_eig_data(), or a dataframe with columns: target_present, identification, confidence |
prior_guilt |
Numeric. Prior probability that suspect is guilty (default = 0.5) |
confidence_bins |
Numeric vector of bin edges (only used if eig_data is a dataframe) |
lineup_size |
Nominal lineup size used when raw target-absent data have no designated innocent suspect. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
EIG measures how much information (in bits) a witness response provides about guilt vs. innocence. It is computed as:
where:
x = response category (e.g., "suspect_high_confidence")
p(x) = probability of that response
H() = Shannon entropy
p(guilty|x) = posterior probability of guilt given response x
Higher EIG values indicate more diagnostic procedures. EIG = 0 means no information gain (responses don't distinguish guilty from innocent). The maximum EIG is the prior entropy; it is 1 bit only at prior guilt 0.5.
A list of class "lineup_eig" containing:
eig: Expected Information Gain in bits
response_data: Dataframe with information gain for each response category
prior_guilt: Prior probability used
prior_entropy: Entropy of prior (baseline uncertainty)
n_guilty: Number of target-present lineups
n_innocent: Number of target-absent lineups
Starns, J. J., Cohen, A. L., & Rotello, C. M. (2023). A complete method for assessing the effectiveness of eyewitness identification procedures: Expected information gain. Psychological Review, 130(3), 677–719.
# Compute EIG with binned confidence data(lineup_example) eig_result <- compute_eig(lineup_example, prior_guilt = 0.5, confidence_bins = c(0, 60, 80, 100)) print(eig_result)# Compute EIG with binned confidence data(lineup_example) eig_result <- compute_eig(lineup_example, prior_guilt = 0.5, confidence_bins = c(0, 60, 80, 100)) print(eig_result)
Converts cosine distance to cosine similarity in [-1, 1].
cosine_to_similarity(distance)cosine_to_similarity(distance)
distance |
Numeric. Cosine distance value(s). |
Numeric cosine similarity score(s), where 1 means the same direction, 0 means orthogonal, and -1 means opposite directions.
# Distance of 0 -> similarity of 1 cosine_to_similarity(0) # Returns 1 # Distance of 0.5 -> similarity of 0.5 cosine_to_similarity(0.5) # Returns 0.5# Distance of 0 -> similarity of 1 cosine_to_similarity(0) # Returns 1 # Distance of 0.5 -> similarity of 0.5 cosine_to_similarity(0.5) # Returns 0.5
Generate example lineup identification data in standardized format. Useful for testing, demonstrations, and learning r4lineups functions.
create_example_lineup_data( n_trials = 100, prop_target_present = 0.5, include_confidence = TRUE, include_response_time = FALSE, seed = NULL )create_example_lineup_data( n_trials = 100, prop_target_present = 0.5, include_confidence = TRUE, include_response_time = FALSE, seed = NULL )
n_trials |
Integer. Number of trials to generate (default = 100) |
prop_target_present |
Numeric. Proportion of target-present lineups (default = 0.5) |
include_confidence |
Logical. Include confidence ratings (default = TRUE) |
include_response_time |
Logical. Include response times (default = FALSE) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
A lineup_data object with realistic example data
# Basic example data example_data <- create_example_lineup_data(n_trials = 200, seed = 123) print(example_data) # Use in analysis roc <- make_roc(example_data, lineup_size = 6)# Basic example data example_data <- create_example_lineup_data(n_trials = 200, seed = 123) print(example_data) # Use in analysis roc <- make_roc(example_data, lineup_size = 6)
Function for computing pooled estimator from a set of k diagnosticity ratios
d_bar(df)d_bar(df)
df |
A dataframe containing rows: ln(d), variance of ln(d), d weights |
. The order in which the estimates are bound together (i.e., their position in the dataframe) is important, and should always be as follows:
row 1: var
row 2: lnd
row 3: wi
Mean diagnosticity ratio for k independent lineups
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Compute ln(d): ratio <- ln_diag_ratio(linedf, correction = FALSE) #Compute variance of ln(d): var <- var_lnd(linedf) #Compute weights for pooled estimator: wi <- d_weights(linedf) #Bind ln(d), variance of ln(d) and weights into one df (of 3 rows & x observations) #(see Details above): df <- t(cbind(var, ratio, wi)) #Call: d_bar(df)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Compute ln(d): ratio <- ln_diag_ratio(linedf, correction = FALSE) #Compute variance of ln(d): var <- var_lnd(linedf) #Compute weights for pooled estimator: wi <- d_weights(linedf) #Bind ln(d), variance of ln(d) and weights into one df (of 3 rows & x observations) #(see Details above): df <- t(cbind(var, ratio, wi)) #Call: d_bar(df)
Function to compute weights of each diagnosticity ratio for k lineup pairs
d_weights(linedf)d_weights(linedf)
linedf |
A dataframe of parameters for computing diagnosticity ratio |
In order to obtain a pooled estimate of a set of diagnosticity ratios, we use a weight for each ratio that is equal to the inverse of its variance.
To get linedf, use the diag_param helper function
diag_param returns a dataframe containing the following:
n11: Number of mock witnesses who identified the suspect in the target present condition
n21: Number of mock witnesses who did not identify the suspect in the target present condition
n12: Number of mock witnesses who identified the suspect in the target absent condition
n22: Number of mock witnesses who did not identify the suspect in the target absent condition
A dataframe with one column containing weights for each pair of lineups for which diagnosticity ratio is being calculated.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Call: wi <- d_weights(linedf)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Call: wi <- d_weights(linedf)
Checks that number of lineup choices contained in a vector is accurate
datacheck1(lineup_vec, k)datacheck1(lineup_vec, k)
lineup_vec |
A numeric vector of lineup choices |
k |
Number of members in lineup |
This function ensures that a non-selected lineup member is not accidentally omitted from the dataframe due to lack of selection by all mock witnesses. It functions as a check that the total number of lineup members is accurate.
# Choices of 50 mock witnesses to a 6-member lineup: passes silently set.seed(1) lineup_vec <- sample(1:6, 50, replace = TRUE) datacheck1(lineup_vec, 6)# Choices of 50 mock witnesses to a 6-member lineup: passes silently set.seed(1) lineup_vec <- sample(1:6, 50, replace = TRUE) datacheck1(lineup_vec, 6)
Checks that number of lineup choices contained in a vector is accurate
datacheck2(lineup_pres, lineup_abs, k1, k2)datacheck2(lineup_pres, lineup_abs, k1, k2)
lineup_pres |
A numeric vector of lineup choices for a lineup in which the target was present |
lineup_abs |
A numeric vector of lineup choices for a lineup in which the target was absent |
k1 |
Number of targets in TP lineup |
k2 |
Number of targets in TA lineup |
This function ensures that a non-selected lineup member is not accidentally omitted from the dataframe due to lack of selection by all mock witnesses. It functions as a check that the total number of lineup members is accurate
# Target-present and target-absent lineups of 6 members each set.seed(1) lineup_pres <- sample(1:6, 50, replace = TRUE) lineup_abs <- sample(1:6, 50, replace = TRUE) datacheck2(lineup_pres, lineup_abs, 6, 6)# Target-present and target-absent lineups of 6 members each set.seed(1) lineup_pres <- sample(1:6, 50, replace = TRUE) lineup_abs <- sample(1:6, 50, replace = TRUE) datacheck2(lineup_pres, lineup_abs, 6, 6)
Checks that number of lineup choices contained in a data table is accurate
datacheck3(lineup_tab, k)datacheck3(lineup_tab, k)
lineup_tab |
A table of lineup choices |
k |
Nominal size (i.e., total number of members in lineup) |
This function ensures that a non-selected lineup member is not accidentally omitted from the data due to lack of selection by all mock witnesses. It functions as a check that the total number of lineup members is accurate.
# Table of choices from 50 mock witnesses to a 6-member lineup set.seed(1) lineup_tab <- table(sample(1:6, 50, replace = TRUE)) datacheck3(lineup_tab, 6)# Table of choices from 50 mock witnesses to a 6-member lineup set.seed(1) lineup_tab <- table(sample(1:6, 50, replace = TRUE)) datacheck3(lineup_tab, 6)
Detects all faces in an image and returns their bounding boxes and facial landmarks.
detect_faces(img_path, detector = "retinaface")detect_faces(img_path, detector = "retinaface")
img_path |
Path to the image file. |
detector |
Character. Face detection backend to use. Options: "retinaface" (default, most accurate), "mtcnn", "mediapipe", "opencv", "ssd", "dlib", "yolov8", "centerface". |
A list where each element represents a detected face with:
facial_area - List with x, y, w, h coordinates
landmarks - List with eye, nose, mouth positions (if available)
confidence - Detection confidence score
Returns an empty list if no faces are detected.
## Not run: # Detect faces in an image faces <- detect_faces("group_photo.jpg") # Number of faces found length(faces) # Get bounding box of first face faces[[1]]$facial_area ## End(Not run)## Not run: # Detect faces in an image faces <- detect_faces("group_photo.jpg") # Number of faces found length(faces) # Get bounding box of first face faces[[1]]$facial_area ## End(Not run)
This function calculates the parameters needed to calculate the diagnosticity ratio for several lineup pairs.
diag_param(lineup_pres_list, lineup_abs_list, pos_list, k)diag_param(lineup_pres_list, lineup_abs_list, pos_list, k)
lineup_pres_list |
A list containing k vectors of lineup choices for k lineups, in which the target was present |
lineup_abs_list |
A list containing k vectors of lineup choices for k lineups, in which the target was absent |
pos_list |
Suspect positions for each lineup pair. Supply a numeric vector with one position per pair when TP and TA suspect positions match, or a list whose elements contain either one shared position or two positions in TP, TA order. |
k |
A vector indexing number of members in each lineup pair. Must be specified by user (scalar). |
Lineup pairs consist of one lineup in which the target was present (TP) and one lineup in which the target was absent (TA).
Each lineup pair must occupy corresponding positions in the TA and TP lists.
Example:
For a lineup pair A that consists of (1)TP lineup and (2)TA lineup: A(1) is the first vector in the TP list A(2) is the first vector in the TA list
The order in which nominal size for each lineup pair is listed must also correspond with the positions of each respective lineup in the lineup lists (i.e., if lineup 1 has k = 6, then the first element of vector 'k' = 6)
Data must be in a list format. This allows the function to compare lineups in which the number of choices and number of lineup members differs.
TP and TA vectors may have different sample sizes; they are counted independently and are never compared element by element.
Returns a dataframe containing:
n11: Number of mock witnesses who identified the suspect in the target present condition
n21: Number of mock witnesses who did not identify the suspect in the target present condition
n12: Number of mock witnesses who identified the suspect in the target absent condition
n22: Number of mock witnesses who did not identify the suspect in the target absent condition
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Call: linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Call: linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k)
Computes Tredoux's small-cell corrected diagnosticity ratio for one target-present/target-absent lineup pair.
diag_ratio_T(lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2)diag_ratio_T(lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2)
lineup_pres |
A numeric vector of lineup choices for a lineup in which the target was present |
lineup_abs |
A numeric vector of lineup choices for a lineup in which the target was absent |
pos_pres |
A scalar, representing target position in TP lineup. Must be declared by user |
pos_abs |
A scalar, representing target position in TA lineup. Must be declared by user |
k1 |
Number of targets in TP lineup. Must be specified by user (scalar). |
k2 |
Number of targets in TA lineup. Must be specified by user (scalar). |
The corrected estimator is:
where and are suspect-selection
counts in target-present and target-absent lineups, respectively,
and and are the corresponding sample
sizes.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(70, 1, 5)) pos_pres <- 3 pos_abs <- 5 #Call: diag_ratio_T(lineup_pres, lineup_abs, pos_pres, pos_abs, 6, 5) diag_ratio_T(lineup_pres, lineup_abs, 3, 5, 6, 5)#Data: lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(70, 1, 5)) pos_pres <- 3 pos_abs <- 5 #Call: diag_ratio_T(lineup_pres, lineup_abs, pos_pres, pos_abs, 6, 5) diag_ratio_T(lineup_pres, lineup_abs, 3, 5, 6, 5)
Computes a Bayesian posterior distribution for the Tredoux diagnosticity ratio using independent Beta-Binomial models for the target-present and target-absent suspect-selection rates.
diag_ratio_T_bayes( lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2, alpha = 0.5, S = 10000, credible_mass = 0.95, threshold = NULL )diag_ratio_T_bayes( lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2, alpha = 0.5, S = 10000, credible_mass = 0.95, threshold = NULL )
lineup_pres |
A numeric vector of lineup choices for a target-present lineup. |
lineup_abs |
A numeric vector of lineup choices for a target-absent lineup. |
pos_pres |
Scalar; suspect position in the target-present lineup. |
pos_abs |
Scalar; suspect position in the target-absent lineup. |
k1 |
Number of members in the target-present lineup (for input validation). |
k2 |
Number of members in the target-absent lineup (for input validation). |
alpha |
Dirichlet concentration (prior strength). A scalar |
S |
Number of posterior draws. Default 10000. |
credible_mass |
Width of the equal-tailed credible interval. Default 0.95. |
threshold |
Optional numeric. If supplied, reports |
The target-present suspect-selection rate and the target-absent rate
are modelled independently with Beta posteriors:
The posterior of the diagnosticity ratio is obtained by drawing
for each sample .
The default Jeffreys prior () is recommended for small samples.
Unlike the continuity-corrected point estimate, the posterior handles zero cell
counts naturally and provides direct probability statements such as
.
The posterior mean is computed exactly, not from the draws. Because the two
rates are independent, , with
for when . When (for example,
no target-absent suspect choices under the Jeffreys prior) the mean is
infinite, and an average of the draws would not settle on any value as
S grew. Report the median and interval instead. In
diag_ratio_T_bayes_compare(), the mean difference is NA when
both means are infinite.
An object of class "diag_ratio_T_bayes" containing:
Numeric vector of length S: posterior draws of DR.
Log-transformed posterior draws.
Posterior point estimates. The
mean is exact, and is Inf when it does not exist (see Details).
Named two-element vector (lower, upper).
Alpha value used.
Input metadata.
Threshold and associated probabilities (or NULL).
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Wells, G. L., & Turtle, J. W. (1986). Eyewitness identification: The importance of lineup models. Psychological Bulletin, 99(3), 320-329.
diag_ratio_T, diag_ratio_T_bayes_compare,
esize_T_bayes
set.seed(1) lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(80, 1, 6)) res <- diag_ratio_T_bayes(lineup_pres, lineup_abs, pos_pres = 3, pos_abs = 3, k1 = 6, k2 = 6) print(res) plot(res)set.seed(1) lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(80, 1, 6)) res <- diag_ratio_T_bayes(lineup_pres, lineup_abs, pos_pres = 3, pos_abs = 3, k1 = 6, k2 = 6) print(res) plot(res)
Fits independent Beta-Binomial models to two lineup pairs and returns the
posterior distribution of the difference , together
with .
diag_ratio_T_bayes_compare( pres_A, abs_A, pos_pres_A, pos_abs_A, k1_A, k2_A, pres_B, abs_B, pos_pres_B, pos_abs_B, k1_B, k2_B, alpha = 0.5, S = 10000, credible_mass = 0.95 )diag_ratio_T_bayes_compare( pres_A, abs_A, pos_pres_A, pos_abs_A, k1_A, k2_A, pres_B, abs_B, pos_pres_B, pos_abs_B, k1_B, k2_B, alpha = 0.5, S = 10000, credible_mass = 0.95 )
pres_A, abs_A
|
Numeric vectors of lineup choices for lineup pair A (target-present and target-absent, respectively). |
pos_pres_A, pos_abs_A
|
Suspect positions for pair A. |
k1_A, k2_A
|
Lineup sizes for pair A (for validation). |
pres_B, abs_B
|
Numeric vectors of lineup choices for lineup pair B. |
pos_pres_B, pos_abs_B
|
Suspect positions for pair B. |
k1_B, k2_B
|
Lineup sizes for pair B. |
alpha |
Prior concentration (shared). Default 0.5 (Jeffreys). |
S |
Number of posterior draws. Default 10000. |
credible_mass |
Width of credible interval. Default 0.95. |
An object of class "diag_ratio_T_bayes_compare".
set.seed(1) pres_A <- round(runif(100, 1, 6)); abs_A <- round(runif(80, 1, 6)) pres_B <- round(runif(100, 1, 6)); abs_B <- round(runif(80, 1, 6)) res <- diag_ratio_T_bayes_compare(pres_A, abs_A, 3, 3, 6, 6, pres_B, abs_B, 3, 3, 6, 6) print(res)set.seed(1) pres_A <- round(runif(100, 1, 6)); abs_A <- round(runif(80, 1, 6)) pres_B <- round(runif(100, 1, 6)); abs_B <- round(runif(80, 1, 6)) res <- diag_ratio_T_bayes_compare(pres_A, abs_A, 3, 3, 6, 6, pres_B, abs_B, 3, 3, 6, 6) print(res)
Computes Wells' diagnosticity ratio for one lineup pair
diag_ratio_W(lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2)diag_ratio_W(lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2)
lineup_pres |
A numeric vector of lineup choices for a lineup in which the target was present |
lineup_abs |
A numeric vector of lineup choices for a lineup in which the target was absent |
pos_pres |
A scalar, representing target position in TP lineup. Must be declared by user |
pos_abs |
A scalar, representing target position in TA lineup. Must be declared by user |
k1 |
Number of targets in TP lineup. Must be specified by user (scalar). |
k2 |
Number of targets in TA lineup. Must be specified by user (scalar). |
Wells, G. L., & Lindsay, R. C. L. (1980).On estimating the diagnosticity of eyewitness nonidentifications.Psychological Bulletin, 88, 776-784.
Wells, G. L., & Turtle, J. W. (1986). Eyewitness identification: The importance of lineup models. Psychological Bulletin, 99, 320-329.
#Data: lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(70, 1, 5)) pos_pres <- 3 pos_abs <- 5 #Call: diag_ratio_W(lineup_pres, lineup_abs, pos_pres, pos_abs, 6, 5) diag_ratio_W(lineup_pres, lineup_abs, 3, 5, 6, 5)#Data: lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(70, 1, 5)) pos_pres <- 3 pos_abs <- 5 #Call: diag_ratio_W(lineup_pres, lineup_abs, pos_pres, pos_abs, 6, 5) diag_ratio_W(lineup_pres, lineup_abs, 3, 5, 6, 5)
Creates a visual display of the lineup faces arranged in a grid.
display_lineup( target_path, foil_paths, ncol = 3, scale = 150, show_labels = TRUE, highlight_target = TRUE )display_lineup( target_path, foil_paths, ncol = 3, scale = 150, show_labels = TRUE, highlight_target = TRUE )
target_path |
Path to target image. |
foil_paths |
Paths to foil images. |
ncol |
Number of columns in the grid. |
scale |
Scale factor for images. |
show_labels |
Logical. Add labels under each face. |
highlight_target |
Logical. Add border around target. |
A magick image object that can be displayed or saved.
## Not run: # Display lineup img <- display_lineup("target.jpg", paste0("foil", 1:5, ".jpg")) print(img) # Save to file magick::image_write(img, "lineup_display.png") ## End(Not run)## Not run: # Display lineup img <- display_lineup("target.jpg", paste0("foil", 1:5, ".jpg")) print(img) # Save to file magick::image_write(img, "lineup_display.png") ## End(Not run)
Distribution functions for the maximum of (m-1) independent standard normal filler signals, where m is the lineup size (suspect + fillers).
dmax_filler(x, lineup_size, log = FALSE) pmax_filler(q, lineup_size, lower.tail = TRUE, log.p = FALSE) qmax_filler(p, lineup_size, lower.tail = TRUE, log.p = FALSE) rmax_filler(n, lineup_size)dmax_filler(x, lineup_size, log = FALSE) pmax_filler(q, lineup_size, lower.tail = TRUE, log.p = FALSE) qmax_filler(p, lineup_size, lower.tail = TRUE, log.p = FALSE) rmax_filler(n, lineup_size)
x, q
|
Numeric vector of quantiles. |
lineup_size |
Lineup size (suspect + fillers), must be >= 2. |
log, log.p
|
Logical. Return log-density / log-probability. |
lower.tail |
Logical. If TRUE (default), returns P[X <= q]. |
p |
Numeric vector of probabilities. |
n |
Number of random draws. |
Density, distribution, quantile, or random draws.
dmax_filler(0, lineup_size = 6) pmax_filler(0, lineup_size = 6) qmax_filler(0.5, lineup_size = 6) rmax_filler(5, lineup_size = 6)dmax_filler(0, lineup_size = 6) pmax_filler(0, lineup_size = 6) qmax_filler(0.5, lineup_size = 6) rmax_filler(5, lineup_size = 6)
A package-specific count of members whose choice-proportion intervals include chance guessing. This is distinct from Malpass' continuous effective size.
eff_size_per_foils(lineup_vec, target_pos, k, conf = 0.95, R = 1000)eff_size_per_foils(lineup_vec, target_pos, k, conf = 0.95, R = 1000)
lineup_vec |
A numeric vector of lineup choices |
target_pos |
A numeric vector indexing all lineup members |
k |
Number of members in lineup. Must be specified by user (scalar). |
conf |
Confidence level for the intervals, e.g. 0.95 for 95 percent intervals. Defaults to 0.95. May be specified by user (scalar). |
R |
Number of bootstrap replications. Defaults to 1000. |
Because the result is a count of members rather than a continuous
quantity, it changes only in whole steps as conf varies, and
it is flat across ranges of conf in which no member's interval
crosses . A value that does not move when conf is
changed from, say, 0.95 to 0.99 is therefore expected behaviour and
not a sign that the argument is being ignored. Widening the interval
can only ever increase the count.
Intervals are computed by lineup_boot_allprop, and are
not corrected for the fact that k of them are examined at once.
A single integer: the number of lineup members whose bootstrapped
confidence interval for the proportion choosing them includes the
chance value . These are the members that a mock witness
chose at a rate not distinguishable from guessing.
Malpass, R S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5, 299-309.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- c(1, 2, 3, 4, 5, 6) #Call: eff_size_per_foils(lineup_vec, target_pos, 6) eff_size_per_foils(lineup_vec, target_pos, conf = 0.95, 6)#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- c(1, 2, 3, 4, 5, 6) #Call: eff_size_per_foils(lineup_vec, target_pos, 6) eff_size_per_foils(lineup_vec, target_pos, conf = 0.95, 6)
Function for comparing effective size of two independent lineups (Tredoux, 1998)
effsize_compare(linedf, R = 1000)effsize_compare(linedf, R = 1000)
linedf |
A dataframe of lineup data. Contains 2 columns, each of which hold data for 2 independent lineups |
R |
Number of bootstrap replications. |
This function is a master function, calling other functions it needs, and reporting results in some detail Each lineup is resampled independently; row alignment has no sampling meaning. Confidence intervals are bootstrap approximations and can be unreliable for small or nearly uniform empirical choice distributions. Normal and BCa intervals can under-cover in finite concentrated samples; percentile intervals can be conservative near uniformity. None provides a distribution-free level-.05 test of equal population effective sizes.
Effective size, significance level, and confidence intervals (95 normal theory, percentile & bias-corrected)
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
#Data: lineup_vec1 <- round(runif(100, 1, 6)) lineup_vec2 <- round(runif(100, 1, 6)) linedf <- as.data.frame(cbind(lineup_vec1, lineup_vec2)) #Call: x <- effsize_compare(linedf, R = 100)#Data: lineup_vec1 <- round(runif(100, 1, 6)) lineup_vec2 <- round(runif(100, 1, 6)) linedf <- as.data.frame(cbind(lineup_vec1, lineup_vec2)) #Call: x <- effsize_compare(linedf, R = 100)
Calculates the distance or similarity between two face embedding vectors.
embedding_distance( emb1, emb2, metric = c("cosine", "euclidean", "euclidean_l2") )embedding_distance( emb1, emb2, metric = c("cosine", "euclidean", "euclidean_l2") )
emb1 |
Numeric vector. First embedding. |
emb2 |
Numeric vector. Second embedding. |
metric |
Character. Distance metric to use:
|
For ArcFace and other angular-margin trained models, cosine distance is recommended as it aligns with the training objective.
Cosine distance ranges from 0 (identical) to 2 (opposite). Typical thresholds for same-person verification:
ArcFace + cosine: ~0.68
Facenet512 + cosine: ~0.30
Numeric distance value. Lower values indicate more similar faces.
## Not run: emb1 <- get_embedding("face1.jpg")$embedding emb2 <- get_embedding("face2.jpg")$embedding # Cosine distance (recommended) embedding_distance(emb1, emb2, metric = "cosine") # Euclidean distance embedding_distance(emb1, emb2, metric = "euclidean") ## End(Not run)## Not run: emb1 <- get_embedding("face1.jpg")$embedding emb2 <- get_embedding("face2.jpg")$embedding # Cosine distance (recommended) embedding_distance(emb1, emb2, metric = "cosine") # Euclidean distance embedding_distance(emb1, emb2, metric = "euclidean") ## End(Not run)
Computes a bootstrap distribution for effective size using either Tredoux's E' or Malpass's adjusted E.
esize_boot_dist( lineup_input, k, metric = c("tredoux", "malpass"), input_type = c("vector", "table"), R = 1000, unbiased = FALSE )esize_boot_dist( lineup_input, k, metric = c("tredoux", "malpass"), input_type = c("vector", "table"), R = 1000, unbiased = FALSE )
lineup_input |
A numeric lineup vector or a lineup table. |
k |
Nominal lineup size (suspect + fillers). |
metric |
Either "tredoux" or "malpass". |
input_type |
Either "vector" or "table". |
R |
Number of bootstrap resamples. |
unbiased |
Logical. For |
Percentile or BCa intervals from the plug-in E' bootstrap are not valid tests of exact uniform fairness. The population maximum E'=k is a nonregular boundary. If the sample size is not a multiple of k, even the largest attainable empirical E' is below k, so those intervals have zero coverage at exact fairness. More bootstrap replicates cannot fix this.
A numeric vector of bootstrap estimates, carrying an "n"
attribute equal to the number of lineup choices resampled. With
unbiased = TRUE, resamples in which every choice fell on a
different member have an unbounded corrected estimate; they are retained
as Inf, with a warning, and their number is recorded in an "n_undefined"
attribute.
vec <- round(runif(200, 1, 6)) dist <- esize_boot_dist(vec, k = 6, metric = "tredoux", R = 500) dist_corrected <- esize_boot_dist(vec, k = 6, R = 500, unbiased = TRUE)vec <- round(runif(200, 1, 6)) dist <- esize_boot_dist(vec, k = 6, metric = "tredoux", R = 500) dist_corrected <- esize_boot_dist(vec, k = 6, R = 500, unbiased = TRUE)
Function for computing Effective Size
esize_m(lineup_tab, k, both = FALSE)esize_m(lineup_tab, k, both = FALSE)
lineup_tab |
A table of lineup choices. |
k |
Number of members in the lineup. Must be specified by the user (scalar). |
both |
Logical. Defaults to FALSE, returning Tredoux's adjusted effective size estimate only. If TRUE, both Malpass's (1981) original formulation and Malpass's adjusted version (Tredoux, 1998) are printed to the console. |
Reduces the size of a lineup from a (corrected) nominal starting value by the degree to which members are, in sum, chosen below the level of chance expectation.
The Tredoux-adjusted Malpass effective-size estimate. With
both = TRUE, the original and adjusted estimates are also printed.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
esize_T, whose “Small-sample bias” section
describes a downward bias affecting plug-in effective-size estimators at
small numbers of choices, and an optional correction.
#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: esize_m(lineup_vec, 6, both = TRUE) esize_m(lineup_vec, 6)#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: esize_m(lineup_vec, 6, both = TRUE) esize_m(lineup_vec, 6)
Base function for computing bootstrapped effective size
esize_m_boot(lineup_vec, d, k)esize_m_boot(lineup_vec, d, k)
lineup_vec |
A vector of lineup choices |
d |
Indices for bootstrap resampling |
k |
A vector indexing number of members in each lineup pair. Must be specified by user (scalar). |
Function to call when bootstrap resampling using boot function (in package 'boot')
The Tredoux-adjusted Malpass effective-size estimate for the resample.
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
#Data: lineup_vec <- round(runif(100, 1, 6)) #Get boot object: bootobject <- boot::boot(lineup_vec, esize_m_boot, k = 6, R=1000) bootobject #To get confidence intervals: cis <- boot::boot.ci(bootobject, conf = 0.95, type = "all", k = 6)#Data: lineup_vec <- round(runif(100, 1, 6)) #Get boot object: bootobject <- boot::boot(lineup_vec, esize_m_boot, k = 6, R=1000) bootobject #To get confidence intervals: cis <- boot::boot.ci(bootobject, conf = 0.95, type = "all", k = 6)
Function for generating Tredoux's transformed index-of-diversity effective size.
esize_T(lineup_tab, unbiased = FALSE)esize_T(lineup_tab, unbiased = FALSE)
lineup_tab |
A table of lineup choices |
unbiased |
Logical. If |
Tredoux (1998) proposed using Agresti and Agresti's (1978) index of diversity for lineup choices,
where is the observed count for lineup member
and is the total number of choices. The index is then
transformed back to an effective-size scale:
This formula is algebraically equivalent to a reciprocal concentration form.
The expression above is a plug-in estimator: sample proportions are
substituted for the unknown . The plug-in concentration
is biased upward. Its reciprocal is biased
downward for fair and near-fair lineups, but the direction is not
guaranteed: when one member draws most choices the bias can be upward (two
members, , : mean 1.230 against a true 1.220,
by exact enumeration). For choices spread evenly over
members the bias is approximately
A perfectly fair six-person lineup assessed with 50 choices therefore returns about 5.47 rather than 6, and with 20 choices about 4.87. The bias is depends on both nominal size and the choice distribution; no single sample size makes it negligible for every lineup.
With unbiased = TRUE the function substitutes the unbiased estimator
of the concentration, , and returns its
reciprocal,
The reciprocal of an unbiased estimate is not itself unbiased. For
near-fair lineups this can reduce bias, with increased variability; in concentrated
distributions it can overshoot (mean 1.269 in the two-member example).
When fewer than two choices are available the correction is undefined and
returns NA with a warning. When every choice is on a different member,
the corrected reciprocal is Inf, retained with a warning. If
, that event can have positive probability, so the corrected
estimator can have an infinite sampling mean. It is not uniformly superior.
Which to use depends on what is being reported. For a description of the
choice distribution actually observed, the plug-in value is the quantity of
interest. For inference about the lineup that produced those choices, the
framing of Tredoux (1998), the corrected form usually has less bias when
choices are spread across members. Neither is uniformly better.
Note that the other functions in this family (esize_T_boot,
esize_T_ci_n, i_esize_T) use the plug-in form.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Agresti, A., & Agresti, B. F. (1978). Statistical analysis of qualitative variation. Sociological Methodology, 9, 204-237.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L., Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) #Call: e <- esize_T(lineup_tab)#Data: lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) #Call: e <- esize_T(lineup_tab)
Fits a Dirichlet-Multinomial conjugate model to mock-witness choice counts and returns the posterior distribution of effective size E' (Tredoux, 1998).
esize_T_bayes( lineup_tab, alpha = 0.5, S = 10000, credible_mass = 0.95, threshold = NULL )esize_T_bayes( lineup_tab, alpha = 0.5, S = 10000, credible_mass = 0.95, threshold = NULL )
lineup_tab |
A table of lineup choice counts (one entry per position). |
alpha |
Dirichlet concentration parameter(s). A scalar (applied to all k positions) or a length-k numeric vector. Typical choices: 0.5 (Jeffreys prior, default), 1 (uniform over the simplex), 0.1 (weakly informative). Smaller values allow the posterior to concentrate more sharply around the observed proportions. |
S |
Number of posterior draws. Default 10000. |
credible_mass |
Width of the equal-tailed credible interval. Default 0.95. |
threshold |
Optional finite scalar. When supplied, the function also reports P(E' < threshold | data) and P(E' > threshold | data), enabling direct probability statements about whether the lineup is functioning below or above a fairness benchmark. Because E' <= k for every choice-probability vector, with equality only when all k probabilities are exactly equal, P(E' < k | data) is 1 under any continuous posterior, however uniform the data. A threshold of k or more therefore carries no information, and the function warns. Use a tolerance below k that you choose and justify in advance (for example 0.8 k), or report the credible interval for E' or for the deficit k - E'. |
Effective size E' = 1/sum(p_i^2) (Tredoux, 1998) is mathematically identical to the reciprocal of the Herfindahl-Hirschman concentration index (Herfindahl, 1950; Hirschman, 1945) and to the reciprocal of Simpson's (1949) concentration statistic lambda. The Bayesian approach estimates the latent choice-probability vector p rather than E' directly.
With mock-witness counts n = (n_1, ..., n_k) and a Dirichlet prior:
the posterior is conjugate:
Each posterior draw of p is mapped to a draw of E' via E'(p) = 1/sum(p_i^2). The resulting posterior distribution of E' summarises uncertainty about effective lineup size given the observed choices.
Dirichlet samples are generated by normalising independent Gamma draws (base R only; no additional package dependencies).
For publication, a prior sensitivity analysis using at least alpha = 0.5, 1, and 0.1 is recommended. If the substantive conclusion changes across these priors, the mock-witness sample is likely too small to support a confident fairness conclusion.
An object of class "esize_T_bayes" containing:
Numeric vector of S posterior draws of E'.
Posterior mean of E'.
Posterior median of E'.
Named 2-vector (lower, upper) giving the equal-tailed credible interval.
The alpha vector used.
Total number of mock-witness choices.
Number of lineup positions.
Number of posterior draws.
Credible interval width.
Threshold value supplied, or NULL.
List with P_below and P_above,
or NULL when no threshold was supplied.
Herfindahl, O. C. (1950). Concentration in the US steel industry. Doctoral dissertation, Columbia University.
Hirschman, A. O. (1945). National power and the structure of foreign trade. University of California Press.
Simpson, E. H. (1949). Measurement of diversity. Nature, 163, 688.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
esize_T, esize_T_ci_n,
esize_boot_dist, esize_T_bayes_compare
counts <- c(18, 7, 5, 4, 3, 3) lineup_tab <- as.table(setNames(counts, 1:6)) # Default Jeffreys prior result <- esize_T_bayes(lineup_tab) print(result) # With a fairness threshold result2 <- esize_T_bayes(lineup_tab, threshold = 4) print(result2) # Plot the posterior plot(result)counts <- c(18, 7, 5, 4, 3, 3) lineup_tab <- as.table(setNames(counts, 1:6)) # Default Jeffreys prior result <- esize_T_bayes(lineup_tab) print(result) # With a fairness threshold result2 <- esize_T_bayes(lineup_tab, threshold = 4) print(result2) # Plot the posterior plot(result)
Fits independent Dirichlet-Multinomial models to two lineup tables and returns the posterior distribution of the difference in Tredoux E'.
esize_T_bayes_compare( table_a, table_b, alpha = 0.5, S = 10000, credible_mass = 0.95 )esize_T_bayes_compare( table_a, table_b, alpha = 0.5, S = 10000, credible_mass = 0.95 )
table_a |
A table of choice counts for lineup A. |
table_b |
A table of choice counts for lineup B. |
alpha |
Dirichlet concentration parameter(s). Applied to both lineups. A scalar or a length-k vector (both lineups must have the same k). Default 0.5 (Jeffreys). |
S |
Number of posterior draws per lineup. Default 10000. |
credible_mass |
Width of the equal-tailed credible interval for the difference. Default 0.95. |
Independent Dirichlet-Multinomial posteriors are fitted for each lineup. The posterior of Delta = E'_A - E'_B is obtained by subtracting corresponding draws. P(E'_A > E'_B | data) is reported directly.
When the two lineups have different nominal sizes (different k), alpha must be a scalar; it is expanded independently for each lineup.
An object of class "esize_T_bayes_compare" containing:
Posterior draws of Delta = E'_A - E'_B.
Posterior mean of Delta.
Posterior median of Delta.
Named 2-vector (lower, upper) for Delta.
Posterior probability that E'_A > E'_B.
Posterior probability that E'_B > E'_A.
The esize_T_bayes object for lineup A.
The esize_T_bayes object for lineup B.
Credible interval width.
Number of posterior draws.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
esize_T_bayes, compare_eff_sizes.boot
counts_a <- c(20, 5, 5, 5, 5, 5) counts_b <- c(10, 8, 8, 8, 8, 8) tab_a <- as.table(setNames(counts_a, 1:6)) tab_b <- as.table(setNames(counts_b, 1:6)) result <- esize_T_bayes_compare(tab_a, tab_b) print(result) plot(result)counts_a <- c(20, 5, 5, 5, 5, 5) counts_b <- c(10, 8, 8, 8, 8, 8) tab_a <- as.table(setNames(counts_a, 1:6)) tab_b <- as.table(setNames(counts_b, 1:6)) result <- esize_T_bayes_compare(tab_a, tab_b) print(result) plot(result)
Base function for generating bootstrapped Effective Size (Tredoux, 1998)
esize_T_boot(lineup_tab, d)esize_T_boot(lineup_tab, d)
lineup_tab |
A vector of individual lineup choices. The historical argument name is retained for compatibility. |
d |
Indices for bootstrap resampling |
This is the statistic function passed to boot::boot() for
bootstrapping Tredoux's E'. The data argument must contain individual
lineup choices; d resamples mock witnesses rather than count-table cells.
For a higher-level interface that accepts raw lineup vectors, see
esize_boot_dist.
Percentile and BCa intervals from this plug-in statistic cannot be used to
test exact fairness E'=k; see the boundary warning in esize_boot_dist.
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L., Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
# Choices from 50 mock witnesses to a 6-member lineup set.seed(1) lineup_tab <- sample(1:6, 50, replace = TRUE) # E' for the observed data esize_T_boot(lineup_tab, seq_along(lineup_tab))# Choices from 50 mock witnesses to a 6-member lineup set.seed(1) lineup_tab <- sample(1:6, 50, replace = TRUE) # E' for the observed data esize_T_boot(lineup_tab, seq_along(lineup_tab))
Computes effective size E' (Tredoux, 1998) together with a confidence interval derived from the normal-theory variance formula in Tredoux (1998).
esize_T_ci_n(lineup_tab, alpha)esize_T_ci_n(lineup_tab, alpha)
lineup_tab |
A table of lineup choices. |
alpha |
Confidence level as a decimal (e.g., 0.95 for a 95-percent CI). |
The variance of the diversity index I is estimated via Tredoux's (1998) closed-form expression:
Normal quantiles are applied to I and the resulting bounds are back-transformed
to the E' scale via . For small samples or skewed distributions,
all interval methods require qualification. In particular, plug-in bootstrap
intervals also fail at exact uniform fairness; see esize_boot_dist.
Bayesian credible intervals describe a stated posterior rather than
guaranteeing fixed-parameter frequentist coverage.
Note that this concerns the interval. The point estimate itself is
also biased downward near uniform choice distributions at small samples,
although the direction is not universal, since is a plug-in
estimator; see the “Small-sample bias” section of
esize_T. This function uses the plug-in form.
This first-order interval is unreliable in small samples and at uniform or
concentrated boundary distributions. A zero estimated variance yields a
degenerate interval, not evidence of zero population uncertainty. An ordinary
bootstrap is also not a general solution to these boundary problems.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) #Call: e_ci <- esize_T_ci_n(lineup_tab, .95)#Data: lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) #Call: e_ci <- esize_T_ci_n(lineup_tab, .95)
Estimate decision criterion (gamma) and discriminability (d') from rejection rates in culprit-absent and culprit-present lineups.
estimate_msdt_params(pr_rej_I, pr_rej_G, lineup_size, eps = 1e-06) msdt_gamma_from_rej(pr_rej_I, lineup_size, eps = 1e-06) msdt_dprime_from_rej(pr_rej_G, gamma, lineup_size, eps = 1e-06)estimate_msdt_params(pr_rej_I, pr_rej_G, lineup_size, eps = 1e-06) msdt_gamma_from_rej(pr_rej_I, lineup_size, eps = 1e-06) msdt_dprime_from_rej(pr_rej_G, gamma, lineup_size, eps = 1e-06)
pr_rej_I |
Rejection rate in culprit-absent lineups. |
pr_rej_G |
Rejection rate in culprit-present lineups. |
lineup_size |
Lineup size (suspect + fillers), must be >= 2. |
eps |
Small value used to avoid probabilities of 0 or 1. |
gamma |
Decision criterion estimated from culprit-absent rejection
rates, typically returned by |
This is a method-of-moments estimator that is exactly identified
from the two rejection rates alone: Pr(REJ|I) fixes gamma, and the gap
between culprit-absent and culprit-present rejection rates fixes d'. It is
information-lossy: it ignores the composition of choices
(suspect-ID vs filler-ID) and the spread of filler choices, which are the
most diagnostic outcomes. If you have the full response breakdown
(suspect/filler/reject counts for both lineup types), prefer the
minimum-Pearson fitter fit_max_sdt or the 2-HT model
(fit_winter_2ht), which use all outcome counts.
A list with gamma and dprime.
fit_max_sdt for a full-outcome, minimum-Pearson fit;
fit_winter_2ht for the 2-HT MPT model.
estimate_msdt_params(pr_rej_I = 0.25, pr_rej_G = 0.15, lineup_size = 6)estimate_msdt_params(pr_rej_I = 0.25, pr_rej_G = 0.15, lineup_size = 6)
Extracts d' (probit) or ln(OR) (logit) from SDT GLM/GLMM fits. Criterion is reported only when is_old was centered to -0.5/+0.5. With covariates these quantities are evaluated at zero/reference covariates; interactions can change them at other covariate values. GLMM coefficients are conditional latent-scale effects, not marginal population SDT measures. A logit coefficient is log odds ratio rather than normal-theory d-prime.
extract_sdt_metrics(model, link = NULL)extract_sdt_metrics(model, link = NULL)
model |
A glm or glmerMod object produced by fit_sdt_glm/fit_sdt_glmm. |
link |
Optional. If NULL, inferred from the model family. |
A list containing d' or ln(OR), criterion (if available), and metadata.
set.seed(123) n <- 200 is_old <- rep(c(0, 1), each = n / 2) said_old <- rbinom(n, 1, ifelse(is_old == 1, 0.7, 0.3)) df <- data.frame(is_old = is_old, said_old = said_old) fit <- fit_sdt_glm(df, is_old = "is_old", said_old = "said_old") metrics <- extract_sdt_metrics(fit) metrics$estimate # d' under the probit linkset.seed(123) n <- 200 is_old <- rep(c(0, 1), each = n / 2) said_old <- rbinom(n, 1, ifelse(is_old == 1, 0.7, 0.3)) df <- data.frame(is_old = is_old, said_old = said_old) fit <- fit_sdt_glm(df, is_old = "is_old", said_old = "said_old") metrics <- extract_sdt_metrics(fit) metrics$estimate # d' under the probit link
Computes the similarity between two face images using deep learning embeddings. This is the core function for comparing a target face to a foil face.
face_similarity( img1_path, img2_path, model = "ArcFace", metric = "cosine", detector = "retinaface", return_embeddings = FALSE )face_similarity( img1_path, img2_path, model = "ArcFace", metric = "cosine", detector = "retinaface", return_embeddings = FALSE )
img1_path |
Path to the first (target) image. |
img2_path |
Path to the second (foil) image. |
model |
Character. Face recognition model. Default is "ArcFace". |
metric |
Character. Distance metric: "cosine" (recommended), "euclidean", or "euclidean_l2". |
detector |
Character. Face detection backend. Default is "retinaface". |
return_embeddings |
Logical. If TRUE, also returns the embedding vectors. |
This function wraps DeepFace's verify function but is designed for similarity measurement rather than identity verification. In lineup research, we typically want to measure how similar foils are to the target, not whether they are the same person.
The 'verified' field is the model's identity decision: whether the distance falls inside the model's threshold for "same person". In a lineup it is a prompt to inspect the two photographs, not a finding. Face recognition systems make false matches, and a match does not show that a foil is nonfunctioning for witnesses.
A list containing:
distance - The computed distance (lower = more similar)
similarity - Similarity score in [-1,1] for cosine metric
verified - Logical, TRUE if distance is below the model's threshold
threshold - The verification threshold for this model/metric
model - Model used
metric - Metric used
embeddings - (if return_embeddings=TRUE) List of both embeddings
## Not run: # Compare target face to a foil result <- face_similarity("target.jpg", "foil1.jpg") # Check similarity score result$similarity # 0.85 would mean very similar # Inspect the model's same-identity flag; this is not a fairness judgment. result$verified # TRUE would indicate problematic similarity ## End(Not run)## Not run: # Compare target face to a foil result <- face_similarity("target.jpg", "foil1.jpg") # Check similarity score result$similarity # 0.85 would mean very similar # Inspect the model's same-identity flag; this is not a fairness judgment. result$verified # TRUE would indicate problematic similarity ## End(Not run)
Fits confidence-binned Independent Observations, Ensemble/BEST-Rest, and Integration models through an explicitly installed pyWitness engine.
fit_lineup_models( data, models = c("independent", "ensemble", "integration"), lineup_size = 6L, confidence_bins = NULL, variance = c("equal", "unequal"), shared_variance = c("estimated", "zero"), starts = NULL, control = NULL, envname = .pywitness_default_env )fit_lineup_models( data, models = c("independent", "ensemble", "integration"), lineup_size = 6L, confidence_bins = NULL, variance = c("equal", "unequal"), shared_variance = c("estimated", "zero"), starts = NULL, control = NULL, envname = .pywitness_default_env )
data |
A data frame with |
models |
Character vector containing |
lineup_size |
Integer lineup size. |
confidence_bins |
Optional strictly increasing numeric bin edges. When
|
variance |
Either |
shared_variance |
Either |
starts |
Optional named numeric starting vector applied to all models, or a named list of such vectors indexed by model. |
control |
Named list controlling |
envname |
Name or path of the isolated pyWitness virtual environment. |
This interface is restricted to fair simultaneous lineups. Explicit
target-absent suspect IDs are combined with other target-absent choices
because all innocent members share one lure distribution. The upstream
processor requires at least one observation in each aggregate TP/TA response
category; zero cells within particular confidence bins remain supported.
If the input includes a condition or procedure column, it must
contain only one non-missing value; otherwise the data must be subset before
fitting.
By default the bridge corrects the pinned engine's reversed observed/expected
rejection-cell variance denominators in memory for each fit, without changing
its installed environment; control = list(objective = "pywitness")
turns the correction off. With the default chi2_variance = "expected",
every cell therefore uses the Pearson denominator. With "observed",
every cell uses the observed denominator (the Neyman discrepancy), requiring
positive observed cells. Thus, loglik_at_estimate is a multinomial likelihood
evaluated at the minimum-Pearson estimate, not a maximized likelihood. AIC
and BIC are returned as NA; computing them from a non-likelihood
optimum would be invalid.
BEST-Rest and Ensemble obey
They fit identically after rescaling criteria and must not be counted as independent model-selection evidence. Integration is included as a historically important comparator, not a recommended default.
An R-native "lineup_model_comparison" object containing
comparison statistics, parameters, observed and expected cells,
diagnostics, specifications, and engine metadata. It contains no live
Python objects.
Wixted, J. T., Vul, E., Mickes, L., & Wilson, B. M. (2018). Models of lineup memory. Cognitive Psychology, 105, 81–114. doi:10.1016/j.cogpsych.2018.06.001
Mickes, L., Seale-Carlisle, T. M., Chen, X., & Boogert, S. (2024). pyWitness 1.0: A Python eyewitness identification analysis toolkit. Behavior Research Methods, 56, 1533–1550.
## Not run: install_pywitness() fits <- fit_lineup_models( lineup_example, confidence_bins = c(0, 60, 80, 100) ) print(fits) plot(fits, type = "fit") ## End(Not run)## Not run: install_pywitness() fits <- fit_lineup_models( lineup_example, confidence_bins = c(0, 60, 80, 100) ) print(fits) plot(fits, type = "fit") ## End(Not run)
Fits the equal-variance, independent-signal, single-criterion MAX model to aggregate simultaneous-lineup counts using minimum-Pearson chi-squared estimation. Estimates d' (sensitivity) and lambda (criterion) simultaneously, supporting one or two conditions and optional cross-condition constraints.
fit_max_sdt( n_hit, n_tp_choose, n_fa, N_tp, N_ta, n_hit_2 = NULL, n_tp_choose_2 = NULL, n_fa_2 = NULL, N_tp_2 = NULL, N_ta_2 = NULL, n = 6, constrain_d = FALSE, constrain_c = FALSE, start = NULL, nboot = 0, ci_level = 0.95, seed = NULL )fit_max_sdt( n_hit, n_tp_choose, n_fa, N_tp, N_ta, n_hit_2 = NULL, n_tp_choose_2 = NULL, n_fa_2 = NULL, N_tp_2 = NULL, N_ta_2 = NULL, n = 6, constrain_d = FALSE, constrain_c = FALSE, start = NULL, nboot = 0, ci_level = 0.95, seed = NULL )
n_hit |
Number of correct suspect identifications in target-present (TP) lineups (condition 1). |
n_tp_choose |
Total number of participants who chose any lineup member in TP lineups (condition 1). Must be >= n_hit. |
n_fa |
Number of participants who chose any lineup member in target-absent (TA) lineups (condition 1). |
N_tp |
Total number of participants in TP lineups (condition 1). |
N_ta |
Total number of participants in TA lineups (condition 1). |
n_hit_2, n_tp_choose_2, n_fa_2, N_tp_2, N_ta_2
|
Corresponding counts for an optional second condition. All five must be supplied together or not at all. |
n |
Lineup size (number of members). Default 6. |
constrain_d |
Logical. If |
constrain_c |
Logical. If |
start |
Named numeric vector of starting values for the optimizer.
If |
nboot |
Number of parametric bootstrap replicates for confidence intervals. Default 0 (no bootstrap). Set to e.g. 1000 to obtain bootstrap CIs. |
ci_level |
Confidence level for bootstrap CIs. Default 0.95. |
seed |
Optional integer seed for reproducible bootstrap. |
This is a restricted Independent Observations/MAX model for simultaneous,
fair lineups. Fillers are IID , the culprit signal is
, memory signals are independent, and a single criterion is
applied to the largest signal. Target-absent n_fa therefore means a
choice of any lineup member, not only a designated innocent suspect.
For a lineup of members with culprit signal strength and
response criterion :
Parameters are estimated by minimizing the Pearson chi-squared discrepancy
between the mutually exclusive response cells: target-present correct ID,
filler ID and rejection; and target-absent choice and rejection. This is not
maximum-likelihood estimation and is not the multi-criterion likelihood model
of Wixted et al. (2018). Nested comparisons (equal d' or equal lambda across
conditions) are supported via compare_max_sdt.
Estimates are conditional on the model assumptions. Correlated signals, unequal variances, confidence criteria, sequential presentation, unfair lineups, and the Ensemble and Integration decision variables are outside the scope of this function.
Parametric bootstrap CIs are obtained by simulating data from the fitted model
and re-fitting. Replicates that fail to converge are dropped and counted in
boot_n_failed.
An object of class "max_sdt_fit" containing:
Estimated d' and criterion for condition 1.
Estimated d' and criterion for condition 2 (if supplied).
Chi-squared goodness-of-fit statistic.
Degrees of freedom for the GoF test.
P-value for the GoF test.
Named vector of observed counts.
Named vector of predicted counts.
Convergence code from optim() (0 = success).
Input metadata.
Bootstrap confidence intervals (if nboot > 0).
Full bootstrap parameter matrix (if nboot > 0). Rows for
replicates that failed to converge are NA, so summarise with
na.rm = TRUE.
Number of bootstrap replicates that converged and that did not (if nboot > 0).
The implementation follows R scripts written by Andrew M. Smith (Iowa State
University) for the analyses in Reisberg and Pezdek (2024), which fit what he
describes as “the MAX SDT model (sometimes called the
compound-decision model)”. The default example counts are the Profile-lineup
condition from that work. The constrained fits (constrain_d,
constrain_c) and the nested chi-square comparison in
compare_max_sdt follow the procedure described there.
Estimates from this function will not match those scripts exactly, and the reason is the set of cells entering the discrepancy function.
Smith's scripts fit culprit identifications, total target-present choices and total target-absent choices, together with the rejection counts. Culprit identifications therefore enter twice — once on their own and again inside total target-present choices — so the cells do not partition the sample, and the filler count is never fitted directly.
This function uses five mutually exclusive cells per condition: correct identification, filler identification, target-present rejection, target-absent choice, target-absent rejection. A Pearson chi-square is defined for a partition, so this form keeps the statistic and its degrees of freedom interpretable, and it fits the observed counts better on that criterion.
On the Profile-lineup counts used in the examples below, the two give d' = 1.94 (this function) against d' = 1.78 (Smith's script) for the first condition. Neither is a coding error; they minimise different discrepancies.
Duncan, M. (2006). A signal detection model of compound decision tasks. DRDC Toronto TR 2006-256.
Reisberg, D., & Pezdek, K. (2024). Data analysed with the MAX SDT model in scripts supplied by A. M. Smith (personal communication, 4 April 2024).
Kaesler, M., Dunn, J. C., Ransom, K., & Semmler, C. (2020). Do sequential lineups impair underlying discriminability? Cognitive Research: Principles and Implications, 5, 35. doi:10.1186/s41235-020-00234-5
Wixted, J. T., Vul, E., Mickes, L., & Wilson, B. M. (2018). Models of lineup memory. Cognitive Psychology, 105, 81–114. doi:10.1016/j.cogpsych.2018.06.001
compare_max_sdt, estimate_msdt_params,
sdt_compare
# Single condition fit1 <- fit_max_sdt( n_hit = 69, n_tp_choose = 82, n_fa = 64, N_tp = 96, N_ta = 106, n = 6 ) print(fit1) # Two conditions, free parameters (each two-condition fit takes a few seconds) fit_free <- fit_max_sdt( n_hit = 69, n_tp_choose = 82, n_fa = 64, N_tp = 96, N_ta = 106, n_hit_2 = 68, n_tp_choose_2 = 78, n_fa_2 = 42, N_tp_2 = 96, N_ta_2 = 96, n = 6 ) # Two conditions, equal d' (constrained) fit_eqd <- fit_max_sdt( n_hit = 69, n_tp_choose = 82, n_fa = 64, N_tp = 96, N_ta = 106, n_hit_2 = 68, n_tp_choose_2 = 78, n_fa_2 = 42, N_tp_2 = 96, N_ta_2 = 96, n = 6, constrain_d = TRUE ) # Compare models cmp <- compare_max_sdt(fit_free, fit_eqd) print(cmp)# Single condition fit1 <- fit_max_sdt( n_hit = 69, n_tp_choose = 82, n_fa = 64, N_tp = 96, N_ta = 106, n = 6 ) print(fit1) # Two conditions, free parameters (each two-condition fit takes a few seconds) fit_free <- fit_max_sdt( n_hit = 69, n_tp_choose = 82, n_fa = 64, N_tp = 96, N_ta = 106, n_hit_2 = 68, n_tp_choose_2 = 78, n_fa_2 = 42, N_tp_2 = 96, N_ta_2 = 96, n = 6 ) # Two conditions, equal d' (constrained) fit_eqd <- fit_max_sdt( n_hit = 69, n_tp_choose = 82, n_fa = 64, N_tp = 96, N_ta = 106, n_hit_2 = 68, n_tp_choose_2 = 78, n_fa_2 = 42, N_tp_2 = 96, N_ta_2 = 96, n = 6, constrain_d = TRUE ) # Compare models cmp <- compare_max_sdt(fit_free, fit_eqd) print(cmp)
Fits a single-level GLM (binomial link) to estimate SDT metrics from old/new recognition responses. With centered item coding, the coefficient on item status corresponds to d' (probit) or ln(OR) (logit).
fit_sdt_glm( data, is_old, said_old, covariates = NULL, link = "probit", center_isold = TRUE, interactions = FALSE )fit_sdt_glm( data, is_old, said_old, covariates = NULL, link = "probit", center_isold = TRUE, interactions = FALSE )
data |
Data frame containing trial-level responses. |
is_old |
Column name indicating whether the item is old (1) or new (0). |
said_old |
Column name indicating whether the participant said "old" (1) or "new" (0). |
covariates |
Optional character vector of covariate column names. |
link |
Link function for the binomial model ("probit" or "logit"). |
center_isold |
Logical. If TRUE, recodes is_old to -0.5 / +0.5. |
interactions |
Logical. If TRUE, include is_old interactions with covariates. |
A glm object with SDT attributes.
set.seed(123) n <- 200 is_old <- rep(c(0, 1), each = n / 2) # d' of about 1: higher hit rate for old items said_old <- rbinom(n, 1, ifelse(is_old == 1, 0.7, 0.3)) df <- data.frame(is_old = is_old, said_old = said_old) fit <- fit_sdt_glm(df, is_old = "is_old", said_old = "said_old") extract_sdt_metrics(fit)set.seed(123) n <- 200 is_old <- rep(c(0, 1), each = n / 2) # d' of about 1: higher hit rate for old items said_old <- rbinom(n, 1, ifelse(is_old == 1, 0.7, 0.3)) df <- data.frame(is_old = is_old, said_old = said_old) fit <- fit_sdt_glm(df, is_old = "is_old", said_old = "said_old") extract_sdt_metrics(fit)
Fits a multilevel GLMM (binomial link) to estimate SDT metrics from old/new recognition responses, using random effects for participants and optionally items.
fit_sdt_glmm( data, is_old, said_old, subject_id, covariates = NULL, item_id = NULL, link = "probit", center_isold = TRUE, random_slope = TRUE, interactions = FALSE )fit_sdt_glmm( data, is_old, said_old, subject_id, covariates = NULL, item_id = NULL, link = "probit", center_isold = TRUE, random_slope = TRUE, interactions = FALSE )
data |
Data frame containing trial-level responses. |
is_old |
Column name indicating whether the item is old (1) or new (0). |
said_old |
Column name indicating whether the participant said "old" (1) or "new" (0). |
subject_id |
Column name identifying participants. |
covariates |
Optional character vector of covariate column names. |
item_id |
Optional column name for item-level random intercepts. |
link |
Link function for the binomial model ("probit" or "logit"). |
center_isold |
Logical. If TRUE, recodes is_old to -0.5 / +0.5. |
random_slope |
Logical. If TRUE, include random slope for is_old by subject. |
interactions |
Logical. If TRUE, include is_old interactions with covariates. |
A glmerMod object with SDT attributes.
if (requireNamespace("lme4", quietly = TRUE)) { set.seed(123) n_subj <- 20 n_trial <- 20 df <- expand.grid(subject = seq_len(n_subj), trial = seq_len(n_trial)) df$is_old <- rep(c(0, 1), length.out = nrow(df)) df$said_old <- rbinom(nrow(df), 1, ifelse(df$is_old == 1, 0.7, 0.3)) fit <- fit_sdt_glmm(df, is_old = "is_old", said_old = "said_old", subject_id = "subject", random_slope = FALSE) extract_sdt_metrics(fit) }if (requireNamespace("lme4", quietly = TRUE)) { set.seed(123) n_subj <- 20 n_trial <- 20 df <- expand.grid(subject = seq_len(n_subj), trial = seq_len(n_trial)) df$is_old <- rep(c(0, 1), length.out = nrow(df)) df$said_old <- rbinom(nrow(df), 1, ifelse(df$is_old == 1, 0.7, 0.3)) fit <- fit_sdt_glmm(df, is_old = "is_old", said_old = "said_old", subject_id = "subject", random_slope = FALSE) extract_sdt_metrics(fit) }
Fits an equal-variance or unequal-variance SDT model to ROC data using z-ROC analysis. Extracts discriminability (d'), decision criteria (c), and optionally the target/lure variance ratio.
fit_sdt_roc( data, lineup_size = NULL, model = c("equal_variance", "unequal_variance"), bootstrap = TRUE, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, innocent_suspect = "auto" )fit_sdt_roc( data, lineup_size = NULL, model = c("equal_variance", "unequal_variance"), bootstrap = TRUE, n_bootstrap = 1000, conf_level = 0.95, seed = NULL, innocent_suspect = "auto" )
data |
A dataframe with columns: target_present, identification, confidence OR a rocdata object from make_rocdata() |
lineup_size |
Integer. Number of people in lineup (required if data is dataframe) |
model |
Character. "equal_variance" or "unequal_variance" (default = "equal_variance") |
bootstrap |
Logical. Compute bootstrap confidence intervals? (default = TRUE) |
n_bootstrap |
Integer. Number of bootstrap samples (default = 1000) |
conf_level |
Numeric. Confidence level for intervals (default = 0.95) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
**z-ROC Analysis**:
The z-ROC is created by plotting z-transformed hit rates against z-transformed false alarm rates for each confidence level.
Under equal-variance SDT: - z-ROC should be linear with slope = 1 - d' = z(HR) - z(FAR) for any criterion - Intercept = d' in the line z(HR) = z(FAR) + d'
Under unequal-variance SDT: - Slope = SD_lure / SD_target - Slope > 1 indicates greater variability in lure distribution
**Parameter Estimation**: - d' = average distance between distributions in standard deviation units - c = decision criterion (0 = unbiased, positive = conservative, negative = liberal) - Higher d' = better discriminability
**Bootstrap Confidence Intervals**: If bootstrap = TRUE, resamples data with replacement to estimate standard errors and confidence intervals for all parameters.
An S3 object of class "sdt_roc_fit" containing:
dprime: Overall discriminability (d')
criteria: Decision criterion for each confidence level (c values)
variance_ratio: Ratio of lure SD to target SD, the z-ROC slope (if unequal_variance)
slope: Slope of z-ROC
intercept: Intercept of z-ROC
roc_data: Original ROC data with z-transforms
model_type: Type of model fit
bootstrap_ci: Confidence intervals (if bootstrap = TRUE)
fit_diagnostics: R-squared, residuals, etc.
Macmillan, N. A., & Creelman, C. D. (2005). Detection theory: A user's guide (2nd ed.).
Mickes, L. (2015). Receiver operating characteristic analysis and confidence-accuracy characteristic analysis in investigations of system variables. Journal of Applied Research in Memory and Cognition, 4(2), 93-102.
# Simulate data sim_data <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 1.5, conf_levels = 5, seed = 123 ) # Fit equal-variance SDT model sdt_fit <- fit_sdt_roc(sim_data, lineup_size = 6, bootstrap = FALSE) print(sdt_fit) plot(sdt_fit) # Get d' estimate sdt_fit$dprime # Fit unequal-variance model sdt_uv <- fit_sdt_roc( sim_data, lineup_size = 6, model = "unequal_variance", bootstrap = FALSE ) sdt_uv$variance_ratio # Use more replicates for publication analyses. sdt_boot <- fit_sdt_roc( sim_data, lineup_size = 6, n_bootstrap = 200, seed = 123 )# Simulate data sim_data <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 1.5, conf_levels = 5, seed = 123 ) # Fit equal-variance SDT model sdt_fit <- fit_sdt_roc(sim_data, lineup_size = 6, bootstrap = FALSE) print(sdt_fit) plot(sdt_fit) # Get d' estimate sdt_fit$dprime # Fit unequal-variance model sdt_uv <- fit_sdt_roc( sim_data, lineup_size = 6, model = "unequal_variance", bootstrap = FALSE ) sdt_uv$variance_ratio # Use more replicates for publication analyses. sdt_boot <- fit_sdt_roc( sim_data, lineup_size = 6, n_bootstrap = 200, seed = 123 )
Fits the 2-HT multinomial processing tree (MPT) model to lineup identification data. This model separates detection-based processes (dP, dA) from non-detection-based processes (biased selection b, guessing-based selection g) using the full 2x3 outcome structure of lineup procedures.
fit_winter_2ht( data, lineup_size = 6, target_present = NULL, identification = NULL, start_params = NULL, method = "L-BFGS-B", ... )fit_winter_2ht( data, lineup_size = 6, target_present = NULL, identification = NULL, start_params = NULL, method = "L-BFGS-B", ... )
data |
A data frame with lineup identification data, OR a named vector/list of counts with the following elements: - n_tp_suspect: Number of suspect IDs in target-present (TP) lineups - n_tp_filler: Number of filler IDs in TP lineups - n_tp_reject: Number of rejections in TP lineups - n_ta_suspect: Number of suspect IDs in target-absent (TA) lineups - n_ta_filler: Number of filler IDs in TA lineups - n_ta_reject: Number of rejections in TA lineups |
lineup_size |
Size of the lineup (L). Default is 6. |
target_present |
Column name indicating target presence (TRUE/FALSE) if data is a data frame. |
identification |
Column name for identification decision if data is a data frame. Should contain "suspect", "filler", or "reject". |
start_params |
Optional named vector of starting parameter values (dP, dA, b, g). If NULL, uses reasonable defaults. |
method |
Optimization method. Default is "L-BFGS-B" for bounded optimization. |
... |
Additional arguments passed to optim(). |
The 2-HT model (Winter, Menne, Bell, & Buchner, 2022) uses four parameters:
dP: Probability of detecting culprit presence (0 to 1)
dA: Probability of detecting culprit absence (0 to 1)
b: Probability of biased suspect selection (0 to 1)
g: Probability of guessing-based selection (0 to 1)
Model equations for culprit-present lineups:
P(suspect ID) = dP + (1-dP) * [b + (1-b) * g * (1/L)]
P(filler ID) = (1-dP) * (1-b) * g * ((L-1)/L)
P(reject) = (1-dP) * (1-b) * (1-g)
Model equations for culprit-absent lineups:
P(suspect ID) = (1-dA) * [b + (1-b) * g * (1/L)]
P(filler ID) = (1-dA) * (1-b) * g * ((L-1)/L)
P(reject) = dA + (1-dA) * (1-b) * (1-g)
With one aggregate 2 by 3 outcome table, the four free parameters fit the four independent cell proportions, so the model is saturated and has no residual goodness-of-fit degrees of freedom. Inference about model fit or restrictions requires additional conditions, parameter constraints, or participant/item-level replication; the reported AIC/BIC describe this likelihood fit but do not by themselves validate the process interpretation.
A feasible inverse of the published tree supplies the saturated interior solution; otherwise bounded optimization uses multiple starts. Regular Hessian SEs are returned only for interior fits. Boundary fits retain their estimates but have unavailable regular SEs. A low rejection percentage alone is not used as a criterion for refusing a fit.
An object of class "winter_2ht" containing:
parameters |
Estimated parameters (dP, dA, b, g) |
se |
Standard errors of parameter estimates |
loglik |
Log-likelihood of the fitted model |
aic |
Akaike Information Criterion |
bic |
Bayesian Information Criterion |
fitted_probs |
Model-predicted probabilities for each outcome |
observed_counts |
Observed counts |
expected_counts |
Expected counts under the model |
lineup_size |
Lineup size used |
convergence |
Convergence code from optim |
n_total |
Total sample size |
Winter, K., Menne, N. M., Bell, R., & Buchner, A. (2022). Experimental validation of a multinomial processing tree model for analyzing eyewitness identification decisions. Scientific Reports, 12, 15571. https://doi.org/10.1038/s41598-022-19513-w
# Example 1: Using count data directly counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) print(fit) summary(fit) # Example 2: Using a data frame data(lineup_example) fit <- fit_winter_2ht( lineup_example, lineup_size = 6, target_present = "target_present", identification = "identification" )# Example 1: Using count data directly counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) print(fit) summary(fit) # Example 2: Using a data frame data(lineup_example) fit <- fit_winter_2ht( lineup_example, lineup_size = 6, target_present = "target_present", identification = "identification" )
Creates a formatted table comparing model fits for publication or reporting.
format_comparison_table(comparison_obj, format = "console", digits = 3)format_comparison_table(comparison_obj, format = "console", digits = 3)
comparison_obj |
A model_comparison object from compare_models() |
format |
Character. Output format: "console" (default), "markdown", "latex" |
digits |
Integer. Number of decimal places (default = 3) |
Creates a publication-ready table with key statistics for each model:
2-HT: Parameters (dP, dA, b, g), AIC, BIC
EIG: Information gain, efficiency
Full ROC: AUC
For format = "console", the comparison dataframe. For
"markdown" and "latex", a knitr_kable object, which
is a character vector with **one element per line**. Print it with
cat(x, sep = "\n") inside a chunk using results = "asis";
plain cat(x) joins the lines with spaces and the table collapses
into a single paragraph.
data(lineup_example) comparison <- compare_models(lineup_example) format_comparison_table(comparison)data(lineup_example) comparison <- compare_models(lineup_example) format_comparison_table(comparison)
This function computes functional size (N/D), where N = number of mock witnesses, & D = number of mock witnesses choosing the target
func_size(lineup_vec, target_pos)func_size(lineup_vec, target_pos)
lineup_vec |
A numeric vector of lineup choices |
target_pos |
A scalar, representing target position in lineup. Must be declared by user |
An estimation of the number of plausible lineup members in a given lineup.
Functional size of lineup
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Call: x <- func_size(lineup_vec, target_pos) x <- func_size(lineup_vec, 3)#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Call: x <- func_size(lineup_vec, target_pos) x <- func_size(lineup_vec, 3)
Computes a Bayesian posterior distribution for the functional size of a lineup using a Beta-Binomial conjugate model for the suspect-selection rate.
func_size_bayes( lineup_vec, target_pos, alpha = 0.5, S = 10000, credible_mass = 0.95, threshold = NULL )func_size_bayes( lineup_vec, target_pos, alpha = 0.5, S = 10000, credible_mass = 0.95, threshold = NULL )
lineup_vec |
A numeric vector of lineup choices. |
target_pos |
A scalar; the position of the suspect in the lineup. |
alpha |
Dirichlet concentration (prior strength). A positive scalar.
Named shortcuts: |
S |
Number of posterior draws. Default 10000. |
credible_mass |
Width of the equal-tailed credible interval. Default 0.95. |
threshold |
Optional numeric. If supplied, reports |
Functional size is the reciprocal of the suspect-selection rate
. The Beta-Binomial conjugate model gives:
Posterior draws of are transformed via .
The default Jeffreys prior () is recommended. The posterior
gives direct probability statements such as ,
where is the nominal lineup size.
The posterior mean of a reciprocal is computed exactly, not from the draws:
with and ,
when . When
(for example, no suspect choices under the Jeffreys prior) the
mean is infinite, and the Monte Carlo average of the draws would not settle
on any value as S grew. Report the median and interval instead.
An object of class "func_size_bayes" containing:
Numeric vector of length S: posterior draws of functional size.
Posterior point estimates. The
mean is exact, and is Inf when it does not exist (see Details).
Named two-element vector (lower, upper).
Input metadata.
Threshold and probabilities (or NULL).
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
set.seed(1) lineup_vec <- round(runif(100, 1, 6)) res <- func_size_bayes(lineup_vec, target_pos = 3) print(res) plot(res) # Threshold: P(F < 6 | data) res_t <- func_size_bayes(lineup_vec, target_pos = 3, threshold = 6) print(res_t)set.seed(1) lineup_vec <- round(runif(100, 1, 6)) res <- func_size_bayes(lineup_vec, target_pos = 3) print(res) plot(res) # Threshold: P(F < 6 | data) res_t <- func_size_bayes(lineup_vec, target_pos = 3, threshold = 6) print(res_t)
This function is a master function, calling other functions it needs, and reporting results in some detail
func_size_report(lineup_vec, target_pos, k, R = 1000)func_size_report(lineup_vec, target_pos, k, R = 1000)
lineup_vec |
A numeric vector of lineup choices |
target_pos |
A scalar, representing target position in lineup. Must be declared by user |
k |
Number of members in lineup. Must be specified by user (scalar). |
R |
Number of bootstrap samples. Defaults to 1000 |
Functional size is the reciprocal of the proportion of mock witnesses
choosing the target, p. The bootstrap resamples p, which stays finite when a
resample contains no target choices, and the interval limits for p are
inverted. Percentile and BCa intervals respect monotone transformations, so
they are the intervals for functional size itself, computed from the whole
bootstrap distribution; a lower limit of 0 for p gives an unbounded
(Inf) upper limit. The normal interval is the normal-theory interval
for p, inverted. The exact interval inverts the Clopper-Pearson interval for
p (binom.test).
Near zero or one, empirical bootstrap intervals can have severe undercoverage,
including a degenerate interval when the suspect was never chosen. Prefer
the exact interval in these cases; retaining infinite limits alone does not
guarantee nominal bootstrap coverage.
Function depends on functions from package 'boot'
Invisibly returns the functional-size estimate; the normal,
percentile and BCa bootstrap intervals; the exact interval; the number of
replications; the number of replications in which nobody chose the target
(n_infinite); and the bootstrap object, whose statistic is the
proportion choosing the target.
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Call: x <- func_size_report(lineup_vec, target_pos, 6) x <- func_size_report(lineup_vec, 3, 6)#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Call: x <- func_size_report(lineup_vec, target_pos, 6) x <- func_size_report(lineup_vec, 3, 6)
This function is a base function for the bootstrapping that ensues to compute bootstrapped confidence intervals for the estimate of functional size
func_size.boot(lineup_vec, d = d, target_pos)func_size.boot(lineup_vec, d = d, target_pos)
lineup_vec |
A numeric vector of lineup choices |
d |
Indices for bootstrap resampling |
target_pos |
A scalar, representing position of target in lineup. Must be declared by user |
Function is called bootstrap resampling using boot function (in package 'boot') This function is never called by the user - it is called in the functional size master function
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
# Lineup choices of 50 mock witnesses; target in position 3 set.seed(1) lineup_vec <- sample(1:6, 50, replace = TRUE) # Functional size for the observed data func_size.boot(lineup_vec, seq_along(lineup_vec), target_pos = 3) # Bootstrap functional size boot_fs <- boot::boot(lineup_vec, func_size.boot, R = 100, target_pos = 3) boot::boot.ci(boot_fs, type = "perc")# Lineup choices of 50 mock witnesses; target in position 3 set.seed(1) lineup_vec <- sample(1:6, 50, replace = TRUE) # Functional size for the observed data func_size.boot(lineup_vec, seq_along(lineup_vec), target_pos = 3) # Bootstrap functional size boot_fs <- boot::boot(lineup_vec, func_size.boot, R = 100, target_pos = 3) boot::boot.ci(boot_fs, type = "perc")
Function for computing arbitrary percentile of bootstrapped lineup proportion
gen_boot_propci(lineuprops, perc = 0.05)gen_boot_propci(lineuprops, perc = 0.05)
lineuprops |
A vector of bootstrapped lineup proportions |
perc |
Percentile to be computed. Must be declared by user (scalar) Defaults to .05 |
Can be used to calculate confidence intervals at desired level of alpha
Arbitrary percentile of bootstrapped lineup proportion
#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 k <- 6 #Bootstrap data: lineup_boot_df <- gen_boot_samples(lineup_vec, 1000) #Compute proportion for bootstrap samples: lineuprops <- gen_lineup_prop(lineup_boot_df, target_pos, 6) #Get boot CIs: prop_bootci_lower <- gen_boot_propci(lineuprops, perc = .025) prop_bootci_upper <- gen_boot_propci(lineuprops, perc = .975)#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 k <- 6 #Bootstrap data: lineup_boot_df <- gen_boot_samples(lineup_vec, 1000) #Compute proportion for bootstrap samples: lineuprops <- gen_lineup_prop(lineup_boot_df, target_pos, 6) #Get boot CIs: prop_bootci_lower <- gen_boot_propci(lineuprops, perc = .025) prop_bootci_upper <- gen_boot_propci(lineuprops, perc = .975)
Function for computing the mean, median, and bootstrap standard error. The standard deviation of the bootstrap replicates is the estimated standard error; it is not divided by the square root of the number of replicates.
gen_boot_propmean_se(lineuprops)gen_boot_propmean_se(lineuprops)
lineuprops |
A dataframe of bootstrapped lineup proportions |
Mean, median, standard deviation, standard error & 95 CIs of lineup proportion across a bootstrapped dataframe
#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Bootstrap data: lineup_boot_df <- gen_boot_samples(lineup_vec, 1000) #Compute proportion for bootstrap samples: lineuprops <- gen_lineup_prop(lineup_boot_df, target_pos = 3, k = 6) #Call: gen_boot_propmean_se(lineuprops) #OR: lineuprops <- boot::boot(lineup_vec, lineup_prop_boot, target_pos = 3, R = 1000) gen_boot_propmean_se(lineuprops$t)#Data: lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Bootstrap data: lineup_boot_df <- gen_boot_samples(lineup_vec, 1000) #Compute proportion for bootstrap samples: lineuprops <- gen_lineup_prop(lineup_boot_df, target_pos = 3, k = 6) #Call: gen_boot_propmean_se(lineuprops) #OR: lineuprops <- boot::boot(lineup_vec, lineup_prop_boot, target_pos = 3, R = 1000) gen_boot_propmean_se(lineuprops$t)
Function for generating bootstrapped samples from 1 vector of lineup data
gen_boot_samples(lineup_vec, bootno)gen_boot_samples(lineup_vec, bootno)
lineup_vec |
A numeric vectors of lineup choices |
bootno |
Number of bootstrap samples |
A dataframe of bootstrapped lineup data
#Data: lineup_vec <- round(runif(100,1,6)) bootno <- 1000 #Call: bootdf <- gen_boot_samples(lineup_vec, bootno)#Data: lineup_vec <- round(runif(100,1,6)) bootno <- 1000 #Call: bootdf <- gen_boot_samples(lineup_vec, bootno)
Function for generating bootstrapped samples for of k vectors of lineup choices
gen_boot_samples_list(lineup_list, bootno)gen_boot_samples_list(lineup_list, bootno)
lineup_list |
A list containing k vectors of lineup choices for k lineups, in which the target was either absent or present |
bootno |
Number of bootstrap samples |
A list of bootstrapped lineup data Length of list = number of bootstrap sample draws
#Data: A <- round(runif(100,1,6)) B <- round(runif(70,1,5)) C <- round(runif(20,1,4)) linelist <- list(A, B, C) rm(A, B, C) bootno <- 1000 #Call: bootdata <- gen_boot_samples_list(linelist, bootno)#Data: A <- round(runif(100,1,6)) B <- round(runif(70,1,5)) C <- round(runif(20,1,4)) linelist <- list(A, B, C) rm(A, B, C) bootno <- 1000 #Call: bootdata <- gen_boot_samples_list(linelist, bootno)
Function for computing Effective Size (Tredoux, 1998) on lineups contained as columns in a df, usually from a bootstrapped sample
gen_esize_m(lineup_boot_df, k)gen_esize_m(lineup_boot_df, k)
lineup_boot_df |
A dataframe containing bootstrapped samples of lineup data |
k |
Number of members in lineup. Must be specified by user (scalar). |
This function computes effective size for k lineups simultaneously.
A vector of effective size calculations for each lineup in bootstrapped df
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100,1,6)) bootdf <- gen_boot_samples(lineup_vec, 1000) #Call: esize_vec <- gen_esize_m(bootdf, 6)#Data: lineup_vec <- round(runif(100,1,6)) bootdf <- gen_boot_samples(lineup_vec, 1000) #Call: esize_vec <- gen_esize_m(bootdf, 6)
Computes one requested quantile from a vector of bootstrapped Malpass effective sizes. Call the function twice (for example, at 0.025 and 0.975) to obtain both endpoints of a percentile interval.
gen_esize_m_ci(lineupsizes, perc = 0.05)gen_esize_m_ci(lineupsizes, perc = 0.05)
lineupsizes |
A non-empty numeric vector of bootstrapped effective sizes. |
perc |
A single quantile probability between 0 and 1. Defaults to 0.05. |
A single named bootstrap quantile.
#Data: lineup_vec <- rep(1:6, length.out = 100) k <- 6 #Use gen_boot_samples to get bootstrapped data: bootdata <- gen_boot_samples(lineup_vec, 1000) #Compute effective size over df of bootstrapped data: lineupsizes <- gen_esize_m(bootdata, 6) #Call: gen_esize_m_ci(lineupsizes) gen_esize_m_ci(lineupsizes, perc = .025) gen_esize_m_ci(lineupsizes, perc = .975)#Data: lineup_vec <- rep(1:6, length.out = 100) k <- 6 #Use gen_boot_samples to get bootstrapped data: bootdata <- gen_boot_samples(lineup_vec, 1000) #Compute effective size over df of bootstrapped data: lineupsizes <- gen_esize_m(bootdata, 6) #Call: gen_esize_m_ci(lineupsizes) gen_esize_m_ci(lineupsizes, perc = .025) gen_esize_m_ci(lineupsizes, perc = .975)
Function for computing lineup proportion over a dataframe
gen_lineup_prop(lineup_boot_df, target_pos, k)gen_lineup_prop(lineup_boot_df, target_pos, k)
lineup_boot_df |
Dataframe of lineup choices (usually a bootstrapped set) |
target_pos |
A scalar, representing target position in lineup. Must be declared by user. |
k |
A vector indexing number of members in each lineup pair (nominal size). Must be specified by user (scalar). |
A vector of bootstrapped proportions, indicating the frequency with which a target was identified in a lineup Length of vector = number of bootstrap sample draws
#Data lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Get bootstrapped data: lineup_boot_df <- gen_boot_samples(lineup_vec, 1000) #Call: lineuprops <- gen_lineup_prop(lineup_boot_df, 3, 6)#Data lineup_vec <- round(runif(100, 1, 6)) target_pos <- 3 #Get bootstrapped data: lineup_boot_df <- gen_boot_samples(lineup_vec, 1000) #Call: lineuprops <- gen_lineup_prop(lineup_boot_df, 3, 6)
A function that takes a table of lineup choices and generates a vector of raw lineup data
gen_linevec(lineup_tab, k)gen_linevec(lineup_tab, k)
lineup_tab |
A table of lineup choices |
k |
Nominal size (i.e.,total number of lineup members). Must be declared by user |
Returns a vector of lineup choices
#Data: lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) #Call: lineup_vec <- gen_linevec(lineup_tab, 6)#Data: lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) #Call: lineup_vec <- gen_linevec(lineup_tab, 6)
Extracts a deep learning embedding vector from a face image using a specified model. The embedding can be used to compute face similarity.
get_embedding( img_path, model = "ArcFace", detector = "retinaface", align = TRUE, enforce_detection = TRUE, expand_percentage = 0 )get_embedding( img_path, model = "ArcFace", detector = "retinaface", align = TRUE, enforce_detection = TRUE, expand_percentage = 0 )
img_path |
Path to the image file containing a face. |
model |
Character. The face recognition model to use.
Default is "ArcFace" (recommended). See |
detector |
Character. Face detection backend.
Default is "retinaface" (most accurate). See |
align |
Logical. Whether to align the face before embedding. Default is TRUE (recommended). |
enforce_detection |
Logical. If TRUE, raises an error if no face is detected. If FALSE, attempts to use the entire image. Default is TRUE. |
expand_percentage |
Numeric. Percentage to expand the detected face region. Default is 0. |
The embedding dimension depends on the model:
ArcFace: 512 dimensions
Facenet512: 512 dimensions
Facenet: 128 dimensions
VGG-Face: 4096 dimensions
One example configuration used in the package is:
model = "ArcFace"
detector = "retinaface"
align = TRUE
These defaults do not establish superior accuracy for every population, image source or research question, or validate lineup fairness.
A named list containing:
embedding - Numeric vector of face embeddings
facial_area - List with x, y, w, h of detected face region
face_confidence - Detection confidence score
## Not run: # Get embedding for a single face emb <- get_embedding("path/to/face.jpg") # Access the embedding vector emb$embedding # Use a different model emb_facenet <- get_embedding("path/to/face.jpg", model = "Facenet512") ## End(Not run)## Not run: # Get embedding for a single face emb <- get_embedding("path/to/face.jpg") # Access the embedding vector emb$embedding # Use a different model emb_facenet <- get_embedding("path/to/face.jpg", model = "Facenet512") ## End(Not run)
Extracts dense facial landmarks using MediaPipe Face Mesh, which provides 478 3D landmark points covering the entire face, including the irises.
get_face_landmarks(img_path, return_3d = TRUE)get_face_landmarks(img_path, return_3d = TRUE)
img_path |
Path to the image file. |
return_3d |
Logical. If TRUE, returns 3D coordinates (x, y, z). If FALSE, returns only 2D coordinates (x, y). Default is TRUE. |
MediaPipe Face Mesh provides detailed landmarks covering:
Face oval/contour
Left and right eyebrows
Left and right eyes, with 10 iris points (468 to 477)
Nose
Lips (inner and outer contours)
Face interior regions (cheeks, forehead)
The x and y coordinates are normalized to [0, 1] relative to image dimensions. The z coordinate represents depth relative to the face center.
Older MediaPipe releases expose the legacy
mediapipe.solutions.face_mesh interface, which is used when present.
Current releases have only the Tasks API (FaceLandmarker), which
needs a model file. It is downloaded once, on first use, to
tools::R_user_dir("r4lineups", "cache"). On macOS the Tasks API runs
on the GPU delegate, because the CPU delegate in current macOS wheels aborts
the process.
A list containing:
landmarks - Matrix of landmark coordinates (478 x 2 or 478 x 3)
face_detected - Logical indicating if a face was found
image_width - Width of the input image
image_height - Height of the input image
n_landmarks - Number of landmarks (when a face was found)
Requires MediaPipe to be installed (installed by default with install_r4lineups_python()).
## Not run: # Get face mesh landmarks mesh <- get_face_landmarks("face.jpg") # Plot the landmarks plot(mesh$landmarks[, 1], mesh$landmarks[, 2], pch = ".") ## End(Not run)## Not run: # Get face mesh landmarks mesh <- get_face_landmarks("face.jpg") # Plot the landmarks plot(mesh$landmarks[, 1], mesh$landmarks[, 2], pch = ".") ## End(Not run)
Extracts the 5 key facial landmarks commonly used for face alignment: left eye, right eye, nose tip, left mouth corner, right mouth corner.
get_key_landmarks(img_path, detector = "retinaface")get_key_landmarks(img_path, detector = "retinaface")
img_path |
Path to the image file. |
detector |
Detector backend. "retinaface" provides these landmarks directly. |
These 5 landmarks are sufficient for basic face alignment and are
commonly used in face recognition pipelines. For more detailed
landmarks (478 points), use get_face_landmarks.
A list with named landmark coordinates:
left_eye - (x, y) coordinates
right_eye - (x, y) coordinates
nose - (x, y) coordinates
mouth_left - (x, y) coordinates
mouth_right - (x, y) coordinates
## Not run: # Get key landmarks lm <- get_key_landmarks("face.jpg") # Calculate inter-pupillary distance ipd <- sqrt(sum((lm$left_eye - lm$right_eye)^2)) ## End(Not run)## Not run: # Get key landmarks lm <- get_key_landmarks("face.jpg") # Calculate inter-pupillary distance ipd <- sqrt(sum((lm$left_eye - lm$right_eye)^2)) ## End(Not run)
Quick check to determine if an image contains a detectable face that can be used for embedding extraction.
has_valid_face(img_path, detector = "retinaface", min_confidence = 0.5)has_valid_face(img_path, detector = "retinaface", min_confidence = 0.5)
img_path |
Path to the image file. |
detector |
Detector to use. |
min_confidence |
Minimum detection confidence (0-1). |
Logical. TRUE if a valid face is detected.
## Not run: if (has_valid_face("image.jpg")) { emb <- get_embedding("image.jpg") } ## End(Not run)## Not run: if (has_valid_face("image.jpg")) { emb <- get_embedding("image.jpg") } ## End(Not run)
This function provides assesses the homogeneity of the diagnosticity ratio of k lineup pairs.
homog_diag(lineup_pres_list, lineup_abs_list, pos_list, k, conf.level = 0.95)homog_diag(lineup_pres_list, lineup_abs_list, pos_list, k, conf.level = 0.95)
lineup_pres_list |
A list containing k vectors of lineup choices for k lineups, in which the target was present |
lineup_abs_list |
A list containing k vectors of lineup choices for k lineups, in which the target was absent |
pos_list |
Suspect positions for each lineup pair. See |
k |
A vector indexing number of members in each lineup pair (nominal size). Must be specified by user (scalar). |
conf.level |
Confidence level for the per-pair intervals, and the complement of the level at which heterogeneity is declared. Defaults to 0.95. |
Master function for assessing homogeneity of diagnosticity ratio for k independent lineups.
The chi-square is Cochran's Q, ,
so it measures the spread of the k ratios around their pooled
value. It answers a different question from a confidence interval on
the pooled ratio, which asks whether the average ratio differs from 1.
The two can disagree: ratios that differ in direction (say 6, 1 and
0.44) give a very large Q while averaging to roughly 1. When Q is
significant the ratios do not share a common value, the pooled
estimate should not be reported as a single summary, and the pairs
should be interpreted separately. This function prints a note to that
effect and records it in the heterogeneous element.
Invisibly, a list with the pooled (precision-weighted) diagnosticity
ratio, the chi-square statistic and its degrees of freedom and p-value,
a heterogeneous flag, and a parameters data frame giving
the cell counts plus , , its variance, its weight and
a confidence interval for each lineup pair. The same information is
printed.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Call: homog_diag(lineup_pres_list, lineup_abs_list, pos_list, k)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Call: homog_diag(lineup_pres_list, lineup_abs_list, pos_list, k)
Function for computing bootstrapped estimates of homogeneity of diagnosticity ratio
homog_diag_boot( lineup_pres_list, lineup_abs_list, k, R = 100, pos_list = NULL, seed = NULL, conf.level = 0.95 )homog_diag_boot( lineup_pres_list, lineup_abs_list, k, R = 100, pos_list = NULL, seed = NULL, conf.level = 0.95 )
lineup_pres_list |
A list containing k vectors of lineup choices for k lineups, in which the target was present |
lineup_abs_list |
A list containing k vectors of lineup choices for k lineups, in which the target was absent |
k |
Number of members in lineup. Must be specified by user (scalar). |
R |
Number of bootstrap replications. Defaults to R = 100. |
pos_list |
Suspect positions for each lineup pair, in the same format as
|
seed |
Optional integer seed for reproducible resampling. |
conf.level |
Confidence level for the percentile intervals and the per-pair intervals, and the complement of the level at which heterogeneity is declared. Defaults to 0.95. |
Computes bootstrapped diagnosticity ratio with chi-squared estimate, significance level and confidence intervals for k lineup pairs.
The interval on the pooled ratio and the chi-square answer different
questions, and can disagree. See homog_diag for the
distinction; as there, a note is printed whenever the k ratios are
heterogeneous and the pooled estimate should not be reported on its
own.
Invisibly returns a list containing the observed mean diagnosticity,
chi-square statistic with degrees of freedom and asymptotic p-value,
percentile intervals, a heterogeneous flag, a parameters data
frame of per-pair ratios and intervals, all attempted bootstrap draws
(failed rows retained as NA), n_successful, n_failed, and
R. Intervals are conditional on successful draws when failures occur.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) pos_list <- c(3, 2, 1) k <- c(6, 5, 4) homog_diag_boot(lineup_pres_list, lineup_abs_list, k, R = 20, pos_list = pos_list, seed = 1)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) pos_list <- c(3, 2, 1) k <- c(6, 5, 4) homog_diag_boot(lineup_pres_list, lineup_abs_list, k, R = 20, pos_list = pos_list, seed = 1)
Computes the Agresti-Agresti index of diversity I, the intermediate quantity used to construct effective size E' = 1/(1 - I) (Tredoux, 1998).
i_esize_T(lineup_tab)i_esize_T(lineup_tab)
lineup_tab |
A table of lineup choices. |
Tredoux (1998) used Agresti and Agresti's (1978) index of diversity:
where is the observed count for lineup member and
is the total number of choices. This equals ,
where . The effective size is the reciprocal transform
, computed by esize_T.
A scalar: the diversity index I.
Agresti, A., & Agresti, B. F. (1978). Statistical analysis of qualitative variation. Sociological Methodology, 9, 204-237.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) i <- i_esize_T(lineup_tab)lineup_vec <- round(runif(100, 1, 6)) lineup_tab <- table(lineup_vec) i <- i_esize_T(lineup_tab)
Estimates innocent-suspect identification rate using effective lineup size, accounting for implausible fillers and lineup bias.
innocent_id_rate_effective(error_rate, effective_size)innocent_id_rate_effective(error_rate, effective_size)
error_rate |
Numeric. Overall mistaken identification rate from culprit-absent lineups. |
effective_size |
Numeric. Effective size computed from the distribution
of mistaken IDs (typically via |
This represents a more realistic estimate when lineup fairness is uncertain. Uses the distribution of mistaken IDs to estimate the number of plausible lineup members. The formula is: innocent_id_rate = error_rate / effective_size
When effective size < nominal size, this indicates lineup bias and increases the estimated innocent-suspect risk.
Numeric. Estimated innocent-suspect identification rate.
Fitzgerald, R. J., Tredoux, C. G., & Juncu, S. (2023). Estimation of eyewitness error rates in fair and biased lineups. Law and Human Behavior.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
# 30% mistaken ID rate with an effective size of 4.2 innocent_id_rate_effective(0.30, 4.2)# 30% mistaken ID rate with an effective size of 4.2 innocent_id_rate_effective(0.30, 4.2)
Estimates innocent-suspect identification rate using nominal lineup size, assuming all lineup members are equally plausible (perfectly fair lineup).
innocent_id_rate_nominal(error_rate, lineup_size)innocent_id_rate_nominal(error_rate, lineup_size)
error_rate |
Numeric. Overall mistaken identification rate from culprit-absent lineups. |
lineup_size |
Integer. Nominal number of people in the lineup. |
This represents the "best case" estimate assuming perfect lineup fairness. The formula is: innocent_id_rate = error_rate / lineup_size
Numeric. Estimated innocent-suspect identification rate.
Fitzgerald, R. J., Tredoux, C. G., & Juncu, S. (2023). Estimation of eyewitness error rates in fair and biased lineups. Law and Human Behavior.
# 30% mistaken ID rate in a fair 6-person lineup innocent_id_rate_nominal(0.30, 6)# 30% mistaken ID rate in a fair 6-person lineup innocent_id_rate_nominal(0.30, 6)
Estimates innocent-suspect identification rate assuming all mistaken IDs are innocent-suspect IDs (no correction for lineup size).
innocent_id_rate_uncorrected(error_rate)innocent_id_rate_uncorrected(error_rate)
error_rate |
Numeric. Overall mistaken identification rate from culprit-absent lineups. |
This represents the "worst case" estimate, treating every mistaken ID as an innocent-suspect identification. Provides an upper bound on innocent-suspect risk. The formula is simply: innocent_id_rate = error_rate
Numeric. Estimated innocent-suspect identification rate (equal to error_rate).
Fitzgerald, R. J., Tredoux, C. G., & Juncu, S. (2023). Estimation of eyewitness error rates in fair and biased lineups. Law and Human Behavior.
# Worst case: all mistaken IDs treated as innocent-suspect IDs innocent_id_rate_uncorrected(0.30)# Worst case: all mistaken IDs treated as innocent-suspect IDs innocent_id_rate_uncorrected(0.30)
Explicitly creates or reuses an isolated Python virtual environment and
installs the audited pyWitness Git revision used by
fit_lineup_models. This function is never called when the
package is loaded or checked.
install_pywitness( envname = .pywitness_default_env, python = NULL, pip_options = NULL, force = FALSE )install_pywitness( envname = .pywitness_default_env, python = NULL, pip_options = NULL, force = FALSE )
envname |
Name or path of the virtual environment. |
python |
Optional path to the Python executable used to create a new virtual environment. |
pip_options |
Optional character vector passed to pip. |
force |
Logical. Reinstall the pinned revision if already present. |
The installation is pinned to Git revision
e726dcfc09423d0e0ff7f46c8e3a711040293eba. pyWitness is GPL-3
software maintained separately from r4lineups; no pyWitness source or
documentation is bundled in this package. The immutable commit archive is
used so installation does not require a local Git executable.
Invisibly returns the environment name.
Mickes, L., Seale-Carlisle, T. M., Chen, X., & Boogert, S. (2024). pyWitness 1.0: A Python eyewitness identification analysis toolkit. Behavior Research Methods, 56, 1533–1550.
## Not run: install_pywitness() check_pywitness_deps(initialize = TRUE) ## End(Not run)## Not run: install_pywitness() check_pywitness_deps(initialize = TRUE) ## End(Not run)
Installs the required Python packages using pip. This function should be run once after installing the r4lineups package.
install_r4lineups_python( method = "auto", envname = NULL, pip_options = NULL, gpu = FALSE, python_version = NULL )install_r4lineups_python( method = "auto", envname = NULL, pip_options = NULL, gpu = FALSE, python_version = NULL )
method |
Installation method. Default is "auto" which uses pip. |
envname |
Name of virtual environment to use (optional). If NULL, installs to the system Python or currently active environment. |
pip_options |
Additional options to pass to pip (e.g., "–user"). |
gpu |
Logical. If TRUE, installs tensorflow with GPU support. Default is FALSE. |
python_version |
Python version to build the virtual environment with,
e.g. |
This function installs:
deepface - Main face analysis library
opencv-python - Image processing
numpy - Numerical computing
tf-keras - TensorFlow/Keras backend
mediapipe - For 478-point face mesh (optional but recommended)
retinaface - Best face detector
Python version. TensorFlow publishes wheels for a limited range of
Python versions (currently 3.9 to 3.12). On a machine whose default
interpreter is newer than that, pip finds no TensorFlow distribution at all
and the install fails partway through with
No matching distribution found for tensorflow, after downloading the
other packages. This function therefore checks the interpreter before
installing anything, and stops with an explanatory message if it is outside
the supported range. Pass python_version to select a suitable
interpreter, which requires envname so that a virtual environment can
be built from it.
TRUE, invisibly.
## Not run: # Basic installation install_r4lineups_python() # Install to a virtual environment install_r4lineups_python(envname = "r4lineups-face-env") # Build the environment with a TensorFlow-supported interpreter install_r4lineups_python(envname = "r4lineups-face-env", python_version = "3.11") # Install with GPU support install_r4lineups_python(gpu = TRUE) ## End(Not run)## Not run: # Basic installation install_r4lineups_python() # Install to a virtual environment install_r4lineups_python(envname = "r4lineups-face-env") # Build the environment with a TensorFlow-supported interpreter install_r4lineups_python(envname = "r4lineups-face-env", python_version = "3.11") # Install with GPU support install_r4lineups_python(gpu = TRUE) ## End(Not run)
A dataframe of lineup choices for 1 trial; real data from a Doob & Kirshenbaum (1973) article. This article was the first to discuss lineup size 1 column x 42 observations
42 mock witnesses Nominal size is 6 Target position is 4
data(line73)data(line73)
An object of class data.frame with 42 rows and 1 columns.
Doob, A. N., & Kirshenbaum, H. M. (1973). Bias in police lineups-Partial remembering. Journal of Police Science and Administration, 1, 287-293.
Computes a bootstrap distribution for the target-position proportion.
lineup_bias_boot_dist( lineup_input, target_pos, k, input_type = c("vector", "table"), R = 1000 )lineup_bias_boot_dist( lineup_input, target_pos, k, input_type = c("vector", "table"), R = 1000 )
lineup_input |
A numeric lineup vector or a lineup table. |
target_pos |
Target position in the lineup (scalar). |
k |
Nominal lineup size (suspect + fillers). |
input_type |
Either "vector" or "table". |
R |
Number of bootstrap resamples. |
A numeric vector of bootstrap estimates, carrying an "n"
attribute equal to the number of lineup choices resampled. Because each
estimate is a count divided by n, the estimates fall on a discrete
lattice with spacing 1/n; plot_lineup_bias_distribution
uses the attribute to bin at that resolution.
vec <- round(runif(200, 1, 6)) dist <- lineup_bias_boot_dist(vec, target_pos = 3, k = 6, R = 500)vec <- round(runif(200, 1, 6)) dist <- lineup_bias_boot_dist(vec, target_pos = 3, k = 6, R = 500)
Computes bootstrapped confidence intervals for lineup proportion
lineup_boot_allprop(lineup_vec, k, conf = 0.95, R = 1000)lineup_boot_allprop(lineup_vec, k, conf = 0.95, R = 1000)
lineup_vec |
A numeric vector of lineup choices |
k |
Number of targets in lineup. Must be specified by user (scalar). |
conf |
Confidence level for the intervals, e.g. 0.95 for 95 percent intervals. Defaults to 0.95. May be specified by user (scalar). |
R |
Number of bootstrap replications. Defaults to 1000. |
Computes a bootstrapped lineup proportion for each member in turn by
calling the boot function in the boot package. Where a
BCa interval cannot be computed, the percentile interval is used
instead.
Note that the k intervals are each computed at the nominal
conf level, with no adjustment for the fact that
k of them are inspected together. Interpreting all of them at
once inflates the chance that at least one excludes the chance value
by more than the nominal amount. If a family-wise statement is wanted,
raise the level for each interval accordingly, e.g.
conf = 1 - 0.05 / k for a Bonferroni-style correction.
A dataframe of k rows and two columns, ci_low and
ci_high, holding the bias-corrected and accelerated (BCa)
confidence interval for the proportion choosing each lineup member.
Rows are named by lineup position.
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Wells, G. L., Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: lineuprops_ci <- lineup_boot_allprop(lineup_vec, k= 6) lineuprops_ci <- lineup_boot_allprop(lineup_vec, k= 6, conf = 0.975)#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: lineuprops_ci <- lineup_boot_allprop(lineup_vec, k= 6) lineuprops_ci <- lineup_boot_allprop(lineup_vec, k= 6, conf = 0.975)
A dataset containing simulated eyewitness lineup identification data with target-present and target-absent lineups, including confidence ratings.
lineup_examplelineup_example
A data frame with 1400 rows and 5 variables:
Logical indicating whether the guilty suspect was in the lineup
Integer position of the lineup member chosen, with 0 for a rejection
Integer position occupied by the suspect (1 throughout)
Character indicating the witness decision: "suspect", "filler", or "reject"
Numeric confidence rating (0-100 scale)
This dataset is used for demonstrating ROC, CAC, and EIG analyses in the r4lineups package. It contains 700 target-present and 700 target-absent lineups of size 6.
lineup_member records which member each witness chose, so foils
keep their separate identities and are not treated as interchangeable. They
are not merely simulated separately: each foil is given its own mean memory
strength, so some are strong alternatives to the suspect and others are
chosen only rarely. In this dataset the five foils attract roughly 248, 203,
115, 53 and 10 identifications respectively.
identification is derived from lineup_member and
suspect_pos, and can be recovered exactly with
classify_lineup_choices; it is retained because the analysis
functions in this package take it directly.
High confidence is strongly diagnostic here but deliberately not perfect: a small number of innocent suspects are identified at the highest confidence levels, as happens in real cases. Data in which no innocent suspect is ever identified with high confidence produces an infinite likelihood ratio and degenerate prior-posterior curves.
Simulated with simulate_lineup_data. The generating
script is data-raw/lineup_example.R, which carries the acceptance
criteria the dataset must satisfy as explicit checks.
classify_lineup_choices, lineup_table
data(lineup_example) str(lineup_example) table(lineup_example$identification, lineup_example$target_present) # Which lineup members were chosen, and were any never chosen? lineup_table(lineup_example$lineup_member[lineup_example$lineup_member > 0], k = 6, suspect_pos = 1)data(lineup_example) str(lineup_example) table(lineup_example$identification, lineup_example$target_present) # Which lineup members were chosen, and were any never chosen? lineup_table(lineup_example$lineup_member[lineup_example$lineup_member > 0], k = 6, suspect_pos = 1)
Computes a full pairwise similarity matrix between all faces in a lineup, including the target. Useful for understanding the overall structure of face similarities in the lineup.
lineup_pairwise_matrix( target_path, foil_paths, model = "ArcFace", metric = "cosine", detector = "retinaface" )lineup_pairwise_matrix( target_path, foil_paths, model = "ArcFace", metric = "cosine", detector = "retinaface" )
target_path |
Path to target face. |
foil_paths |
Paths to foil faces. |
model |
Face recognition model. |
metric |
Distance metric. |
detector |
Face detector. |
A square matrix of pairwise distances, with row/column names.
## Not run: mat <- lineup_pairwise_matrix("target.jpg", c("foil1.jpg", "foil2.jpg")) # Visualize with heatmap heatmap(mat) ## End(Not run)## Not run: mat <- lineup_pairwise_matrix("target.jpg", c("foil1.jpg", "foil2.jpg")) # Visualize with heatmap heatmap(mat) ## End(Not run)
Base function for computing bootstrapped lineup proportion for a lineup member
lineup_prop_boot(lineup_vec, d, target_pos)lineup_prop_boot(lineup_vec, d, target_pos)
lineup_vec |
A numeric vector of lineup choices |
d |
Indices for bootstrap sample. Argument used by boot function to select samples for bootstrapping |
target_pos |
A scalar, representing target position in lineup. Must be declared by user |
Function to call when bootstrap resampling using boot function
Davison, A.C. & Hinkley, D.V. (1997). Bootstrap methods and their application. Cambridge University Press.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
boot: https://cran.r-project.org/web/packages/boot/boot.pdf
lineup_vec <- round(runif(100, 1, 6)) bootobject <- boot::boot(lineup_vec, lineup_prop_boot, target_pos = 3, R = 1000) cis <- boot::boot.ci(bootobject, conf = 0.95, type = "all", target_pos = 3)lineup_vec <- round(runif(100, 1, 6)) bootobject <- boot::boot(lineup_vec, lineup_prop_boot, target_pos = 3, R = 1000) cis <- boot::boot.ci(bootobject, conf = 0.95, type = "all", target_pos = 3)
Computes the proportion of mock witnesses identifying a particular lineup member
lineup_prop_tab(lineup_tab, target_pos)lineup_prop_tab(lineup_tab, target_pos)
lineup_tab |
A table of lineup choices |
target_pos |
A scalar, representing target position in lineup. Must be declared by user |
Returns a proportion indicating the frequency with which a lineup member was selected
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100, 1)) lineup_tab <- table(lineup_vec) #Call: lineup_prop_tab(lineup_tab, 3) lineup_prop_tab(table(lineup_vec), 2)#Data: lineup_vec <- round(runif(100, 1)) lineup_tab <- table(lineup_vec) #Call: lineup_prop_tab(lineup_tab, 3) lineup_prop_tab(table(lineup_vec), 2)
Computes the proportion of mock witnesses identifying a particular lineup member
lineup_prop_vec(lineup_vec, target_pos, k)lineup_prop_vec(lineup_vec, target_pos, k)
lineup_vec |
A numeric vector of lineup choices |
target_pos |
Suspect/lineup member position. Must be declared by user (scalar). |
k |
Nominal size (i.e., total number of members in lineup). Must be specified by user (scalar). |
Returns a proportion indicating the frequency with which a target was identified in a lineup
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: lineup_prop_vec(lineup_vec, 3, 6)#Data: lineup_vec <- round(runif(100, 1, 6)) #Call: lineup_prop_vec(lineup_vec, 3, 6)
Computes similarity between a target face and all foils in a lineup, returning a comprehensive similarity analysis useful for lineup fairness assessment.
lineup_similarity( target_path, foil_paths, model = "ArcFace", metric = "cosine", detector = "retinaface", foil_names = NULL )lineup_similarity( target_path, foil_paths, model = "ArcFace", metric = "cosine", detector = "retinaface", foil_names = NULL )
target_path |
Path to the target (suspect) face image. |
foil_paths |
Character vector of paths to foil face images. |
model |
Character. Face recognition model. Default is "ArcFace". |
metric |
Character. Distance metric. Default is "cosine". |
detector |
Character. Face detection backend. Default is "retinaface". |
foil_names |
Optional character vector of names/labels for foils. If NULL, uses file names. |
This function describes how similar each foil is to the target in a face
recognition model's embedding space. It does not assess fairness: no
validated similarity range defines a fair lineup, and embedding similarity
is not the same as resemblance judged by people or as mock-witness choice.
Foils very dissimilar to the target may make the target stand out; foils
the model judges to be the same identity (verified = TRUE) deserve
inspection. Rows are ordered by rank (most similar first), and the model's
distance threshold is stored in attr(x, "threshold"). For cosine,
similarity = 1 - distance, which ranges from -1 to 1.
A tibble with columns:
foil_id - Numeric ID (1, 2, 3, ...)
foil_name - Name/label for the foil
foil_path - Path to foil image
distance - Distance from target (lower = more similar)
similarity - Cosine similarity score [-1,1] for cosine metric
verified - Would be identified as same person
rank - Rank by similarity (1 = most similar to target)
Also includes attributes:
target_path - Path to target image
model - Model used
metric - Metric used
threshold - Verification threshold
## Not run: # Analyze a 6-person lineup foils <- paste0("foil", 1:5, ".jpg") lineup <- lineup_similarity("target.jpg", foils) # View results sorted by similarity print(lineup) # Check which foils might be too similar lineup[lineup$verified, ] # Get summary statistics summary(lineup) ## End(Not run)## Not run: # Analyze a 6-person lineup foils <- paste0("foil", 1:5, ".jpg") lineup <- lineup_similarity("target.jpg", foils) # View results sorted by similarity print(lineup) # Check which foils might be too similar lineup[lineup$verified, ] # Get summary statistics summary(lineup) ## End(Not run)
Builds a table of lineup choices with one entry for every lineup member, including members that no mock witness chose.
lineup_table(lineup_vec, k, suspect_pos = NULL)lineup_table(lineup_vec, k, suspect_pos = NULL)
lineup_vec |
A numeric vector of lineup choices. |
k |
Number of members in the lineup (suspect plus foils). Must be specified by the user (scalar). |
suspect_pos |
Optional position of the nominated suspect, marked in the
printed output. Defaults to |
Calling table() directly on a vector of lineup choices tabulates only
the positions that were actually chosen. A lineup member that no witness
selected — a null foil — is silently dropped, so the resulting table has
fewer than k entries and nothing in the output reveals that a member is
missing. This is important because a foil nobody would pick is not a
functioning alternative to the suspect, one of several things that can make a
lineup unfair. A member with no choices in a small sample is not necessarily
such a foil.
lineup_table() tabulates over all k positions, so unchosen
members appear explicitly as zero counts, and its print method names them.
The measures in this package already handle null foils correctly when given a
complete table. esize_m(), for instance, reindexes counts by position
and deducts unchosen members from the adjusted estimate. What
lineup_table() adds is visibility, not a change in the arithmetic.
An object of class lineup_table, inheriting from table,
with k named entries. Because it inherits from table it can be
passed to esize_m, esize_T and the other
table-taking functions in this package unchanged.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
# A lineup in which position 4 was never chosen lineup_vec <- rep(c(5, 3, 2, 1, 8, 6, 7), each = 4) # table() drops the unchosen member: only 7 entries for an 8-person lineup table(lineup_vec) # lineup_table() keeps it, and says so lineup_table(lineup_vec, k = 8, suspect_pos = 3) # Accepted by the effective size functions unchanged esize_m(lineup_table(lineup_vec, k = 8), k = 8)# A lineup in which position 4 was never chosen lineup_vec <- rep(c(5, 3, 2, 1, 8, 6, 7), each = 4) # table() drops the unchosen member: only 7 entries for an 8-person lineup table(lineup_vec) # lineup_table() keeps it, and says so lineup_table(lineup_vec, k = 8, suspect_pos = 3) # Accepted by the effective size functions unchanged esize_m(lineup_table(lineup_vec, k = 8), k = 8)
Computes ln of diagnosticity ratio: ln(d)
ln_diag_ratio(linedf, correction = TRUE)ln_diag_ratio(linedf, correction = TRUE)
linedf |
A dataframe of parameters for computing diagnosticity ratio |
correction |
Logical. Apply Tredoux's small-cell correction (default
|
The correction adds 0.5 to each suspect-identification count and to its corresponding sample total before computing the ratio:
is then returned. The correction avoids undefined log ratios when a
cell count is zero and stabilises variance estimates in small samples.
To get linedf, use the diag_param helper function
diag_param returns a dataframe containing the following:
n11: Number of mock witnesses who identified the suspect in the target present condition
n21: Number of mock witnesses who did not identify the suspect in the target present condition
n12: Number of mock witnesses who identified the suspect in the target absent condition
n22: Number of mock witnesses who did not identify the suspect in the target absent condition
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Call: lnd <- ln_diag_ratio(linedf)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Call: lnd <- ln_diag_ratio(linedf)
Computes Bayesian updating curves showing how different lineup responses update beliefs about suspect guilt, following Wells, Yang, & Smalarz (2015).
make_bayes_curves( data, response_categories = c("simple", "confidence"), confidence_bins = NULL, prior_grid = seq(0.01, 0.99, 0.01), lineup_size = 6, innocent_suspect = "auto" )make_bayes_curves( data, response_categories = c("simple", "confidence"), confidence_bins = NULL, prior_grid = seq(0.01, 0.99, 0.01), lineup_size = 6, innocent_suspect = "auto" )
data |
A dataframe with the following columns:
|
response_categories |
Character vector specifying how to categorize responses. Options:
|
confidence_bins |
Numeric vector of bin edges if using confidence categories (e.g., c(0, 60, 80, 100)). Ignored if response_categories = "simple". |
prior_grid |
Numeric vector of prior probabilities to evaluate (default: seq(0.01, 0.99, 0.01)) |
lineup_size |
Nominal lineup size, used when target-absent data have no designated innocent suspect. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
This function treats lineup outcomes as evidence for Bayesian updating. For each response type x (e.g., high-confidence suspect ID, rejection, etc.), it computes:
Likelihoods from observed data:
For each prior probability p(guilty):
Information gain:
where H(p) is binary entropy.
The information gain quantifies how much uncertainty is reduced by observing response x. It is negative when the response moves belief towards 0.5, as a suspect identification does at a low prior; that is a correct update that leaves more uncertainty, not misleading evidence.
A list containing:
curves: Dataframe with prior, posterior, and information gain for each response
likelihoods: Dataframe with p(x|guilty) and p(x|innocent) for each response
response_counts: Count of each response type by target presence
n_guilty: Number of target-present lineups
n_innocent: Number of target-absent lineups
Wells, G. L., Yang, Y., & Smalarz, L. (2015). Eyewitness identification: Bayesian information gain, base-rate effect-equivalency curves, and reasonable suspicion. Law and Human Behavior, 39(2), 99-122.
data(lineup_example) bayes <- make_bayes_curves(lineup_example) bayes$likelihoodsdata(lineup_example) bayes <- make_bayes_curves(lineup_example) bayes$likelihoods
Computes BREE curves showing the base-rate shift required for one procedure to produce the same posterior probability as another procedure, following Wells, Yang, & Smalarz (2015).
make_bree_curve( data_proc_a, data_proc_b, reference_response = "suspect", confidence_bins = NULL, prior_grid = seq(0.01, 0.99, 0.01), lineup_size = 6, innocent_suspect = "auto" )make_bree_curve( data_proc_a, data_proc_b, reference_response = "suspect", confidence_bins = NULL, prior_grid = seq(0.01, 0.99, 0.01), lineup_size = 6, innocent_suspect = "auto" )
data_proc_a |
A dataframe for procedure A with columns: target_present, identification, confidence (standard lineup data format) |
data_proc_b |
A dataframe for procedure B (same format as proc_a) |
reference_response |
Character string specifying which response to compare (e.g., "suspect" for suspect IDs). Default = "suspect". |
confidence_bins |
Optional numeric vector of bin edges if using confidence-based response (e.g., c(0, 60, 80, 100)). If provided, reference_response should specify both identification and bin (e.g., "suspect_(80,100]") |
prior_grid |
Numeric vector of prior probabilities for procedure A (default: seq(0.01, 0.99, 0.01)) |
lineup_size |
Nominal lineup size for procedures without a designated innocent suspect. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
BREE curves answer the question: "How much would the base rate need to change for Procedure B to yield the same posterior probability as Procedure A?"
For each prior probability in procedure A: 1. Compute the posterior p(guilty|x) for the reference response in procedure A 2. Find the prior probability in procedure B that yields the same posterior 3. Delta = prior_B - prior_A
Interpretation:
Delta > 0: Procedure B requires a higher base rate to match A's posterior (A is more diagnostic)
Delta < 0: Procedure B requires a lower base rate (B is more diagnostic)
Delta = 0: Procedures are equally diagnostic
A list containing:
bree_curve: Dataframe with prior_a, posterior_a, prior_b, and delta
proc_a_likelihoods: Likelihoods for procedure A
proc_b_likelihoods: Likelihoods for procedure B
reference_response: The response category used for comparison
Wells, G. L., Yang, Y., & Smalarz, L. (2015). Eyewitness identification: Bayesian information gain, base-rate effect-equivalency curves, and reasonable suspicion. Law and Human Behavior, 39(2), 99-122.
data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) bree <- make_bree_curve(lineup_example[odd, ], lineup_example[-odd, ]) head(bree$bree_curve)data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) bree <- make_bree_curve(lineup_example[odd, ], lineup_example[-odd, ]) head(bree$bree_curve)
Main function to compute and plot CAC (Confidence-Accuracy Characteristic) for eyewitness lineup data.
make_cac( data, lineup_size = 6, confidence_bins = NULL, show_plot = TRUE, innocent_suspect = "auto", base_rate = NULL, ... )make_cac( data, lineup_size = 6, confidence_bins = NULL, show_plot = TRUE, innocent_suspect = "auto", base_rate = NULL, ... )
data |
A dataframe with columns: target_present, identification, confidence |
lineup_size |
Integer. Number of people in lineup (default = 6) |
confidence_bins |
Numeric vector of bin edges (optional) |
show_plot |
Logical. Whether to display the plot (default = TRUE) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
base_rate |
See |
... |
Additional arguments passed to make_cac_gg() |
A list containing CAC data and plot
data(lineup_example) cac_result <- make_cac(lineup_example, confidence_bins = c(0, 60, 80, 100)) print(cac_result)data(lineup_example) cac_result <- make_cac(lineup_example, confidence_bins = c(0, 60, 80, 100)) print(cac_result)
Creates a ggplot2 visualization of CAC data showing the relationship between confidence and accuracy.
make_cac_gg(cacobj_list, show_errorbars = TRUE, show_n = TRUE)make_cac_gg(cacobj_list, show_errorbars = TRUE, show_n = TRUE)
cacobj_list |
List output from make_cacdata() |
show_errorbars |
Logical. Whether to show error bars (default = TRUE) |
show_n |
Logical. Whether to show sample sizes (default = TRUE) |
A ggplot2 object
data(lineup_example) cac <- make_cacdata(lineup_example, confidence_bins = c(0, 60, 80, 100)) make_cac_gg(cac)data(lineup_example) cac <- make_cacdata(lineup_example, confidence_bins = c(0, 60, 80, 100)) make_cac_gg(cac)
Computes confidence-accuracy data for lineup identifications following Mickes (2015). CAC analysis shows the relationship between confidence and accuracy for suspect identifications.
make_cacdata( data, lineup_size = 6, confidence_bins = NULL, innocent_suspect = "auto", base_rate = NULL )make_cacdata( data, lineup_size = 6, confidence_bins = NULL, innocent_suspect = "auto", base_rate = NULL )
data |
A dataframe with the following columns:
|
lineup_size |
Integer. Number of people in lineup (default = 6) |
confidence_bins |
Numeric vector of bin edges for grouping confidence (e.g., c(0, 60, 80, 100) creates bins 0-60, 61-80, 81-100). If NULL, uses individual confidence levels. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
base_rate |
Prior probability that the suspect is guilty, used to
compute accuracy from rates rather than counts. |
CAC analysis computes, for each confidence level:
Explicit target-absent suspect IDs are used when present. Otherwise, target-absent filler IDs are divided by lineup size to estimate incorrect suspect IDs. The two estimators are never added together.
The count ratio equals the probability of guilt given a suspect ID only
when the target-present and target-absent samples are the same size, which
Mickes (2015) assumes; she notes that unequal base rates make the
calculation more complicated. With unequal samples the count-based
accuracy shifts with the TP:TA split even when hit and false-ID rates are
unchanged, and the function says so in a message. Setting base_rate
computes accuracy from the hit rate HR(c) = correct IDs / n(TP) and the
false-ID rate FAR(c) = incorrect IDs / n(TA) at the stated prior, so that
base_rate = 0.5 gives the balanced-design value whatever the sample
sizes. Its standard error is then a delta-method approximation from the two
independent binomial rates; the count-based standard error is binomial over
the bin's suspect IDs.
According to Mickes (2015), CAC analysis is most relevant for triers of fact (judges/jurors) evaluating estimator variables (e.g., exposure duration, retention interval). It shows how trustworthy an ID is at different confidence levels.
A list containing:
cac_data: Dataframe with confidence levels and accuracy
overall_accuracy: Overall accuracy across all suspect IDs
n_total_suspect_ids: Correct plus incorrect suspect IDs
base_rate: The base rate used, or NULL for count-based accuracy
n_target_present, n_target_absent: Sample sizes of the two arms
Mickes, L. (2015). Receiver operating characteristic analysis and confidence-accuracy characteristic analysis in investigations of system variables and estimator variables that affect eyewitness memory. Journal of Applied Research in Memory and Cognition, 4(2), 93-102.
Juslin, P., Olsson, N., & Winman, A. (1996). Calibration and diagnosticity of confidence in eyewitness identification: Comments on what can be inferred from the low confidence-accuracy correlation. Journal of Experimental Psychology: Learning, Memory, and Cognition, 22(5), 1304-1316.
data(lineup_example) cac <- make_cacdata(lineup_example, confidence_bins = c(0, 60, 80, 100)) cac$cac_datadata(lineup_example) cac <- make_cacdata(lineup_example, confidence_bins = c(0, 60, 80, 100)) cac$cac_data
Main function to compute and plot calibration analysis for eyewitness lineup data.
make_calibration( data, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, show_plot = TRUE, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto", ... )make_calibration( data, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, show_plot = TRUE, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto", ... )
data |
A dataframe with columns: target_present, identification, confidence |
confidence_bins |
Numeric vector of bin edges (optional) |
choosers_only |
Logical. Whether to analyze only suspect IDs (default = TRUE) |
lineup_size |
Integer. Number of people in lineup (default = 6) |
show_plot |
Logical. Whether to display the plot (default = TRUE) |
confidence_scale |
How the confidence scale is interpreted: "auto" (default),
"0-1", or "0-100". See |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
... |
Additional arguments passed to make_calibration_gg() |
A list containing calibration data, statistics, and plot
set.seed(123) n <- 200 lineup_data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) cal_result <- make_calibration(lineup_data, confidence_bins = c(0, 60, 80, 100)) print(cal_result)set.seed(123) n <- 200 lineup_data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) cal_result <- make_calibration(lineup_data, confidence_bins = c(0, 60, 80, 100)) print(cal_result)
Computes calibration analysis separately for different experimental conditions. Useful for examining how calibration varies across lineup instructions, foil similarity, or other system/estimator variables.
make_calibration_by_condition( data, condition_vars, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )make_calibration_by_condition( data, condition_vars, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )
data |
A dataframe with columns: target_present, identification, confidence, plus one or more condition variables |
condition_vars |
Character vector of column names defining conditions (e.g., c("instruction_type", "foil_similarity")) |
confidence_bins |
Numeric vector of bin edges (optional) |
choosers_only |
Logical. Whether to analyze only suspect IDs (default = TRUE) |
lineup_size |
Integer. Number of people in lineup (default = 6) |
confidence_scale |
How the confidence scale is interpreted: "auto" (default),
"0-1", or "0-100". See |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
This function splits the data by the specified condition variables and computes calibration statistics separately for each combination. Useful for examining questions like "Does calibration improve with biased vs unbiased instructions?" or "How does foil similarity affect over/underconfidence?"
A list containing:
by_condition: List of calibration results for each condition combination
condition_summary: Dataframe summarizing C, O/U, NRI across conditions
Brewer, N., & Wells, G. L. (2006). The confidence-accuracy relationship in eyewitness identification: Effects of lineup instructions, foil similarity, and target-absent base rates. Journal of Experimental Psychology: Applied, 12(1), 11-30.
set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), instruction = rep(c("biased", "unbiased"), times = n / 2) ) by_cond <- make_calibration_by_condition(data, condition_vars = "instruction", confidence_bins = c(0, 60, 80, 100)) by_cond$condition_summaryset.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), instruction = rep(c("biased", "unbiased"), times = n / 2) ) by_cond <- make_calibration_by_condition(data, condition_vars = "instruction", confidence_bins = c(0, 60, 80, 100)) by_cond$condition_summary
Creates a faceted or overlaid plot comparing calibration across conditions.
make_calibration_by_condition_gg( cal_by_cond_obj, facet = TRUE, show_stats = TRUE )make_calibration_by_condition_gg( cal_by_cond_obj, facet = TRUE, show_stats = TRUE )
cal_by_cond_obj |
List output from make_calibration_by_condition() |
facet |
Logical. If TRUE (default), creates faceted plot. If FALSE, overlays curves. |
show_stats |
Logical. Whether to display statistics (default = TRUE when faceted) |
A ggplot2 object
set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), instruction = rep(c("biased", "unbiased"), times = n / 2) ) by_cond <- make_calibration_by_condition(data, condition_vars = "instruction", confidence_bins = c(0, 60, 80, 100)) make_calibration_by_condition_gg(by_cond)set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), instruction = rep(c("biased", "unbiased"), times = n / 2) ) by_cond <- make_calibration_by_condition(data, condition_vars = "instruction", confidence_bins = c(0, 60, 80, 100)) make_calibration_by_condition_gg(by_cond)
Computes calibration analysis metrics following Juslin, Olsson, & Winman (1996). Calibration analysis assesses the match between confidence and accuracy, providing a more appropriate measure than simple confidence-accuracy correlation.
make_calibration_data( data, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )make_calibration_data( data, confidence_bins = NULL, choosers_only = TRUE, lineup_size = 6, confidence_scale = c("auto", "0-1", "0-100"), innocent_suspect = "auto" )
data |
A dataframe with the following columns:
|
confidence_bins |
Numeric vector of bin edges for grouping confidence (e.g., c(0, 60, 80, 100) creates bins 0-60, 60-80, 80-100). If NULL, uses individual confidence levels. |
choosers_only |
Logical. If TRUE (default), only analyze suspect identifications. If FALSE, analyze all responses (including fillers and rejections). |
lineup_size |
Integer. Number of people in lineup (default = 6). Used for estimating incorrect suspect IDs from filler choices in target-absent lineups. |
confidence_scale |
How the confidence scale is interpreted: "auto" (default; max <= 1 treated as 0-1, otherwise as 0-100, with a warning for ambiguous Likert/0-10 scales), "0-1", or "0-100". C and O/U require a 0-1/0-100 probability scale. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
Calibration analysis distinguishes between:
Calibration (C): How well confidence matches accuracy (perfect = 0)
Over/underconfidence (O/U): Overall bias in confidence judgments (0 = perfectly calibrated)
Resolution (NRI): Ability to discriminate correct from incorrect responses
The calibration statistic C measures the weighted mean squared deviation between mean confidence and accuracy in each bin:
The Normalized Resolution Index (NRI) captures how well confidence discriminates correct from incorrect responses:
When choosers_only = TRUE, only suspect identifications are included (standard
for eyewitness calibration analysis). When choosers_only = FALSE, all responses
are included with fillers and rejections counted as incorrect.
Consequently that option measures correct-suspect identification across all
responses, not general decision accuracy: a correct TA rejection still scores
zero. Do not interpret it as calibration of the correctness of every decision.
If target-absent data contain designated innocent-suspect identifications,
those observations are used directly. Otherwise, each target-absent filler
identification contributes weight 1 / lineup_size as an estimate of
innocent-suspect identifications. In that latter case the per-bin counts and
effective total used by C, O/U, NRI, and downstream ANRI can be fractional;
the results are lineup-size approximations rather than statistics from an
observed designated-suspect outcome.
A list containing:
calibration_data: Dataframe with per-bin confidence, accuracy, and counts
C: Calibration statistic (weighted mean squared difference between confidence and accuracy)
OU: Over/underconfidence (mean confidence minus mean accuracy)
NRI: Normalized Resolution Index (standardized within-person variance)
overall_accuracy: Mean accuracy across all responses
overall_confidence: Mean confidence across all responses
n_total: Total number of responses analyzed
Juslin, P., Olsson, N., & Winman, A. (1996). Calibration and diagnosticity of confidence in eyewitness identification: Comments on what can be inferred from the low confidence-accuracy correlation. Journal of Experimental Psychology: Learning, Memory, and Cognition, 22(5), 1304-1316.
Brewer, N., & Wells, G. L. (2006). The confidence-accuracy relationship in eyewitness identification: Effects of lineup instructions, foil similarity, and target-absent base rates. Journal of Experimental Psychology: Applied, 12(1), 11-30.
set.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) cal <- make_calibration_data(data, confidence_bins = c(0, 60, 80, 100)) cal$C cal$calibration_dataset.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) cal <- make_calibration_data(data, confidence_bins = c(0, 60, 80, 100)) cal$C cal$calibration_data
Creates a calibration curve plot showing the relationship between confidence and accuracy. A perfectly calibrated witness would fall on the diagonal line.
make_calibration_gg( cal_obj, show_stats = TRUE, show_n = TRUE, show_diagonal = TRUE )make_calibration_gg( cal_obj, show_stats = TRUE, show_n = TRUE, show_diagonal = TRUE )
cal_obj |
List output from make_calibration_data() |
show_stats |
Logical. Whether to display calibration statistics on plot (default = TRUE) |
show_n |
Logical. Whether to show sample sizes per bin (default = TRUE) |
show_diagonal |
Logical. Whether to show perfect calibration line (default = TRUE) |
The calibration curve plots mean confidence (x-axis) against accuracy (y-axis) for each confidence bin. Points on the diagonal indicate perfect calibration. Points above the diagonal indicate underconfidence, while points below indicate overconfidence.
A ggplot2 object
set.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) cal <- make_calibration_data(data, confidence_bins = c(0, 60, 80, 100)) make_calibration_gg(cal)set.seed(123) n <- 200 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)) ) cal <- make_calibration_data(data, confidence_bins = c(0, 60, 80, 100)) make_calibration_gg(cal)
Computes the average deviation from perfect performance of Smith, Lampinen, Wells, Smalarz and Mackovichova (2019). DPP is calculated at every operating point (cumulative confidence threshold) of the ROC and averaged over them, so that, unlike pAUC, it is not tied to a region of false-ID rates that two differently truncated ROC curves may not share.
make_dpp( data, lineup_size = 6, use_roc_obj = FALSE, alpha = NULL, beta = NULL, innocent_suspect = "auto", thresholds = NULL )make_dpp( data, lineup_size = 6, use_roc_obj = FALSE, alpha = NULL, beta = NULL, innocent_suspect = "auto", thresholds = NULL )
data |
A dataframe with the following columns:
Alternatively, with |
lineup_size |
Integer. Number of people in lineup (default = 6) |
use_roc_obj |
Logical. If FALSE (default), computes ROC from data.
If TRUE, data should be the output from make_rocdata(), whose first
|
alpha, beta
|
Optional weights for Smith et al.'s Equation 3.
|
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
thresholds |
Optional numeric vector of confidence criteria at which to evaluate the ROC. The default uses the confidence levels of the responses that can move an operating point; see Details. Supplying the criteria of a full sample keeps them fixed across bootstrap resamples. |
At an operating point , Smith et al.'s (2019) Equation 2 is
where is the culprit (correct) identification rate and
the innocent-suspect identification rate at that point.
DPP(c) ranges from 0 (perfect performance: every culprit identified, no
innocent suspect identified) to 2 (the reverse). Any point on the chance
diagonal has DPP(c) = 1. When the base rate of culprit presence is .5 and
the costs of the two errors are equal, DPP(c) = 1 - U(c), where
is the expected utility.
The reported dpp is the mean of DPP(c) over the operating points,
one for each confidence criterion of the ROC, from the highest to the
lowest (which includes every identification). Lower values are better. The
criteria are the confidence levels of the responses that enter the ROC
(see make_rocdata), so the confidence given to rejections
does not add points or change the average.
Equation 3 weights the two errors:
With alpha = 1 and beta = 0.5 this is half of Equation 2.
Weighted values no longer range from 0 to 2.
A list containing:
dpp: Average DPP over the operating points
dpp_by_point: Tibble with confidence threshold, culprit (correct-ID) rate, innocent-suspect ID rate and DPP at each operating point
best_point: The operating point with the lowest DPP, which is the point of maximum utility
roc_data: ROC curve data from make_rocdata()
alpha, beta: The weights used (NULL for Equation 2)
n_target_present, n_target_absent: Sample sizes
innocent_suspect_method: How innocent-suspect IDs were obtained
thresholds: The confidence criteria of the operating points
Smith, A. M., Lampinen, J. M., Wells, G. L., Smalarz, L., & Mackovichova, S. (2019). Deviation from perfect performance measures the diagnostic utility of eyewitness lineups but partial area under the ROC curve does not. Journal of Applied Research in Memory and Cognition, 8(1), 50-59. doi:10.1016/j.jarmac.2018.09.003
data(lineup_example) dpp <- make_dpp(lineup_example) dpp$dpp dpp$dpp_by_point # Innocent-suspect IDs twice as costly as misses, culprit base rate .5 make_dpp(lineup_example, alpha = 2, beta = 0.5)$dppdata(lineup_example) dpp <- make_dpp(lineup_example) dpp$dpp dpp$dpp_by_point # Innocent-suspect IDs twice as costly as misses, culprit base rate .5 make_dpp(lineup_example, alpha = 2, beta = 0.5)$dpp
Convenience wrapper that computes EIG and creates visualizations.
make_eig( data, prior_guilt = 0.5, confidence_bins = NULL, show_plot = TRUE, plot_type = "both", lineup_size = 6, innocent_suspect = "auto" )make_eig( data, prior_guilt = 0.5, confidence_bins = NULL, show_plot = TRUE, plot_type = "both", lineup_size = 6, innocent_suspect = "auto" )
data |
A dataframe with columns: target_present, identification, confidence |
prior_guilt |
Numeric. Prior probability that suspect is guilty (default = 0.5) |
confidence_bins |
Numeric vector of bin edges (optional) |
show_plot |
Logical. Whether to create plots (default = TRUE) |
plot_type |
Character. Which plot to create: "ig" (information gain), "posteriors" (posterior probabilities), or "both" (default) |
lineup_size |
Nominal lineup size used when no designated innocent suspect is present. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
A list of class "lineup_eig" containing:
eig: Expected Information Gain value
response_data: Full response category data
plot_ig: ggplot of information gain (if requested)
plot_posteriors: ggplot of posteriors (if requested)
... (additional components from compute_eig)
data(lineup_example) # With confidence binning result <- make_eig(lineup_example, confidence_bins = c(0, 60, 80, 100)) # Access plots result$plot_ig result$plot_posteriorsdata(lineup_example) # With confidence binning result <- make_eig(lineup_example, confidence_bins = c(0, 60, 80, 100)) # Access plots result$plot_ig result$plot_posteriors
Prepares response category data for Expected Information Gain (EIG) analysis following Starns et al. (2023). Creates response categories from identification decisions and confidence levels.
make_eig_data( data, confidence_bins = NULL, lineup_size = 6, innocent_suspect = "auto" )make_eig_data( data, confidence_bins = NULL, lineup_size = 6, innocent_suspect = "auto" )
data |
A dataframe with the following columns:
|
confidence_bins |
Numeric vector of bin edges for grouping confidence (e.g., c(0, 60, 80, 100) creates bins 0-60, 61-80, 81-100). If NULL, uses individual confidence levels. |
lineup_size |
Nominal lineup size. Used to split target-absent filler identifications when no designated innocent suspect is present. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
Response categories are defined as combinations of identification decision (suspect/filler/reject) and confidence level or bin. This function counts the frequency of each response category separately for target-present (guilty) and target-absent (innocent) lineups.
A list containing:
response_data: Dataframe with response categories and frequencies
n_guilty: Total number of target-present lineups
n_innocent: Total number of target-absent lineups
confidence_bins: The binning specification used (if any)
Starns, J. J., Cohen, A. L., & Rotello, C. M. (2023). A complete method for assessing the effectiveness of eyewitness identification procedures: Expected information gain. Psychological Review, 130(3), 677–719.
data(lineup_example) eig_data <- make_eig_data(lineup_example, confidence_bins = c(0, 60, 80, 100)) eig_data$response_datadata(lineup_example) eig_data <- make_eig_data(lineup_example, confidence_bins = c(0, 60, 80, 100)) eig_data$response_data
Main function to compute and plot a full ROC curve using all eyewitness responses (suspect ID, filler ID, rejection) following Smith & Yang (2020).
make_fullroc( data, conf_bins = NULL, order = c("diagnosticity", "apriori"), lineup_size = 6, show_plot = TRUE, epsilon = 0.001, innocent_suspect = "auto", ... )make_fullroc( data, conf_bins = NULL, order = c("diagnosticity", "apriori"), lineup_size = 6, show_plot = TRUE, epsilon = 0.001, innocent_suspect = "auto", ... )
data |
A dataframe with columns: target_present, identification, confidence |
conf_bins |
Numeric vector for confidence bin boundaries (default = NULL) |
order |
Character. "diagnosticity" or "apriori" (default = "diagnosticity") |
lineup_size |
Integer. Number of people in lineup (default = 6) |
show_plot |
Logical. Whether to display the plot (default = TRUE) |
epsilon |
Numeric. Small value for FAR = 0 cases (default = 0.001) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
... |
Additional arguments passed to plot_fullroc() |
This is the main user-facing function for computing full ROC curves. It calls make_fullroc_data() to compute the ROC, then plot_fullroc() to visualize it.
A list containing:
plot: ggplot2 object. Always returned, whatever show_plot is set to; show_plot controls only whether it is displayed immediately
roc_data: Dataframe with ROC curve points
auc: Full area under the curve
diagnosticity_table: Table with diagnosticity ratios
summary: Summary statistics
Smith, A. M., Yang, Y., & Wells, G. L. (2020). Distinguishing between investigator discriminability and eyewitness discriminability: A method for creating full receiver operating characteristic curves of lineup identification performance. Perspectives on Psychological Science, 15(3), 589-607.
data(lineup_example) # Compute and plot full ROC result <- make_fullroc(lineup_example) print(result$auc) # With custom confidence bins result2 <- make_fullroc(lineup_example, conf_bins = c(0, 60, 80, 100))data(lineup_example) # Compute and plot full ROC result <- make_fullroc(lineup_example) print(result$auc) # With custom confidence bins result2 <- make_fullroc(lineup_example, conf_bins = c(0, 60, 80, 100))
Computes full Receiver Operating Characteristic (ROC) data using ALL response categories (suspect ID, filler ID, rejection) following Smith & Yang (2020). This method distinguishes between investigator discriminability and eyewitness discriminability.
make_fullroc_data( data, conf_bins = NULL, order = c("diagnosticity", "apriori"), lineup_size = 6, epsilon = 0.001, innocent_suspect = "auto" )make_fullroc_data( data, conf_bins = NULL, order = c("diagnosticity", "apriori"), lineup_size = 6, epsilon = 0.001, innocent_suspect = "auto" )
data |
A dataframe with the following columns:
|
conf_bins |
Numeric vector specifying confidence bin boundaries (default = NULL, which uses unique confidence values). Can specify bins like c(0, 60, 80, 100) to create low (0-60), medium (60-80), and high (80-100) confidence categories. |
order |
Character. Method for ordering response categories:
|
lineup_size |
Integer. Number of people in the lineup (default = 6) |
epsilon |
Numeric. Small value to add when FAR = 0 to avoid infinite DR (default = 0.001) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
This function implements the "full ROC" method described in Smith & Yang (2020). Unlike traditional partial ROC curves that only use suspect identifications, this method uses ALL eyewitness responses:
Suspect identifications (evidence of guilt)
Filler identifications (evidence of innocence)
Lineup rejections (evidence of innocence)
Each response type is crossed with confidence levels to create decision criteria. These are ordered by their diagnosticity ratio (HR/FAR) or by a-priori ordering, then cumulative hit and false alarm rates are computed to form the full ROC curve.
The full ROC provides a threshold-free measure of investigator discriminability— the ability to distinguish guilty from innocent suspects using ALL available eyewitness evidence.
Note on ordering and bias: ordering cells by the sample
diagnosticity ratio (order = "diagnosticity") bows the curve upward
from noise alone, so the resulting AUC is optimistically biased — identical
target-present and target-absent response distributions yield AUC slightly
above 0.5, with the bias growing with the number of decision-by-confidence
cells and shrinking with sample size. For inference or comparisons across
conditions, prefer order = "apriori" (the theoretically fixed
ordering suggested by Smith & Yang, 2020) or compare against a permutation
baseline.
A list containing:
roc_data: Dataframe with cumulative hit rates and false alarm rates
auc: Full area under the curve
order_used: The ordering method used
diagnosticity_table: Table showing non-cumulative rates and DR for each category
n_target_present: Number of target-present lineups
n_target_absent: Number of target-absent lineups
Smith, A. M., Yang, Y., & Wells, G. L. (2020). Distinguishing between investigator discriminability and eyewitness discriminability: A method for creating full receiver operating characteristic curves of lineup identification performance. Perspectives on Psychological Science, 15(3), 589-607.
data(lineup_example) # Compute full ROC with diagnosticity ordering fullroc_result <- make_fullroc_data(lineup_example) print(fullroc_result$auc) # Compute full ROC with specified confidence bins fullroc_binned <- make_fullroc_data(lineup_example, conf_bins = c(0, 60, 80, 100))data(lineup_example) # Compute full ROC with diagnosticity ordering fullroc_result <- make_fullroc_data(lineup_example) print(fullroc_result$auc) # Compute full ROC with specified confidence bins fullroc_binned <- make_fullroc_data(lineup_example, conf_bins = c(0, 60, 80, 100))
Computes the multiple empirical discriminability (d') indices of Lee and Penrod's (2019) Multi-d' model from target-present (TP) and target-absent (TA) lineup data. Unlike the conventional 2 x 2 signal detection treatment, which folds filler identifications into "negative" responses, the Multi-d' model uses the full 2 x 3 response table (suspect ID, filler ID, rejection) in each lineup type.
make_multid( data = NULL, counts = NULL, lineup_size = 6, filler_rate = c("total", "per_filler"), correction = c("loglinear", "half", "none"), by_confidence = FALSE, conf_thresholds = NULL, n_bootstrap = 0, conf_level = 0.95, seed = NULL, innocent_suspect = "auto" )make_multid( data = NULL, counts = NULL, lineup_size = 6, filler_rate = c("total", "per_filler"), correction = c("loglinear", "half", "none"), by_confidence = FALSE, conf_thresholds = NULL, n_bootstrap = 0, conf_level = 0.95, seed = NULL, innocent_suspect = "auto" )
data |
A data frame with columns |
counts |
A named numeric vector of response frequencies with elements
|
lineup_size |
Nominal lineup size (suspect plus fillers). Default 6. |
filler_rate |
Either |
correction |
Correction for rates of 0 or 1: |
by_confidence |
Logical. If |
conf_thresholds |
Optional numeric vector of confidence thresholds for
|
n_bootstrap |
Number of bootstrap resamples for percentile confidence intervals (default 0, no bootstrap). Resampling is done within the TP and TA arms separately. |
conf_level |
Confidence level for bootstrap intervals. Default 0.95. |
seed |
Optional random seed for the bootstrap. |
innocent_suspect |
For trial-level |
With each d' computed as the difference between two z-transformed (inverse-normal) response rates, the model defines:
: guilty versus innocent suspect, the usual
empirical discriminability;
: guilty suspect versus TP fillers;
: innocent suspect versus TA fillers,
which Lee and Penrod treat as TA lineup bias;
: the greater appeal of TA than TP
fillers (differential filler siphoning);
: discrimination of culprit presence
from culprit absence (detection).
Lee and Penrod also propose as an index of TP
lineup bias net of memory strength, and as an index
of differential filler siphoning. The decomposition
holds exactly for any data, because the filler terms cancel. It is an accounting identity that shows where d'(GI) comes from, not an empirical result.
When TA lineups have no designated innocent suspect, the innocent-suspect
count is estimated as the TA filler count divided by lineup_size, and
the remaining of filler choices are kept as TA filler IDs.
Lee and Penrod call this the "average-filler" practice. It forces the TA
lineup to be fair, so d'(IFa) then reflects the rejection rate rather than
lineup bias.
With filler_rate = "total", a perfectly fair TA lineup does not give
: the innocent suspect is compared with all fillers
together. The returned fair_reference is the d'(IFa) a perfectly
fair lineup would give at the observed TA rejection rate (for example,
about -1.93 when no one rejects a 6-person lineup), and ifa_vs_fair
is the observed d'(IFa) minus that reference. These two quantities are not
part of Lee and Penrod's model; they are provided to help interpret d'(IFa).
With filler_rate = "per_filler", the fair reference is 0, and the
average-filler practice gives d'(IFa) = 0 exactly when correction =
"none" (a rate correction shifts the two rates slightly differently).
The criterion c is and dr is the
diagnosticity ratio G/I, both as reported by Lee and Penrod (2019).
An object of class "multid", a list with:
Data frame of indices with estimates and, if bootstrapped, standard errors and percentile confidence limits.
Named vector of the six response rates used (after correction and, if requested, per-filler scaling).
Named vector of the six response counts (after any average-filler split).
Number of TP and TA lineups.
"designated" or "estimated_from_fillers".
Data frame of cumulative indices by confidence
threshold, or NULL.
Matrix of bootstrap draws, or NULL.
plus the settings used.
Lee, J., & Penrod, S. D. (2019). New signal detection theory-based framework for eyewitness performance in lineups. Law and Human Behavior. doi:10.1037/lhb0000343
Lee, J., Mansour, J. K., & Penrod, S. D. (2022). Validity of mock-witness measures for assessing lineup fairness. Psychology, Crime & Law. doi:10.1080/1068316X.2021.1905811
Lee, J., Mansour, J. K., & Penrod, S. D. (2024). How to measure lineup fairness: Concurrent and predictive validity of lineup-fairness measures. Psychology, Crime & Law. doi:10.1080/1068316X.2024.2307358
compare_multid, plot.multid,
sdt_summary_from_counts
# Lineup column of Lee & Penrod's (2019) Table 4 (Steblay et al., 2003) md <- make_multid(counts = c(G = 45, Fp = 24, Rp = 31, I = 17, Fa = 26, Ra = 57), correction = "none") md # From trial-level data, with bootstrap CIs and confidence breakdown data(lineup_example) md2 <- make_multid(lineup_example, by_confidence = TRUE, n_bootstrap = 200, seed = 1) summary(md2) plot(md2)# Lineup column of Lee & Penrod's (2019) Table 4 (Steblay et al., 2003) md <- make_multid(counts = c(G = 45, Fp = 24, Rp = 31, I = 17, Fa = 26, Ra = 57), correction = "none") md # From trial-level data, with bootstrap CIs and confidence breakdown data(lineup_example) md2 <- make_multid(lineup_example, by_confidence = TRUE, n_bootstrap = 200, seed = 1) summary(md2) plot(md2)
Convenience wrapper that computes PPV range and creates visualizations.
make_ppv_range( data, lineup_size = 6, confidence_bins = NULL, show_plots = TRUE, plot_type = "all", innocent_suspect = "auto", base_rate = NULL )make_ppv_range( data, lineup_size = 6, confidence_bins = NULL, show_plots = TRUE, plot_type = "all", innocent_suspect = "auto", base_rate = NULL )
data |
A dataframe with columns: target_present, identification, confidence |
lineup_size |
Integer. Nominal number of people in lineup (default = 6) |
confidence_bins |
Numeric vector of bin edges (optional) |
show_plots |
Logical. Whether to create plots (default = TRUE) |
plot_type |
Character. Which plots to create: "ppv" (PPV range only), "effective" (effective size only), "all" (all plots, default) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
base_rate |
Prior probability that the suspect is guilty.
|
A list of class "lineup_ppv_range" containing:
ppv_range_data: Dataframe with all PPV estimates
plot_ppv_range: ggplot of PPV range (if requested)
plot_effective_size: ggplot of effective size (if requested)
plot_error_rate: ggplot of error rates (if requested)
... (additional components from ppv_range_by_confidence)
data(lineup_example) # With confidence binning result <- make_ppv_range(lineup_example, confidence_bins = c(0, 60, 80, 100)) # Access plots result$plot_ppv_range result$plot_effective_sizedata(lineup_example) # With confidence binning result <- make_ppv_range(lineup_example, confidence_bins = c(0, 60, 80, 100)) # Access plots result$plot_ppv_range result$plot_effective_size
Main function to compute and plot RAC (Response Time-Accuracy Characteristic) for eyewitness lineup data.
make_rac( data, lineup_size = 6, time_bins = NULL, show_plot = TRUE, innocent_suspect = "auto", ... )make_rac( data, lineup_size = 6, time_bins = NULL, show_plot = TRUE, innocent_suspect = "auto", ... )
data |
A dataframe with columns: target_present, identification, response_time |
lineup_size |
Integer. Number of people in lineup (default = 6) |
time_bins |
Numeric vector of bin edges (recommended for continuous RT data) |
show_plot |
Logical. Whether to display the plot (default = TRUE) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
... |
Additional arguments passed to make_rac_gg() |
A list containing RAC data and plot
# Example with binned response times (in milliseconds) lineup_data <- create_example_lineup_data(n_trials = 200, include_response_time = TRUE, seed = 123) rac_result <- make_rac(lineup_data, time_bins = c(0, 5000, 10000, 15000, 20000)) print(rac_result) rac_result$plot# Example with binned response times (in milliseconds) lineup_data <- create_example_lineup_data(n_trials = 200, include_response_time = TRUE, seed = 123) rac_result <- make_rac(lineup_data, time_bins = c(0, 5000, 10000, 15000, 20000)) print(rac_result) rac_result$plot
Creates a ggplot2 visualization of RAC data showing the relationship between response time and accuracy.
make_rac_gg( racobj_list, show_errorbars = TRUE, show_n = TRUE, time_units = "ms" )make_rac_gg( racobj_list, show_errorbars = TRUE, show_n = TRUE, time_units = "ms" )
racobj_list |
List output from make_racdata() |
show_errorbars |
Logical. Whether to show error bars (default = TRUE) |
show_n |
Logical. Whether to show sample sizes (default = TRUE) |
time_units |
Character. Label for time units (e.g., "ms", "seconds"). Default = "ms" |
A ggplot2 object
data <- create_example_lineup_data(n_trials = 200, include_response_time = TRUE, seed = 123) rac <- make_racdata(data, time_bins = c(0, 4000, 8000, 12000, 20000)) make_rac_gg(rac)data <- create_example_lineup_data(n_trials = 200, include_response_time = TRUE, seed = 123) rac <- make_racdata(data, time_bins = c(0, 4000, 8000, 12000, 20000)) make_rac_gg(rac)
Computes response time-accuracy data for lineup identifications following the approach described in Seale-Carlisle et al. (2019) and implemented in pyWitness (Mickes et al., 2024). RAC analysis shows the relationship between response time and accuracy for suspect identifications.
make_racdata( data, lineup_size = 6, time_bins = NULL, innocent_suspect = "auto" )make_racdata( data, lineup_size = 6, time_bins = NULL, innocent_suspect = "auto" )
data |
A dataframe with the following columns:
|
lineup_size |
Integer. Number of people in lineup (default = 6) |
time_bins |
Numeric vector of bin edges for grouping response times (e.g., c(0, 5000, 10000, 15000, 20000) creates bins in milliseconds). If NULL, uses individual response time values (not recommended for continuous data). |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
RAC analysis computes, for each response time bin:
Explicit target-absent suspect IDs are used when present. Otherwise,
target-absent filler IDs are divided by lineup size to estimate incorrect
suspect IDs. The two estimators are never added together.
The bin mean time uses the same suspect-ID observations as accuracy;
estimated-mode TA filler times receive weight . Bin edges must
cover all supplied observations. Count accuracy depends on TP/TA sampling
fractions and does not adjust to a different population base rate. The
displayed binomial SE is approximate, especially for estimated counts.
RAC analysis is useful for examining the speed-accuracy tradeoff in eyewitness identifications. Faster responses typically indicate stronger memory, and RAC curves often show higher accuracy for faster response times.
A list containing:
rac_data: Dataframe with response time bins and accuracy
n_ids_per_bin: Number of suspect IDs in each time bin
overall_accuracy: Overall accuracy across all suspect IDs
Seale-Carlisle, T. M., Colloff, M. F., Flowe, H. D., Wells, W., Wixted, J. T., & Mickes, L. (2019). Confidence and response time as indicators of eyewitness identification accuracy in the lab and in the real world. Journal of Applied Research in Memory and Cognition, 8(4), 420-428.
Mickes, L., Seale-Carlisle, T. M., Chen, X., & Boogert, S. (2024). pyWitness 1.0: A python eyewitness identification analysis toolkit. Behavior Research Methods, 56, 1533-1550.
data <- create_example_lineup_data(n_trials = 200, include_response_time = TRUE, seed = 123) rac <- make_racdata(data, time_bins = c(0, 4000, 8000, 12000, 20000)) rac$rac_datadata <- create_example_lineup_data(n_trials = 200, include_response_time = TRUE, seed = 123) rac <- make_racdata(data, time_bins = c(0, 4000, 8000, 12000, 20000)) rac$rac_data
Main function to compute and plot an ROC curve for eyewitness lineup data. This follows the methodology of Wixted & Mickes (2012) and Mickes (2015).
make_roc( data, lineup_size = 6, show_plot = TRUE, innocent_suspect = "auto", ... )make_roc( data, lineup_size = 6, show_plot = TRUE, innocent_suspect = "auto", ... )
data |
A dataframe with the following columns:
|
lineup_size |
Integer. Number of people in lineup (default = 6) |
show_plot |
Logical. Whether to display the plot (default = TRUE) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
... |
Additional arguments passed to make_roc_gg() |
ROC analysis measures discriminability - the ability to distinguish innocent from guilty suspects. According to Mickes (2015), ROC analysis is most relevant for policymakers deciding on system variables (e.g., simultaneous vs. sequential lineups, lineup size, etc.).
For estimator variables (e.g., exposure duration, retention interval), use
CAC analysis instead (see make_cac).
A list containing:
plot: ggplot2 object. Always returned, whatever show_plot is set to; show_plot controls only whether it is displayed immediately
roc_data: Dataframe with ROC curve points
pauc: Partial area under the curve
summary: Summary statistics
Wixted, J. T., & Mickes, L. (2012). The field of eyewitness memory should abandon probative value and embrace receiver operating characteristic analysis. Perspectives on Psychological Science, 7(3), 275-278.
Mickes, L. (2015). Receiver operating characteristic analysis and confidence-accuracy characteristic analysis in investigations of system variables and estimator variables that affect eyewitness memory. Journal of Applied Research in Memory and Cognition, 4(2), 93-102.
data(lineup_example) roc_result <- make_roc(lineup_example) print(roc_result$pauc)data(lineup_example) roc_result <- make_roc(lineup_example) print(roc_result$pauc)
Creates a ggplot2 visualization of ROC data from lineup identification experiments.
make_roc_gg( rocobj_list, show_pauc = TRUE, point_labels = TRUE, title = "Partial ROC Curve (suspect IDs only)", data_source = NULL )make_roc_gg( rocobj_list, show_pauc = TRUE, point_labels = TRUE, title = "Partial ROC Curve (suspect IDs only)", data_source = NULL )
rocobj_list |
List output from make_rocdata(), or the lineup_roc object from make_roc() |
show_pauc |
Logical. Whether to display pAUC value on plot (default = TRUE) |
point_labels |
Logical. Whether to label points with confidence levels (default = TRUE) |
title |
Character. Plot title. The default names the ROC variant, so that
a partial ROC is not mistaken for a full ROC (see |
data_source |
Character. Name of the dataset the curve was computed from,
appended to the caption. Defaults to the |
A ggplot2 object. Being an ordinary ggplot, it can be modified with
the usual + syntax, e.g. p + ggplot2::theme_minimal().
data(lineup_example) roc <- make_rocdata(lineup_example) make_roc_gg(roc)data(lineup_example) roc <- make_rocdata(lineup_example) make_roc_gg(roc)
Computes Receiver Operating Characteristic (ROC) data from lineup identification experiments following the methodology of Mickes, Wixted, and Gronlund.
make_rocdata( data, lineup_size = 6, innocent_suspect = "auto", thresholds = NULL )make_rocdata( data, lineup_size = 6, innocent_suspect = "auto", thresholds = NULL )
data |
A dataframe with the following columns:
|
lineup_size |
Integer. Number of people in the lineup (default = 6). Used to estimate false ID rate when fillers are chosen from target-absent lineups. |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
thresholds |
Optional numeric vector of confidence criteria at which to evaluate the ROC. The default uses the confidence levels of the responses that can move an operating point; see Details. Supplying the criteria of a full sample keeps them fixed across bootstrap resamples. |
This function computes ROC curves following Wixted & Mickes (2012) and Mickes (2015). For each confidence criterion:
Correct ID rate = proportion of target-present lineups where suspect identified
False ID rate = proportion of target-absent lineups where suspect identified
The criteria are the confidence levels of the responses that enter the ROC: target-present suspect IDs, and target-absent suspect IDs (designated suspect) or filler IDs (estimated). The confidence given to rejections, or to filler IDs in target-present lineups, does not define a criterion, because it cannot move an operating point. Each criterion therefore gives a distinct point, and averages over points (DPP, z-ROC d') do not depend on how rejection confidence was coded.
With a designated innocent suspect, target-absent "suspect"
responses are used directly. Without one, filler IDs are divided by lineup
size. The two estimators are never added together. See
innocent_suspect for how the design is determined.
A list containing:
roc_data: Dataframe with correct_id_rate, false_id_rate, and confidence
pauc: Partial area under the curve
n_target_present: Number of target-present lineups
n_target_absent: Number of target-absent lineups
thresholds: The confidence criteria used, highest first
Wixted, J. T., & Mickes, L. (2012). The field of eyewitness memory should abandon probative value and embrace receiver operating characteristic analysis. Perspectives on Psychological Science, 7(3), 275-278.
Mickes, L. (2015). Receiver operating characteristic analysis and confidence-accuracy characteristic analysis in investigations of system variables and estimator variables that affect eyewitness memory. Journal of Applied Research in Memory and Cognition, 4(2), 93-102.
Gronlund, S. D., Wixted, J. T., & Mickes, L. (2014). Evaluating eyewitness identification procedures using receiver operating characteristic analysis. Current Directions in Psychological Science, 23(1), 3-10.
data(lineup_example) roc <- make_rocdata(lineup_example) roc$roc_data roc$paucdata(lineup_example) roc <- make_rocdata(lineup_example) roc$roc_data roc$pauc
Computes expected utility at different confidence criteria following Lampinen, Smith, & Wells (2019). Utility analysis accounts for the costs and benefits of different identification outcomes.
make_utility_curves( data, base_rate = 0.5, utility_matrix = c(tp = 1, fn = -0.5, fp = -2, tn = 0.5), lineup_size = 6, criteria = c("confidence", "all"), innocent_suspect = "auto", thresholds = NULL )make_utility_curves( data, base_rate = 0.5, utility_matrix = c(tp = 1, fn = -0.5, fp = -2, tn = 0.5), lineup_size = 6, criteria = c("confidence", "all"), innocent_suspect = "auto", thresholds = NULL )
data |
A dataframe with the following columns:
|
base_rate |
Numeric. Prior probability that suspect is guilty (0 to 1). Default = 0.5. |
utility_matrix |
Named numeric vector with utilities/costs for:
Default: c(tp=1, fn=-0.5, fp=-2, tn=0.5) |
lineup_size |
Integer. Number of people in lineup (default = 6). Used to estimate false IDs from filler choices. |
criteria |
Character. How to define decision criteria:
Default = "confidence". |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
thresholds |
Optional numeric vector of confidence criteria. The
default uses the confidence levels of the responses that can change the
hit or false-alarm rate, as in |
Expected utility combines hit rates and false alarm rates with the costs/benefits of different outcomes:
Where:
hit = p(suspect ID | guilty) at criterion
fa = p(suspect ID | innocent) at criterion
U_TP, U_FN, U_FP, U_TN = utilities for each outcome
The utility matrix should reflect the relative value/cost of outcomes. For example:
tp = 1: Correctly identifying the perpetrator
fn = -0.5: Missing the perpetrator (they remain at large)
fp = -2: Wrongly convicting an innocent person (severe injustice)
tn = 0.5: Correctly rejecting when innocent
Lampinen et al. (2019) show that comparing procedures by ROC curves alone can be misleading. Utility analysis incorporates base rates and the relative costs of errors, providing a more complete evaluation.
Note on the false-alarm estimate: explicit target-absent suspect IDs are used when present. Otherwise, filler IDs are divided by lineup size. Direct and filler-derived estimates are never added together.
A list containing:
utility_data: Dataframe with criterion, strategy, hit rate,
false alarm rate, and expected utility. Each row is the rule "accept
suspect IDs with confidence >= criterion". The "reject_all"
row has a criterion above the observed identification confidences
and supplied grid (possibly Inf), so that
applying it accepts no identification.
max_utility: Maximum expected utility, with the corresponding criterion and strategy
avg_utility: Average expected utility across all criteria
utility_all_ids: Expected utility if all IDs are accepted
base_rate: Base rate used
utility_matrix: Utility matrix used
Lampinen, J. M., Smith, A. M., & Wells, G. L. (2019). Four utilities in eyewitness identification practice: Dissociations between receiver operating characteristic analysis and expected utility analysis. Law and Human Behavior, 43(1), 26-44.
data(lineup_example) util <- make_utility_curves(lineup_example) util$max_utilitydata(lineup_example) util <- make_utility_curves(lineup_example) util$max_utility
Computes the difference in expected utility between two lineup procedures across a range of base rates and/or utility matrices, following Lampinen, Smith, & Wells (2019).
make_utility_difference( data_proc_a, data_proc_b, base_rate_grid = seq(0.01, 0.99, 0.01), utility_matrix = c(tp = 1, fn = -0.5, fp = -2, tn = 0.5), utility_type = c("max", "avg", "all"), lineup_size = 6, innocent_suspect = "auto" )make_utility_difference( data_proc_a, data_proc_b, base_rate_grid = seq(0.01, 0.99, 0.01), utility_matrix = c(tp = 1, fn = -0.5, fp = -2, tn = 0.5), utility_type = c("max", "avg", "all"), lineup_size = 6, innocent_suspect = "auto" )
data_proc_a |
A dataframe for procedure A (standard lineup format) |
data_proc_b |
A dataframe for procedure B (standard lineup format) |
base_rate_grid |
Numeric vector of base rates to evaluate. Default: seq(0.01, 0.99, 0.01) |
utility_matrix |
Named numeric vector with utilities (tp, fn, fp, tn). Default: c(tp=1, fn=-0.5, fp=-2, tn=0.5) |
utility_type |
Character. Which utility to compare:
Default = "max". |
lineup_size |
Integer. Lineup size (default = 6) |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
Utility difference curves show how the relative value of two procedures changes with the base rate (prevalence) of guilty suspects. This addresses the question: "Which procedure is better, and does it depend on the base rate?"
Interpretation:
Positive difference: Procedure A has higher utility (preferred)
Negative difference: Procedure B has higher utility (preferred)
Zero difference: Procedures equivalent at that base rate
Lampinen et al. (2019) show that ROC dominance does not guarantee utility dominance. A procedure with a lower pAUC might have higher utility at certain base rates and cost structures.
A list containing:
difference_curve: Dataframe with base_rate, eu_a, eu_b, and difference
utility_type: Type of utility compared
utility_matrix: Utility matrix used
crossover_points: Base rates where procedures have equal utility (if any)
Lampinen, J. M., Smith, A. M., & Wells, G. L. (2019). Four utilities in eyewitness identification practice: Dissociations between receiver operating characteristic analysis and expected utility analysis. Law and Human Behavior, 43(1), 26-44.
data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) util_diff <- make_utility_difference(lineup_example[odd, ], lineup_example[-odd, ], base_rate_grid = seq(0.1, 0.9, 0.1)) util_diff$difference_curvedata(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) util_diff <- make_utility_difference(lineup_example[odd, ], lineup_example[-odd, ], base_rate_grid = seq(0.1, 0.9, 0.1)) util_diff$difference_curve
Several helper functions that compute bootstrap confidence limits from proportions.
makevec_prop(prop, n)makevec_prop(prop, n)
prop |
A single finite proportion between 0 and 1. |
n |
A positive whole-number sample size. |
# Expand a choice proportion of 0.25 among 20 mock witnesses into a # binary choice vector makevec_prop(0.25, 20)# Expand a choice proportion of 0.25 among 20 mock witnesses into a # binary choice vector makevec_prop(0.25, 20)
Numeric moments of the max filler distribution using integration.
max_filler_moments( lineup_size, moments = c("mean", "var", "skewness"), rel.tol = 1e-08, subdivisions = 2000 )max_filler_moments( lineup_size, moments = c("mean", "var", "skewness"), rel.tol = 1e-08, subdivisions = 2000 )
lineup_size |
Lineup size (suspect + fillers), must be >= 2. |
moments |
Character vector specifying moments to return. Supported: "mean", "var", "skewness". |
rel.tol |
Relative tolerance passed to integrate(). |
subdivisions |
Maximum subdivisions for integrate(). |
Named list of moments.
max_filler_moments(6)max_filler_moments(6)
Data capturing confidence and accuracy for a lineup trial
data(mickwick)data(mickwick)
An object of class tbl_df (inherits from tbl, data.frame) with 100 rows and 2 columns.
mickwick
A dataframe containing real lineup data.
3 columns x 94 observations.
The first column simply provides the participant number, whilst the second and third contain data for each participant's choice (i.e., the position of the lineup member that they identified) and a confidence score for each choice.
In the field of eyewitness testimony, it is common for participants to rate how confident they are that they have accurately identified the perpetrator. Here, confidence is recorded as a percentage.
This lineup has the following parameters:
94 mock witness choices
Target position is 7
Nominal size is 9
data(mockdata)data(mockdata)
An object of class tbl_df (inherits from tbl, data.frame) with 94 rows and 3 columns.
A data file containing lineup data for 3 independent lineups. 133 rows x 3 columns. Each lineup trial consisted of 133 mock witnesses & 8 lineup members.
: Target position for lineup 1 is 3
: Target position for lineup 2 is 6
: Target position for lineup 3 is 2
data(nortje2012)data(nortje2012)
An object of class tbl_df (inherits from tbl, data.frame) with 133 rows and 3 columns.
Creates a comparison plot showing observed counts vs. model-predicted expected counts for both target-present and target-absent lineups.
plot_2ht_fit(x, ...)plot_2ht_fit(x, ...)
x |
A winter_2ht object from fit_winter_2ht() |
... |
Additional arguments (not used) |
A ggplot object
counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) plot_2ht_fit(fit)counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) plot_2ht_fit(fit)
Creates a visual display of the parameter estimates from a fitted 2-HT model, with error bars showing the supplied bootstrap intervals, or regular 95% normal intervals where available.
plot_2ht_parameters(x, ..., which = c("dP", "dA", "b", "g"), show_ci = TRUE)plot_2ht_parameters(x, ..., which = c("dP", "dA", "b", "g"), show_ci = TRUE)
x |
A |
... |
Additional arguments passed to plotting functions |
which |
Character vector specifying which parameters to plot. Default is c("dP", "dA", "b", "g") (all parameters). |
show_ci |
Logical indicating whether to show 95% confidence intervals. Default is TRUE. |
A ggplot object
counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) plot_2ht_parameters(fit)counts <- c( n_tp_suspect = 147, n_tp_filler = 94, n_tp_reject = 141, n_ta_suspect = 38, n_ta_filler = 138, n_ta_reject = 206 ) fit <- fit_winter_2ht(counts, lineup_size = 6) plot_2ht_parameters(fit)
Creates a visualization comparing ANRI estimates between two groups with bootstrap confidence intervals.
plot_anri_comparison(compare_obj)plot_anri_comparison(compare_obj)
compare_obj |
List output from compare_anri() |
Creates a point-and-interval plot showing ANRI estimates and bootstrap CIs for each group. Helps visualize the magnitude and uncertainty of group differences.
A ggplot2 object
set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), condition = rep(c("sequential", "simultaneous"), times = n / 2) ) cmp <- compare_anri(data, group_var = "condition", confidence_bins = seq(0, 100, 20), n_bootstrap = 100, seed = 1) plot_anri_comparison(cmp)set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), condition = rep(c("sequential", "simultaneous"), times = n / 2) ) cmp <- compare_anri(data, group_var = "condition", confidence_bins = seq(0, 100, 20), n_bootstrap = 100, seed = 1) plot_anri_comparison(cmp)
Creates a histogram of the bootstrap distribution of the difference between two groups.
plot_anri_difference_distribution(compare_obj)plot_anri_difference_distribution(compare_obj)
compare_obj |
List output from compare_anri() |
A ggplot2 object
set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), condition = rep(c("sequential", "simultaneous"), times = n / 2) ) cmp <- compare_anri(data, group_var = "condition", confidence_bins = seq(0, 100, 20), n_bootstrap = 100, seed = 1) plot_anri_difference_distribution(cmp)set.seed(123) n <- 300 data <- data.frame( target_present = rep(c(TRUE, FALSE), each = n / 2), identification = sample(c("suspect", "filler", "reject"), n, replace = TRUE, prob = c(0.5, 0.25, 0.25)), confidence = round(runif(n, 0, 100)), condition = rep(c("sequential", "simultaneous"), times = n / 2) ) cmp <- compare_anri(data, group_var = "condition", confidence_bins = seq(0, 100, 20), n_bootstrap = 100, seed = 1) plot_anri_difference_distribution(cmp)
Creates a plot showing how much uncertainty is reduced by different lineup responses.
plot_bayes_information_gain(bayes_obj, selected_responses = NULL)plot_bayes_information_gain(bayes_obj, selected_responses = NULL)
bayes_obj |
List output from make_bayes_curves() |
selected_responses |
Character vector of response categories to plot. If NULL (default), plots all responses. |
Information gain curves show the reduction in uncertainty (measured in bits) for each response type across different prior probabilities. A response that points to guilt gains most at priors above 0.5 and one that points to innocence at priors below 0.5; either can be negative where it moves belief towards 0.5. The legend lists responses from the most to the least indicative of guilt.
A ggplot2 object
data(lineup_example) bayes <- make_bayes_curves(lineup_example) plot_bayes_information_gain(bayes)data(lineup_example) bayes <- make_bayes_curves(lineup_example) plot_bayes_information_gain(bayes)
Creates a plot showing how different lineup responses update prior beliefs about suspect guilt to posterior beliefs.
plot_bayes_prior_posterior( bayes_obj, selected_responses = NULL, show_diagonal = TRUE )plot_bayes_prior_posterior( bayes_obj, selected_responses = NULL, show_diagonal = TRUE )
bayes_obj |
List output from make_bayes_curves() |
selected_responses |
Character vector of response categories to plot. If NULL (default), plots all responses. |
show_diagonal |
Logical. Whether to show diagonal line (no update). Default = TRUE. |
Prior-posterior curves show Bayesian updating for different responses. The diagonal line represents no update (posterior = prior). Curves above the diagonal indicate responses that increase belief in guilt, while curves below decrease belief in guilt.
A ggplot2 object
data(lineup_example) bayes <- make_bayes_curves(lineup_example) plot_bayes_prior_posterior(bayes)data(lineup_example) bayes <- make_bayes_curves(lineup_example) plot_bayes_prior_posterior(bayes)
Creates a plot showing the base-rate shift required for one procedure to match another's diagnostic value.
plot_bree(bree_obj, show_reference = TRUE)plot_bree(bree_obj, show_reference = TRUE)
bree_obj |
List output from make_bree_curve() |
show_reference |
Logical. Whether to show reference line at delta = 0. Default = TRUE. |
The BREE curve shows how much the base rate (prior probability) would need to shift for Procedure B to yield the same posterior as Procedure A.
Positive delta: Procedure A is more diagnostic (B needs higher base rate to match) Negative delta: Procedure B is more diagnostic (B needs lower base rate to match) Zero delta: Procedures are equally diagnostic
A ggplot2 object
data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) bree <- make_bree_curve(lineup_example[odd, ], lineup_example[-odd, ]) plot_bree(bree)data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) bree <- make_bree_curve(lineup_example[odd, ], lineup_example[-odd, ]) plot_bree(bree)
Plots DPP at each operating point of the ROC against the confidence threshold that defines it, with the average marked.
plot_dpp(dpp_obj, show_mean = TRUE, show_chance = TRUE, show_dpp = TRUE)plot_dpp(dpp_obj, show_mean = TRUE, show_chance = TRUE, show_dpp = TRUE)
dpp_obj |
List output from make_dpp() or compute_dpp() |
show_mean |
Logical. Draw the average DPP as a dashed line (default = TRUE) |
show_chance |
Logical. Draw the chance reference at DPP = 1, which applies to the unweighted measure only (default = TRUE) |
show_dpp |
Logical. Display the average DPP value on the plot (default = TRUE) |
Points are ordered from the strictest threshold (highest confidence, left) to the most lenient (right), as in Smith et al.'s (2019) Table 4. The operating point with the lowest DPP, the point of maximum utility, is highlighted.
A ggplot2 object
data(lineup_example) dpp <- make_dpp(lineup_example) plot_dpp(dpp)data(lineup_example) dpp <- make_dpp(lineup_example) plot_dpp(dpp)
Overlays DPP at each operating point for two procedures.
plot_dpp_comparison( compare_obj, proc_a_label = "Procedure A", proc_b_label = "Procedure B" )plot_dpp_comparison( compare_obj, proc_a_label = "Procedure A", proc_b_label = "Procedure B" )
compare_obj |
List output from compare_dpp() |
proc_a_label |
Character. Label for procedure A (default = "Procedure A") |
proc_b_label |
Character. Label for procedure B (default = "Procedure B") |
A ggplot2 object
data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) cmp <- compare_dpp(lineup_example[odd, ], lineup_example[-odd, ], n_bootstrap = 0) plot_dpp_comparison(cmp)data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) cmp <- compare_dpp(lineup_example[odd, ], lineup_example[-odd, ], n_bootstrap = 0) plot_dpp_comparison(cmp)
Creates a ggplot visualization showing how effective lineup size varies across confidence levels.
plot_effective_size_conf( ppv_range_obj, show_nominal = TRUE, show_points = TRUE )plot_effective_size_conf( ppv_range_obj, show_nominal = TRUE, show_points = TRUE )
ppv_range_obj |
A lineup_ppv_range object from ppv_range_by_confidence(), or a dataframe with columns: confidence, effective_size |
show_nominal |
Logical. Whether to show nominal lineup size as reference line (default = TRUE) |
show_points |
Logical. Whether to show points at each confidence level (default = TRUE) |
Effective size < nominal size indicates lineup bias, where some members are implausible and rarely chosen. This can vary by confidence level if witnesses at different confidence levels have different lineup viewing strategies.
A horizontal line at the nominal lineup size provides a reference. Values below this line indicate bias; values at or above indicate fair lineups.
A ggplot2 object
data(lineup_example) ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_effective_size_conf(ppv_range)data(lineup_example) ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_effective_size_conf(ppv_range)
Creates a ggplot2 visualization showing information gain for each response category in an EIG analysis.
plot_eig(eig_obj, max_responses = 15, color_by = "identification")plot_eig(eig_obj, max_responses = 15, color_by = "identification")
eig_obj |
A lineup_eig object from compute_eig() |
max_responses |
Maximum number of response categories to display (default = 15). Categories are sorted by information gain (highest first). |
color_by |
Character. Color bars by "identification" decision or "ig" (information gain). Default = "identification". |
This function creates a bar plot showing information gain for each response category. Response categories are ordered by IG (descending), with the most informative responses shown first. Colors distinguish different identification decisions (suspect/filler/reject).
A ggplot2 object
data(lineup_example) eig_result <- compute_eig(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_eig(eig_result)data(lineup_example) eig_result <- compute_eig(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_eig(eig_result)
Creates a ggplot2 visualization showing how each response category updates beliefs about guilt (posterior probabilities).
plot_eig_posteriors(eig_obj, max_responses = 15, show_prior = TRUE)plot_eig_posteriors(eig_obj, max_responses = 15, show_prior = TRUE)
eig_obj |
A lineup_eig object from compute_eig() |
max_responses |
Maximum number of response categories to display (default = 15) |
show_prior |
Logical. Whether to show prior probability line (default = TRUE) |
This function creates a bar plot showing the posterior probability of guilt for each response category. The prior probability is shown as a dashed line for reference. Response categories that push beliefs toward guilt (posterior > prior) are colored red, while those pushing toward innocence are colored blue.
A ggplot2 object
data(lineup_example) eig_result <- compute_eig(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_eig_posteriors(eig_result)data(lineup_example) eig_result <- compute_eig(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_eig_posteriors(eig_result)
Creates a 2D visualization of face embeddings using dimensionality reduction, showing how faces cluster in the embedding space.
plot_embedding_space( embeddings_df, method = c("umap", "pca", "tsne"), labels = NULL, highlight = NULL, n_neighbors = 15, perplexity = 30 )plot_embedding_space( embeddings_df, method = c("umap", "pca", "tsne"), labels = NULL, highlight = NULL, n_neighbors = 15, perplexity = 30 )
embeddings_df |
A tibble from |
method |
Dimensionality reduction method: "umap" (default), "pca", or "tsne". |
labels |
Optional character vector of labels for each point. |
highlight |
Optional indices of points to highlight (e.g., target position). |
n_neighbors |
For UMAP, number of neighbors. Default is 15. |
perplexity |
For t-SNE, perplexity parameter. Default is 30. |
This visualization helps understand:
How faces cluster by identity
Which foils are closest to the target in embedding space
Overall structure of similarity in a lineup
A ggplot2 object.
## Not run: # Get embeddings for all lineup members all_faces <- c("target.jpg", paste0("foil", 1:5, ".jpg")) embs <- batch_embeddings(all_faces) # Plot with target highlighted plot_embedding_space(embs, labels = c("Target", paste0("F", 1:5)), highlight = 1) ## End(Not run)## Not run: # Get embeddings for all lineup members all_faces <- c("target.jpg", paste0("foil", 1:5, ".jpg")) embs <- batch_embeddings(all_faces) # Plot with target highlighted plot_embedding_space(embs, labels = c("Target", paste0("F", 1:5)), highlight = 1) ## End(Not run)
Creates a ggplot visualization showing the mistaken identification rate (from target-absent lineups) across confidence levels.
plot_error_rate_conf(ppv_range_obj, show_points = TRUE)plot_error_rate_conf(ppv_range_obj, show_points = TRUE)
ppv_range_obj |
A lineup_ppv_range object from ppv_range_by_confidence(), or a dataframe with columns: confidence, error_rate |
show_points |
Logical. Whether to show points at each confidence level (default = TRUE) |
The error rate is computed from target-absent (culprit-absent) lineups as: (suspect IDs + filler IDs) / total target-absent trials
Higher confidence should generally be associated with lower error rates (better calibration).
A ggplot2 object
data(lineup_example) ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_error_rate_conf(ppv_range)data(lineup_example) ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_error_rate_conf(ppv_range)
Plot Bootstrap Distribution of Effective Size
plot_esize_distribution( boot_values, metric = "Effective size", bins = NULL, binwidth = NULL )plot_esize_distribution( boot_values, metric = "Effective size", bins = NULL, binwidth = NULL )
boot_values |
Numeric vector of bootstrap estimates, as returned by
|
metric |
Label for the effective size statistic. |
bins |
Number of histogram bins. Defaults to |
binwidth |
Histogram bin width. Defaults to |
Effective size is a function of the whole table of choices, so its
bootstrap distribution is usually fine-grained enough to bin as a continuous
quantity, and the default is then 30 bins. When the number of lineup choices
is small the estimates become visibly discrete, and the automatic rule bins
at the resolution of the attainable values instead. See
plot_lineup_bias_distribution for the rationale.
A ggplot object.
vec <- round(runif(200, 1, 6)) dist <- esize_boot_dist(vec, k = 6, metric = "tredoux", R = 500) plot_esize_distribution(dist, metric = "E (Tredoux, 1998)")vec <- round(runif(200, 1, 6)) dist <- esize_boot_dist(vec, k = 6, metric = "tredoux", R = 500) plot_esize_distribution(dist, metric = "E (Tredoux, 1998)")
Creates a ggplot2 visualization of full ROC data from lineup identification experiments using the Smith & Yang (2020) method.
plot_fullroc( fullroc_obj, show_auc = TRUE, point_labels = FALSE, title = "Full ROC Curve (Smith & Yang, 2020)" )plot_fullroc( fullroc_obj, show_auc = TRUE, point_labels = FALSE, title = "Full ROC Curve (Smith & Yang, 2020)" )
fullroc_obj |
Either the list returned by |
show_auc |
Logical. Whether to display AUC value on plot (default = TRUE) |
point_labels |
Logical. Whether to label points (default = FALSE, can be crowded) |
title |
Character. Plot title (default = "Full ROC Curve (Smith & Yang, 2020)") |
A ggplot2 object. Being an ordinary ggplot, it can be modified with
the usual + syntax, e.g. p + ggplot2::theme_minimal().
data(lineup_example) fullroc_result <- make_fullroc_data(lineup_example) plot_fullroc(fullroc_result)data(lineup_example) fullroc_result <- make_fullroc_data(lineup_example) plot_fullroc(fullroc_result)
Plot Bootstrap Distribution of Lineup Bias
plot_lineup_bias_distribution( boot_values, target_pos = NULL, bins = NULL, binwidth = NULL )plot_lineup_bias_distribution( boot_values, target_pos = NULL, bins = NULL, binwidth = NULL )
boot_values |
Numeric vector of bootstrap estimates, as returned by
|
target_pos |
Optional target position label for title. |
bins |
Number of histogram bins. Defaults to |
binwidth |
Histogram bin width. Defaults to |
Bootstrap estimates of a lineup proportion are counts divided by the
number of lineup choices, so they fall on a discrete lattice with spacing
1/n rather than on a continuum. Binning such values at an arbitrary
resolution produces a misleading histogram: bins that contain no attainable
value are empty regardless of the data, and bins that happen to straddle two
attainable values are twice as tall as their neighbours. When neither
bins nor binwidth is supplied, the lattice spacing is
recovered (from the "n" attribute when present, otherwise from the
values themselves) and used as the bin width, so each bar covers exactly one
attainable proportion. Estimates that are effectively continuous fall back
to 30 bins.
A ggplot object.
vec <- round(runif(200, 1, 6)) dist <- lineup_bias_boot_dist(vec, target_pos = 3, k = 6, R = 500) plot_lineup_bias_distribution(dist, target_pos = 3)vec <- round(runif(200, 1, 6)) dist <- lineup_bias_boot_dist(vec, target_pos = 3, k = 6, R = 500) plot_lineup_bias_distribution(dist, target_pos = 3)
Creates a bar plot showing the similarity of each foil to the target face, describing scores in a recognition model's embedding space, not measuring or certifying lineup fairness.
plot_lineup_similarity( lineup, show_threshold = TRUE, use_similarity = TRUE, title = NULL, colors = NULL )plot_lineup_similarity( lineup, show_threshold = TRUE, use_similarity = TRUE, title = NULL, colors = NULL )
lineup |
A lineup_similarity object from |
show_threshold |
Logical. If TRUE, adds a horizontal line at the verification threshold. Default is TRUE. |
use_similarity |
Logical. If TRUE, plots similarity scores (higher = more similar). If FALSE, plots distance (lower = more similar). Default is TRUE. |
title |
Optional plot title. |
colors |
Color palette for bars. Default uses a gradient from blue (dissimilar) to red (similar). |
A ggplot2 object.
## Not run: lineup <- lineup_similarity("target.jpg", foil_paths) plot_lineup_similarity(lineup) # Show distance instead of similarity plot_lineup_similarity(lineup, use_similarity = FALSE) ## End(Not run)## Not run: lineup <- lineup_similarity("target.jpg", foil_paths) plot_lineup_similarity(lineup) # Show distance instead of similarity plot_lineup_similarity(lineup, use_similarity = FALSE) ## End(Not run)
Contour plot of the joint distribution of suspect signal and max filler signal.
plot_msdt_joint( dprime, gamma, lineup_size, lineup_type = c("culprit_present", "culprit_absent"), n = 120, xlim = NULL, ylim = NULL, show_decision = TRUE )plot_msdt_joint( dprime, gamma, lineup_size, lineup_type = c("culprit_present", "culprit_absent"), n = 120, xlim = NULL, ylim = NULL, show_decision = TRUE )
dprime |
Discriminability between guilty and innocent suspects. |
gamma |
Decision criterion. |
lineup_size |
Lineup size (suspect + fillers), must be >= 2. |
lineup_type |
Either "culprit_present" or "culprit_absent". |
n |
Grid resolution per axis. |
xlim, ylim
|
Optional numeric ranges for axes. |
show_decision |
Logical. If TRUE, overlays MAX rule boundaries. |
A ggplot object.
# Joint distribution for a culprit-present 6-person lineup plot_msdt_joint(dprime = 1.5, gamma = 0.8, lineup_size = 6)# Joint distribution for a culprit-present 6-person lineup plot_msdt_joint(dprime = 1.5, gamma = 0.8, lineup_size = 6)
Creates a ggplot visualization showing the range of PPV estimates across the three correction methods (nominal, effective, none) as recommended by Fitzgerald et al. (2023).
plot_ppv_range(ppv_range_obj, show_band = TRUE, show_points = TRUE)plot_ppv_range(ppv_range_obj, show_band = TRUE, show_points = TRUE)
ppv_range_obj |
A lineup_ppv_range object from ppv_range_by_confidence() |
show_band |
Logical. Whether to show shaded uncertainty band between nominal and none estimates (default = TRUE) |
show_points |
Logical. Whether to show points at each confidence level (default = TRUE) |
The plot shows three curves:
Nominal (blue, dashed): Best-case estimate assuming fair lineup
Effective (red, solid): Size-based approximation accounting for uneven choices
None (gray, dashed): Worst-case estimate (no correction)
The shaded band between nominal and none represents the PPV uncertainty range due to unknown lineup fairness conditions. The effective size estimate typically falls within this range and is a size-based approximation, not a validated best estimate.
A ggplot2 object
Fitzgerald, R. J., Tredoux, C. G., & Juncu, S. (2023). Estimation of eyewitness error rates in fair and biased lineups. Law and Human Behavior.
data(lineup_example) ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_ppv_range(ppv_range)data(lineup_example) ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) plot_ppv_range(ppv_range)
plot() methods for the objects returned by the ROC and CAC functions,
so that plot(x) works directly rather than requiring the user to know
which plot builder matches which object.
## S3 method for class 'lineup_roc_data' plot(x, ...) ## S3 method for class 'lineup_roc' plot(x, ...) ## S3 method for class 'lineup_cac_data' plot(x, ...) ## S3 method for class 'lineup_cac' plot(x, ...) ## S3 method for class 'lineup_fullroc_data' plot(x, ...) ## S3 method for class 'lineup_fullroc' plot(x, ...)## S3 method for class 'lineup_roc_data' plot(x, ...) ## S3 method for class 'lineup_roc' plot(x, ...) ## S3 method for class 'lineup_cac_data' plot(x, ...) ## S3 method for class 'lineup_cac' plot(x, ...) ## S3 method for class 'lineup_fullroc_data' plot(x, ...) ## S3 method for class 'lineup_fullroc' plot(x, ...)
x |
An object returned by one of the ROC or CAC functions listed above. |
... |
Additional arguments passed to the underlying plot builder. |
Each ROC/CAC analysis comes in two forms. The *_data() function
computes and returns the data only; the function without the suffix wraps it,
builds the plot, and returns a classed object with the plot stored in
$plot:
These methods accept either form. When a plot has already been built it is
returned as-is; otherwise it is constructed with the matching builder
(make_roc_gg, make_cac_gg or
plot_fullroc).
The value is an ordinary ggplot object, so it can be modified with the usual
+ syntax, e.g. plot(x) + ggplot2::theme_minimal().
A ggplot2 object.
data(lineup_example) plot(make_rocdata(lineup_example)) plot(make_cacdata(lineup_example))data(lineup_example) plot(make_rocdata(lineup_example)) plot(make_cacdata(lineup_example))
Creates a histogram or density plot showing the distribution of face similarities, useful for understanding the range of similarities in a lineup or database.
plot_similarity_distribution( similarities, type = c("histogram", "density"), threshold = NULL, xlab = "Similarity", title = "Distribution of Face Similarities" )plot_similarity_distribution( similarities, type = c("histogram", "density"), threshold = NULL, xlab = "Similarity", title = "Distribution of Face Similarities" )
similarities |
Numeric vector of similarity scores or distances. |
type |
Plot type: "histogram" or "density". |
threshold |
Optional threshold value to show as vertical line. |
xlab |
X-axis label. |
title |
Plot title. |
A ggplot2 object.
set.seed(123) similarities <- rbeta(100, 2, 5) plot_similarity_distribution(similarities, threshold = 0.6)set.seed(123) similarities <- rbeta(100, 2, 5) plot_similarity_distribution(similarities, threshold = 0.6)
Creates a plot showing expected utility across confidence criteria.
plot_utility_curves(utility_obj, show_max = TRUE, show_reject = TRUE)plot_utility_curves(utility_obj, show_max = TRUE, show_reject = TRUE)
utility_obj |
List output from make_utility_curves() |
show_max |
Logical. Whether to highlight maximum utility point. Default = TRUE. |
show_reject |
Logical. Whether to show the reject-all utility, drawn as a dashed horizontal reference line. Default = TRUE. |
The utility curve shows the expected utility at each confidence criterion. Higher confidence criteria have lower hit rates but also lower false alarm rates. The optimal criterion maximizes expected utility given the base rate and cost structure.
A ggplot2 object
data(lineup_example) util <- make_utility_curves(lineup_example) plot_utility_curves(util)data(lineup_example) util <- make_utility_curves(lineup_example) plot_utility_curves(util)
Creates a plot showing the difference in expected utility between two procedures across base rates.
plot_utility_difference(util_diff_obj, show_crossover = TRUE)plot_utility_difference(util_diff_obj, show_crossover = TRUE)
util_diff_obj |
List output from make_utility_difference() |
show_crossover |
Logical. Whether to mark crossover points. Default = TRUE. |
Utility difference curves reveal which procedure is superior at different base rates. Regions above zero favor Procedure A, while regions below zero favor Procedure B. Crossover points indicate base rates where procedures are equivalent.
A ggplot2 object
data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) util_diff <- make_utility_difference(lineup_example[odd, ], lineup_example[-odd, ], base_rate_grid = seq(0.1, 0.9, 0.1)) plot_utility_difference(util_diff)data(lineup_example) odd <- seq(1, nrow(lineup_example), by = 2) util_diff <- make_utility_difference(lineup_example[odd, ], lineup_example[-odd, ], base_rate_grid = seq(0.1, 0.9, 0.1)) plot_utility_difference(util_diff)
Plots the posterior distribution of Tredoux's E' as a histogram overlaid with a kernel density estimate. Vertical lines mark the posterior median and credible interval bounds.
## S3 method for class 'esize_T_bayes' plot(x, ...)## S3 method for class 'esize_T_bayes' plot(x, ...)
x |
An object of class |
... |
Further arguments (unused). |
A ggplot object.
Plots the posterior distribution of Delta = E'_A - E'_B as a histogram with density overlay. A vertical line at zero and shaded probability mass above/below zero are shown.
## S3 method for class 'esize_T_bayes_compare' plot(x, ...)## S3 method for class 'esize_T_bayes_compare' plot(x, ...)
x |
An object of class |
... |
Further arguments (unused). |
A ggplot object.
Plot a Wixted Lineup-Memory Model Comparison
## S3 method for class 'lineup_model_comparison' plot(x, type = c("comparison", "fit", "roc"), ...)## S3 method for class 'lineup_model_comparison' plot(x, type = c("comparison", "fit", "roc"), ...)
x |
A |
type |
Plot type: Pearson comparison, fitted response cells, or ROC. |
... |
Additional arguments (currently ignored). |
A ggplot object.
Creates a multi-panel visualization comparing fitted models.
## S3 method for class 'model_comparison' plot(x, which = "all", ncol = 2, ...)## S3 method for class 'model_comparison' plot(x, which = "all", ncol = 2, ...)
x |
A model_comparison object from compare_models() |
which |
Character vector specifying which plots to create. Options: "2ht", "eig_ig", "eig_posteriors", "fullroc" Default = "all" creates all available plots. |
ncol |
Integer. Number of columns for plot layout (default = 2) |
... |
Additional arguments passed to individual plotting functions |
This function creates side-by-side visualizations of all fitted models for easy comparison. The specific plots depend on which models were fit:
**2-HT**: Parameter estimates with confidence intervals
**EIG**: Information gain by response category
**Full ROC**: ROC curve with AUC
A combined ggplot object (via patchwork or cowplot)
Plot a Multi-d' Analysis
## S3 method for class 'multid' plot(x, type = c("decomposition", "indices", "confidence"), ...)## S3 method for class 'multid' plot(x, type = c("decomposition", "indices", "confidence"), ...)
x |
An object of class |
type |
|
... |
Unused. |
A ggplot object.
md <- make_multid(counts = c(G = 45, Fp = 24, Rp = 31, I = 17, Fa = 26, Ra = 57)) plot(md) plot(md, type = "indices")md <- make_multid(counts = c(G = 45, Fp = 24, Rp = 31, I = 17, Fa = 26, Ra = 57)) plot(md) plot(md, type = "indices")
Creates a side-by-side visualization of ROC curves for two conditions with shaded pAUC regions and statistical test results.
## S3 method for class 'pauc_comparison' plot(x, show_cutoff = TRUE, show_test_results = TRUE, ...)## S3 method for class 'pauc_comparison' plot(x, show_cutoff = TRUE, show_test_results = TRUE, ...)
x |
A pauc_comparison object from compare_pauc() |
show_cutoff |
Logical. Whether to show false ID rate cutoff line (default = TRUE) |
show_test_results |
Logical. Whether to show test results on plot (default = TRUE) |
... |
Additional arguments (ignored) |
Creates a comparison plot showing:
ROC curves for both conditions
Shaded pAUC regions (up to cutoff)
Vertical line at false ID rate cutoff
Test statistics (Z-score, p-value)
A ggplot2 object
Plot Power Analysis Results
## S3 method for class 'power_analysis' plot(x, ...)## S3 method for class 'power_analysis' plot(x, ...)
x |
A power_analysis object |
... |
Additional arguments (ignored) |
A ggplot object, returned invisibly. Because it is an ordinary ggplot, further layers and themes can be added to it.
Plot Method for SDT ROC Fit
## S3 method for class 'sdt_roc_fit' plot(x, ...)## S3 method for class 'sdt_roc_fit' plot(x, ...)
x |
An sdt_roc_fit object |
... |
Additional arguments passed to plot |
Creates a default plot for winter_2ht objects showing parameter estimates.
## S3 method for class 'winter_2ht' plot(x, ...)## S3 method for class 'winter_2ht' plot(x, ...)
x |
A winter_2ht object |
... |
Additional arguments passed to plot_2ht_parameters() |
Creates histograms showing the bootstrap distributions of parameter estimates.
## S3 method for class 'winter_2ht_boot' plot(x, ...)## S3 method for class 'winter_2ht_boot' plot(x, ...)
x |
A winter_2ht_boot object |
... |
Additional arguments (not used) |
Calculates Positive Predictive Value (PPV) for each confidence level, using a specified method to correct for lineup size when estimating innocent-suspect identification rates.
ppv_by_confidence( data, lineup_size = 6, confidence_bins = NULL, correction = c("nominal", "effective", "none"), effective_size_data = NULL, quiet = FALSE, innocent_suspect = "auto", base_rate = NULL )ppv_by_confidence( data, lineup_size = 6, confidence_bins = NULL, correction = c("nominal", "effective", "none"), effective_size_data = NULL, quiet = FALSE, innocent_suspect = "auto", base_rate = NULL )
data |
A dataframe with columns:
|
lineup_size |
Integer. Nominal number of people in lineup (default = 6) |
confidence_bins |
Numeric vector of bin edges for grouping confidence (e.g., c(0, 60, 80, 100)). If NULL, uses individual confidence levels. |
correction |
Character. Method for estimating innocent-suspect IDs: "nominal" (best-case, assumes fair lineup), "effective" (accounts for bias), "none" (worst-case, no correction). Default = "nominal". |
effective_size_data |
Optional dataframe with pre-computed effective sizes per confidence bin (must have columns: conf_level, effective_size). If NULL and correction="effective", computes from data. |
quiet |
Logical. Suppress the note issued when |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
base_rate |
Prior probability that the suspect is guilty.
|
PPV (Positive Predictive Value) is the probability that a suspect is guilty given an identification. The formula is:
PPV = guilty_suspect_IDs / (guilty_suspect_IDs + innocent_suspect_IDs)
Different correction methods estimate innocent_suspect_IDs differently:
"nominal": assumes perfect lineup fairness (error_rate / lineup_size)
"effective": uses effective size to account for bias (error_rate / effective_size)
"none": assumes all errors are innocent-suspect IDs (error_rate)
Note on the base rate: by default PPV is computed from raw
target-present and target-absent counts, so the prior probability of guilt
is implicitly the experiment's TP:TA ratio (e.g., 0.5 for the usual balanced
design), and a message is issued when the two arms differ in size. Set
base_rate to compute
from the guilty-ID rate
and innocent-suspect-ID rate instead;
base_rate = 0.5 reproduces the balanced-design value at any sample
sizes. PPV is not base-rate-free: at different real-world base
rates the PPV of the same procedure will differ. When target-absent lineups
have no designated innocent suspect, the correction also relies on the
pseudo-distribution assumption of spreading filler choices over lineup
members, which is a modeling approximation.
A list containing:
ppv_data: Dataframe with PPV by confidence level
overall_ppv: Overall PPV across all confidence levels
correction_method: The correction method requested
innocent_suspect_method: "designated" when target-absent trials identify an innocent suspect directly, otherwise the estimation route
lineup_size: Nominal lineup size
Lineup-size corrections exist to estimate how many of the observed
target-absent identifications fell on an innocent suspect, in data where no
innocent suspect was designated. When the data do designate one, that count
is observed rather than estimated, and correction has no work to do:
all three methods return the same PPV. This is correct, not a fault, but it
is easy to mistake for one, so a message is issued unless quiet =
TRUE. lineup_example is data of this kind.
Fitzgerald, R. J., Tredoux, C. G., & Juncu, S. (2023). Estimation of eyewitness error rates in fair and biased lineups. Law and Human Behavior.
data(lineup_example) # Nominal correction (assumes fair lineup) ppv_result <- ppv_by_confidence(lineup_example, correction = "nominal", confidence_bins = c(0, 60, 80, 100)) # Effective size correction (accounts for bias) ppv_result <- ppv_by_confidence(lineup_example, correction = "effective", confidence_bins = c(0, 60, 80, 100))data(lineup_example) # Nominal correction (assumes fair lineup) ppv_result <- ppv_by_confidence(lineup_example, correction = "nominal", confidence_bins = c(0, 60, 80, 100)) # Effective size correction (accounts for bias) ppv_result <- ppv_by_confidence(lineup_example, correction = "effective", confidence_bins = c(0, 60, 80, 100))
Calculates PPV for each confidence level using all three correction methods (nominal, effective, none) to show the range of plausible PPV estimates.
ppv_range_by_confidence( data, lineup_size = 6, confidence_bins = NULL, effective_size_data = NULL, innocent_suspect = "auto", base_rate = NULL )ppv_range_by_confidence( data, lineup_size = 6, confidence_bins = NULL, effective_size_data = NULL, innocent_suspect = "auto", base_rate = NULL )
data |
A dataframe with columns: target_present, identification, confidence |
lineup_size |
Integer. Nominal number of people in lineup (default = 6) |
confidence_bins |
Numeric vector of bin edges (optional) |
effective_size_data |
Optional pre-computed effective sizes |
innocent_suspect |
How innocent-suspect identifications in
target-absent lineups are obtained. |
base_rate |
Prior probability that the suspect is guilty.
|
This function provides the full PPV range recommended by Fitzgerald et al. (2023):
Nominal (upper bound): Assumes perfectly fair lineup
Effective: Accounts for lineup bias via effective size
None (lower bound): Assumes worst-case (all errors are innocent-suspect)
The range between nominal and none represents the uncertainty in PPV due to unknown lineup fairness conditions.
A list of class "lineup_ppv_range" containing:
ppv_range_data: Dataframe with all three PPV estimates per confidence level
ppv_nominal: Nominal correction results
ppv_effective: Effective size correction results
ppv_none: No correction results
lineup_size: Nominal lineup size
Fitzgerald, R. J., Tredoux, C. G., & Juncu, S. (2023). Estimation of eyewitness error rates in fair and biased lineups. Law and Human Behavior.
data(lineup_example) # Compute full PPV range ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) print(ppv_range) # Access individual corrections ppv_range$ppv_nominal$overall_ppv ppv_range$ppv_effective$overall_ppv ppv_range$ppv_none$overall_ppvdata(lineup_example) # Compute full PPV range ppv_range <- ppv_range_by_confidence(lineup_example, confidence_bins = c(0, 60, 80, 100)) print(ppv_range) # Access individual corrections ppv_range$ppv_nominal$overall_ppv ppv_range$ppv_effective$overall_ppv ppv_range$ppv_none$overall_ppv
Print method for esize_T_bayes objects
## S3 method for class 'esize_T_bayes' print(x, ...)## S3 method for class 'esize_T_bayes' print(x, ...)
x |
An object of class |
... |
Further arguments (unused). |
Print method for esize_T_bayes_compare objects
## S3 method for class 'esize_T_bayes_compare' print(x, ...)## S3 method for class 'esize_T_bayes_compare' print(x, ...)
x |
An object of class |
... |
Further arguments (unused). |
Print Method for ANRI Objects
## S3 method for class 'lineup_anri' print(x, ...)## S3 method for class 'lineup_anri' print(x, ...)
x |
An anri object from compute_anri() |
... |
Additional arguments (ignored) |
Print Method for ANRI Comparison Objects
## S3 method for class 'lineup_anri_comparison' print(x, ...)## S3 method for class 'lineup_anri_comparison' print(x, ...)
x |
A comparison object from compare_anri() |
... |
Additional arguments (ignored) |
Print Method for Bayes Curves Objects
## S3 method for class 'lineup_bayes_curves' print(x, ...)## S3 method for class 'lineup_bayes_curves' print(x, ...)
x |
A bayes_curves object from make_bayes_curves() |
... |
Additional arguments (ignored) |
Print Method for lineup_cac Objects
## S3 method for class 'lineup_cac' print(x, ...)## S3 method for class 'lineup_cac' print(x, ...)
x |
A lineup_cac object |
... |
Additional arguments (ignored) |
Print Method for lineup_calibration Objects
## S3 method for class 'lineup_calibration' print(x, ...)## S3 method for class 'lineup_calibration' print(x, ...)
x |
A lineup_calibration object |
... |
Additional arguments (ignored) |
Print Method for Lineup Data
## S3 method for class 'lineup_data' print(x, ...)## S3 method for class 'lineup_data' print(x, ...)
x |
A lineup_data object |
... |
Additional arguments (ignored) |
Print Method for lineup_dpp Objects
## S3 method for class 'lineup_dpp' print(x, ...)## S3 method for class 'lineup_dpp' print(x, ...)
x |
A lineup_dpp object |
... |
Additional arguments (ignored) |
Print Method for lineup_dpp_comparison Objects
## S3 method for class 'lineup_dpp_comparison' print(x, ...)## S3 method for class 'lineup_dpp_comparison' print(x, ...)
x |
A lineup_dpp_comparison object from |
... |
Additional arguments (ignored) |
Print Method for lineup_eig Objects
## S3 method for class 'lineup_eig' print(x, n_responses = 10, ...)## S3 method for class 'lineup_eig' print(x, n_responses = 10, ...)
x |
A lineup_eig object from compute_eig() |
n_responses |
Number of response categories to display (default = 10) |
... |
Additional arguments (ignored) |
Print Method for lineup_fullroc Objects
## S3 method for class 'lineup_fullroc' print(x, show_diagnosticity = TRUE, ...)## S3 method for class 'lineup_fullroc' print(x, show_diagnosticity = TRUE, ...)
x |
A lineup_fullroc object |
show_diagnosticity |
Logical. Whether to print diagnosticity table (default = TRUE) |
... |
Additional arguments (ignored) |
Print Method for lineup_ppv Objects
## S3 method for class 'lineup_ppv' print(x, ...)## S3 method for class 'lineup_ppv' print(x, ...)
x |
A lineup_ppv object |
... |
Additional arguments (ignored) |
Print Method for lineup_ppv_range Objects
## S3 method for class 'lineup_ppv_range' print(x, ...)## S3 method for class 'lineup_ppv_range' print(x, ...)
x |
A lineup_ppv_range object |
... |
Additional arguments (ignored) |
Print Method for lineup_rac Objects
## S3 method for class 'lineup_rac' print(x, ...)## S3 method for class 'lineup_rac' print(x, ...)
x |
A lineup_rac object |
... |
Additional arguments (ignored) |
Print Method for lineup_roc Objects
## S3 method for class 'lineup_roc' print(x, ...)## S3 method for class 'lineup_roc' print(x, ...)
x |
A lineup_roc object |
... |
Additional arguments (ignored) |
Print Method for lineup_similarity
## S3 method for class 'lineup_similarity' print(x, ...)## S3 method for class 'lineup_similarity' print(x, ...)
x |
A lineup_similarity object |
... |
Additional arguments (ignored) |
Print Method for lineup_table Objects
## S3 method for class 'lineup_table' print(x, ...)## S3 method for class 'lineup_table' print(x, ...)
x |
A |
... |
Additional arguments (ignored). |
x, invisibly.
print(lineup_table(rep(c(5, 3, 2, 1, 8, 6, 7), each = 4), k = 8))print(lineup_table(rep(c(5, 3, 2, 1, 8, 6, 7), each = 4), k = 8))
Print Method for lineup_utility_comparison Objects
## S3 method for class 'lineup_utility_comparison' print(x, ...)## S3 method for class 'lineup_utility_comparison' print(x, ...)
x |
A lineup_utility_comparison object |
... |
Additional arguments (ignored) |
Print Method for model_comparison Objects
## S3 method for class 'model_comparison' print(x, ...)## S3 method for class 'model_comparison' print(x, ...)
x |
A model_comparison object |
... |
Additional arguments (ignored) |
Print Method for pauc_comparison Objects
## S3 method for class 'pauc_comparison' print(x, ...)## S3 method for class 'pauc_comparison' print(x, ...)
x |
A pauc_comparison object |
... |
Additional arguments (ignored) |
Print Method for SDT ROC Fit
## S3 method for class 'sdt_roc_fit' print(x, ...)## S3 method for class 'sdt_roc_fit' print(x, ...)
x |
An sdt_roc_fit object |
... |
Additional arguments (ignored) |
Print Method for Simulated Lineup Data
## S3 method for class 'simulated_lineup_data' print(x, ...)## S3 method for class 'simulated_lineup_data' print(x, ...)
x |
A simulated_lineup_data object |
... |
Additional arguments (ignored) |
Print Method for winter_2ht Objects
## S3 method for class 'winter_2ht' print(x, ...)## S3 method for class 'winter_2ht' print(x, ...)
x |
A winter_2ht object |
... |
Additional arguments (not used) |
Print Method for winter_2ht_boot Objects
## S3 method for class 'winter_2ht_boot' print(x, ...)## S3 method for class 'winter_2ht_boot' print(x, ...)
x |
A winter_2ht_boot object |
... |
Additional arguments (not used) |
Helper function that repeats a number k times
rep_index(index, num)rep_index(index, num)
index |
Number to be repeated |
num |
Number of times to repeat index |
Can be used to create vectors of lineup data
rep_index(1, 10)rep_index(1, 10)
Helper function to rotate vector
rot_vector(lineup_vec)rot_vector(lineup_vec)
lineup_vec |
A vector of lineup data |
# Rotate lineup positions by one place rot_vector(1:6)# Rotate lineup positions by one place rot_vector(1:6)
Launches the bundled Shiny app. It covers lineup fairness from mock-witness choices (bias, effective and functional size, diagnosticity), Bayesian posteriors, Lee and Penrod's Multi-d' model, ROC/CAC/full ROC with pAUC comparison, calibration and decision analyses (ANRI, DPP, expected utility, Bayesian and BREE curves, RAC), expected information gain and PPV ranges, and, when Python is available, face similarity. Every result is computed by an exported r4lineups function.
run_r4lineups_app()run_r4lineups_app()
The app needs the suggested packages shiny, bslib and DT.
None. Launches a Shiny app.
## Not run: run_r4lineups_app() ## End(Not run)## Not run: run_r4lineups_app() ## End(Not run)
Computes a Bayesian posterior distribution for the difference
using independent Jeffreys Beta posteriors
for the hit and false-alarm rates in each condition.
sdt_compare( hits_A, misses_A, fas_A, crs_A, hits_B, misses_B, fas_B, crs_B, label_A = "A", label_B = "B", alpha = 0.5, S = 10000, credible_mass = 0.95 )sdt_compare( hits_A, misses_A, fas_A, crs_A, hits_B, misses_B, fas_B, crs_B, label_A = "A", label_B = "B", alpha = 0.5, S = 10000, credible_mass = 0.95 )
hits_A, misses_A, fas_A, crs_A
|
Integer counts for condition A. |
hits_B, misses_B, fas_B, crs_B
|
Integer counts for condition B. |
label_A, label_B
|
Character labels for conditions (default "A", "B"). |
alpha |
Prior concentration for the Beta prior on each rate.
Default 0.5 (Jeffreys). Named shortcuts: |
S |
Number of posterior draws. Default 10000. |
credible_mass |
Width of the equal-tailed credible interval. Default 0.95. |
Independent Jeffreys Beta posteriors are placed on the hit and false-alarm rates in each condition:
The posterior d' draws are:
and analogously for B. The posterior of the difference is
, from which
is directly read off.
The Jeffreys prior () handles zero cell counts gracefully
and provides direct probability statements - unlike the asymptotic z-test in
compare_sdt_summary.
An object of class "sdt_compare" containing:
Posterior draws of .
Per-condition d' draws.
Posterior summaries of .
Named lower/upper CI on .
Posterior probability that .
Posterior probability that .
Posterior means of each d'.
Output of compare_sdt_summary() for comparison.
Metadata.
Macmillan, N. A., & Creelman, C. D. (2005). Detection Theory: A User's Guide (2nd ed.). Lawrence Erlbaum.
Wixted, J. T., & Mickes, L. (2014). A signal-detection-based diagnostic-feature detection model of eyewitness identification. Psychological Review, 121(2), 262-276.
compare_sdt_summary, sdt_summary_from_counts,
diag_ratio_T_bayes
# Simultaneous vs. sequential lineup comparison res <- sdt_compare( hits_A = 45, misses_A = 55, fas_A = 12, crs_A = 88, hits_B = 38, misses_B = 62, fas_B = 10, crs_B = 90, label_A = "Simultaneous", label_B = "Sequential" ) print(res) plot(res)# Simultaneous vs. sequential lineup comparison res <- sdt_compare( hits_A = 45, misses_A = 55, fas_A = 12, crs_A = 88, hits_B = 38, misses_B = 62, fas_B = 10, crs_B = 90, label_A = "Simultaneous", label_B = "Sequential" ) print(res) plot(res)
Compute d', criterion, and beta from 2x2 SDT counts (hits, false alarms, misses, correct rejections).
sdt_summary_from_counts( hits, fas, misses, cr, correction = c("loglinear", "half", "none") )sdt_summary_from_counts( hits, fas, misses, cr, correction = c("loglinear", "half", "none") )
hits |
Number of hits. |
fas |
Number of false alarms. |
misses |
Number of misses. |
cr |
Number of correct rejections. |
correction |
Correction for extreme rates ("loglinear", "half", or "none"). |
A list with rates and SDT metrics.
# 70 hits, 30 misses; 20 false alarms, 80 correct rejections summ <- sdt_summary_from_counts(hits = 70, fas = 20, misses = 30, cr = 80) summ$dprime summ$c# 70 hits, 30 misses; 20 false alarms, 80 correct rejections summ <- sdt_summary_from_counts(hits = 70, fas = 20, misses = 30, cr = 80) summ$dprime summ$c
Estimates the variance of d' (and criterion) from summary SDT counts using the Gourevitch & Galanter (1967) delta method, the Miller (1996) exact-binomial method, or a parametric bootstrap.
sdt_summary_variance( hits, fas, misses, cr, method = c("miller", "gourevitch", "bootstrap"), correction = c("loglinear", "half", "none"), nboot = 1000, seed = NULL )sdt_summary_variance( hits, fas, misses, cr, method = c("miller", "gourevitch", "bootstrap"), correction = c("loglinear", "half", "none"), nboot = 1000, seed = NULL )
hits |
Number of hits. |
fas |
Number of false alarms. |
misses |
Number of misses. |
cr |
Number of correct rejections. |
method |
Variance method: "gourevitch", "miller", or "bootstrap". |
correction |
Correction for extreme rates ("loglinear", "half", or "none"). |
nboot |
Number of bootstrap samples when method = "bootstrap". |
seed |
Optional random seed for bootstrap variance. |
Miller enumeration is exact for a binomial distribution at its supplied probability. Here that probability is estimated from the observed counts. The resulting variance and downstream Wald test are plug-in approximations, not exact finite-sample inference.
A list with variance estimates for zH, zF, d', and c.
v <- sdt_summary_variance(hits = 70, fas = 20, misses = 30, cr = 80, method = "miller") v$var_dprimev <- sdt_summary_variance(hits = 70, fas = 20, misses = 30, cr = 80, method = "miller") v$var_dprime
Generates simulated eyewitness lineup data based on signal detection theory parameters. Useful for power analysis, method validation, and teaching.
simulate_lineup_data( n_tp = 100, n_ta = 100, d_prime = 1.5, c_criterion = 0, lineup_size = 6, conf_levels = 5, decision_rule = c("max", "best_rest", "ensemble", "integration"), foil_bias = NULL, include_response_time = FALSE, seed = NULL )simulate_lineup_data( n_tp = 100, n_ta = 100, d_prime = 1.5, c_criterion = 0, lineup_size = 6, conf_levels = 5, decision_rule = c("max", "best_rest", "ensemble", "integration"), foil_bias = NULL, include_response_time = FALSE, seed = NULL )
n_tp |
Integer. Number of target-present lineups (default = 100) |
n_ta |
Integer. Number of target-absent lineups (default = 100) |
d_prime |
Numeric. Discriminability (d') between target and lure distributions. Higher values = better memory. Typical range: 0.5 to 3.0 (default = 1.5) |
c_criterion |
Numeric scalar or vector of ordered decision criteria.
The first value is always the identification/rejection threshold. With
confidence ratings, a scalar generates |
lineup_size |
Integer. Number of lineup members (default = 6) |
conf_levels |
Integer. Number of confidence levels to simulate. If NULL, returns binary decision only (default = 5) |
decision_rule |
Character. Decision strategy for lineup choices:
|
foil_bias |
Numeric vector of length |
include_response_time |
Logical. Whether to simulate response times correlated with memory strength (default = FALSE) |
seed |
Integer. Random seed for reproducibility (default = NULL) |
This function implements signal detection models with multiple decision rules:
Target distribution: Normal(d_prime, 1)
Lure distribution: Normal(0, 1)
Decision: Depends on decision_rule parameter
Decision Rules:
MAX (default): Independent observations model. Choose lineup member with highest memory strength (Clark, 2003).
BEST-REST: Best strength minus the average of the remaining members (Clark, 2003).
Ensemble: Best strength minus the average of all lineup members
(Wixted et al., 2018). For lineup size , this is
times BEST-REST, so the two are equivalent model
parameterizations when their criteria are rescaled by the same factor.
Integration: Sum memory strengths across all lineup members. Represents complete integration of evidence (Wixted et al., 2018).
All rules use independent signals with target distribution
and filler distribution . Thus, this simulator
represents the equal-variance, zero-correlation special case of the lineup
models. It does not implement the shared-variance or unequal-variance
likelihood models fitted by Wixted et al. (2018).
The simulation assumes:
Fair lineups (all fillers equally similar to description)
Perfect attention (no guessing without memory)
Normal distributions for memory strength
Response times (if simulated) are a heuristic convenience, not a fitted reaction-time or drift-diffusion model:
Faster for stronger memory signals
A bounded noisy function of decision strength
A dataframe with columns:
participant_id: Unique ID for each trial
target_present: Logical. TRUE if target in lineup
lineup_member: Integer. Position of the lineup member actually chosen, or 0 for a rejection. Each foil is simulated separately and retains its own identity; foils are not treated as interchangeable
suspect_pos: Integer. Position occupied by the suspect (1 by construction; the target in target-present lineups, the designated innocent suspect in target-absent lineups)
identification: Character. "suspect", "filler", or "reject",
derived from lineup_member and suspect_pos. See
classify_lineup_choices
confidence: Numeric (if conf_levels specified). Confidence rating
response_time: Numeric (if include_response_time = TRUE). RT in ms
Memory strengths are drawn independently for every lineup member, so the
foils are distinguishable and are not exchangeable. lineup_member
preserves which member was chosen; identification is a derived
summary of it. Analyses that need member-level information (for example the
effective-size correction in make_ppv_range) can therefore use
lineup_member directly rather than working from the collapsed labels.
Wixted, J. T., Vul, E., Mickes, L., & Wilson, B. M. (2018). Models of lineup memory. Cognitive Psychology, 105, 81–114. doi:10.1016/j.cogpsych.2018.06.001
Mickes, L., et al. (2024). pyWitness 1.0: A python eyewitness identification analysis toolkit. Behavior Research Methods, 56, 1533-1550.
# Basic simulation: strong memory with MAX rule (default) strong_memory <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 2.0, conf_levels = 3, seed = 42 ) # Ensemble decision rule ensemble_data <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 1.5, decision_rule = "ensemble", conf_levels = 5, seed = 42 ) # Integration decision rule integration_data <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 1.5, decision_rule = "integration", conf_levels = 5, seed = 42 ) # Compare ROC curves across decision rules roc_max <- make_roc(strong_memory, lineup_size = 6) roc_ensemble <- make_roc(ensemble_data, lineup_size = 6) # Power analysis: vary sample size (few simulations, to keep this quick; # use hundreds in practice) power_results <- simulate_power_analysis( sample_sizes = c(50, 100), d_prime = 1.5, n_simulations = 5, n_bootstrap = 50, seed = 1 )# Basic simulation: strong memory with MAX rule (default) strong_memory <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 2.0, conf_levels = 3, seed = 42 ) # Ensemble decision rule ensemble_data <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 1.5, decision_rule = "ensemble", conf_levels = 5, seed = 42 ) # Integration decision rule integration_data <- simulate_lineup_data( n_tp = 200, n_ta = 200, d_prime = 1.5, decision_rule = "integration", conf_levels = 5, seed = 42 ) # Compare ROC curves across decision rules roc_max <- make_roc(strong_memory, lineup_size = 6) roc_ensemble <- make_roc(ensemble_data, lineup_size = 6) # Power analysis: vary sample size (few simulations, to keep this quick; # use hundreds in practice) power_results <- simulate_power_analysis( sample_sizes = c(50, 100), d_prime = 1.5, n_simulations = 5, n_bootstrap = 50, seed = 1 )
Estimates statistical power to detect a difference in discriminability between two lineup conditions, by simulating both conditions at a range of sample sizes and recording how often the difference is detected.
simulate_power_analysis( sample_sizes, d_prime, d_prime_null = 0, n_simulations = 1000, test_function = NULL, alpha = 0.05, lineup_size = 6, n_bootstrap = 200, seed = NULL, verbose = TRUE, ... )simulate_power_analysis( sample_sizes, d_prime, d_prime_null = 0, n_simulations = 1000, test_function = NULL, alpha = 0.05, lineup_size = 6, n_bootstrap = 200, seed = NULL, verbose = TRUE, ... )
sample_sizes |
Integer vector of sizes to test (e.g., c(50, 100, 200)). A size n means n target-present and n target-absent witnesses in each of the two simulated conditions: 2n witnesses per condition and 4n in the whole simulated experiment. |
d_prime |
Numeric. Discriminability of the condition of interest. |
d_prime_null |
Numeric. Discriminability of the comparison condition. Defaults to 0, which asks how often the procedure can be shown to discriminate better than chance. Set it to a non-zero value to ask the more usual question of how often two working procedures can be told apart. |
n_simulations |
Integer. Number of simulated studies per sample size
(default = 1000). Power is a proportion estimated from these, so its own
standard error is roughly |
test_function |
Function of two dataframes returning a p-value. Defaults
to a bootstrap comparison of partial AUC via |
alpha |
Numeric. Significance level (default = 0.05) |
lineup_size |
Integer. Lineup size for both conditions (default = 6) |
n_bootstrap |
Integer. Bootstrap resamples used by the default test
(default = 200). This is the dominant cost: the default test is run
|
seed |
Optional integer seed for reproducibility. |
verbose |
Logical. Report progress (default = TRUE). |
... |
Additional arguments passed to simulate_lineup_data(), for example
|
Power is the probability that a study of a given size detects an effect that is really there. Estimating it by simulation means specifying the effect, generating many studies of that size, applying the analysis you intend to use, and counting how often it reaches significance.
That requires a null to test against, which is what d_prime_null
supplies. Simulating a single condition and computing a statistic from it is
not a power analysis, because no hypothesis is being tested.
A dataframe of class power_analysis with columns:
sample_size: n, the number of target-present (and, separately, target-absent) witnesses per condition
n_total: Witnesses in the whole simulated experiment, 4n
power: Proportion of simulated studies reaching significance
power_se: Standard error of that proportion
mean_effect: Mean observed difference in pAUC
sd_effect: Standard deviation of the observed difference
n_valid: Simulations that produced a usable test
n_failed: Attempted simulations without a usable test
n_attempted: All attempted simulations
Power and its Monte Carlo standard error are conditional on usable tests. Failures are retained in the attempted count and trigger a warning; compare failure rates as well as conditional power when assessing a design.
compare_pauc, simulate_lineup_data
# Power to show discriminability better than chance power_res <- simulate_power_analysis( sample_sizes = c(50, 100), d_prime = 1.5, n_simulations = 8, n_bootstrap = 50, conf_levels = 5, seed = 123 ) print(power_res)# Power to show discriminability better than chance power_res <- simulate_power_analysis( sample_sizes = c(50, 100), d_prime = 1.5, n_simulations = 8, n_bootstrap = 50, conf_levels = 5, seed = 123 ) print(power_res)
Converts lineup data from various formats into the standardized r4lineups format. Handles common data issues and ensures compatibility with all confidence-based analyses.
standardize_lineup_data( data, target_present_col = "target_present", identification_col = "identification", confidence_col = "confidence", response_time_col = NULL, participant_id_col = NULL, recode_identification = NULL, validate = TRUE )standardize_lineup_data( data, target_present_col = "target_present", identification_col = "identification", confidence_col = "confidence", response_time_col = NULL, participant_id_col = NULL, recode_identification = NULL, validate = TRUE )
data |
A data frame with lineup identification data |
target_present_col |
Name of column indicating target presence (default = "target_present"). Can also be "tp", "culprit_present", etc. |
identification_col |
Name of column with identification responses (default = "identification"). Can also be "response", "choice", etc. |
confidence_col |
Name of column with confidence ratings (default = "confidence"). Can also be "conf", "certainty", etc. |
response_time_col |
Name of column with response times (optional) |
participant_id_col |
Name of column with participant IDs (optional) |
recode_identification |
Named character vector for recoding identification values. E.g., c("target" = "suspect", "foil" = "filler") |
validate |
Logical. Whether to validate the result (default = TRUE) |
This function:
Renames columns to standard names
Converts data types as needed
Recodes identification values to standard terms
Adds participant IDs if missing
Validates the result (if validate = TRUE)
Adds S3 class "lineup_data"
**Common recoding patterns:**
"target" → "suspect"
"foil" / "distractor" → "filler"
"none" / "not present" / "no choice" → "reject"
A standardized data frame with class "lineup_data" containing:
target_present: Logical
identification: Character ("suspect", "filler", "reject")
confidence: Numeric (if present)
participant_id: Original or generated (if present)
response_time: Numeric (if present)
followed by every other column of data unchanged, including
suspect_pos and lineup_member, which some analyses use.
# Data with non-standard column names raw_data <- data.frame( tp = c(1, 1, 0, 0), response = c("target", "foil", "none", "target"), conf = c(5, 3, 2, 4) ) # Standardize std_data <- standardize_lineup_data( raw_data, target_present_col = "tp", identification_col = "response", confidence_col = "conf", recode_identification = c( "target" = "suspect", "foil" = "filler", "none" = "reject" ) ) print(std_data)# Data with non-standard column names raw_data <- data.frame( tp = c(1, 1, 0, 0), response = c("target", "foil", "none", "target"), conf = c(5, 3, 2, 4) ) # Standardize std_data <- standardize_lineup_data( raw_data, target_present_col = "tp", identification_col = "response", confidence_col = "conf", recode_identification = c( "target" = "suspect", "foil" = "filler", "none" = "reject" ) ) print(std_data)
Summary Method for lineup_eig Objects
## S3 method for class 'lineup_eig' summary(object, ...)## S3 method for class 'lineup_eig' summary(object, ...)
object |
A lineup_eig object from compute_eig() |
... |
Additional arguments (ignored) |
Summary Method for lineup_similarity
## S3 method for class 'lineup_similarity' summary(object, ...)## S3 method for class 'lineup_similarity' summary(object, ...)
object |
A lineup_similarity object |
... |
Additional arguments (ignored) |
Summary Method for model_comparison Objects
## S3 method for class 'model_comparison' summary(object, ...)## S3 method for class 'model_comparison' summary(object, ...)
object |
A model_comparison object |
... |
Additional arguments (ignored) |
Summary Method for pauc_comparison Objects
## S3 method for class 'pauc_comparison' summary(object, ...)## S3 method for class 'pauc_comparison' summary(object, ...)
object |
A pauc_comparison object |
... |
Additional arguments (ignored) |
Summary Method for SDT ROC Fit
## S3 method for class 'sdt_roc_fit' summary(object, ...)## S3 method for class 'sdt_roc_fit' summary(object, ...)
object |
An sdt_roc_fit object |
... |
Additional arguments (ignored) |
Summary Method for winter_2ht Objects
## S3 method for class 'winter_2ht' summary(object, ...)## S3 method for class 'winter_2ht' summary(object, ...)
object |
A winter_2ht object |
... |
Additional arguments (not used) |
Summary Method for winter_2ht_boot Objects
## S3 method for class 'winter_2ht_boot' summary(object, ...)## S3 method for class 'winter_2ht_boot' summary(object, ...)
object |
A winter_2ht_boot object |
... |
Additional arguments (not used) |
Checks whether a data frame meets the requirements for r4lineups confidence-based analyses. Ensures required columns exist with correct types and valid values.
validate_lineup_data( data, require_confidence = TRUE, require_response_time = FALSE, strict = FALSE )validate_lineup_data( data, require_confidence = TRUE, require_response_time = FALSE, strict = FALSE )
data |
A data frame to validate |
require_confidence |
Logical. Whether confidence column is required (default = TRUE). Set to FALSE for analyses that don't need confidence. |
require_response_time |
Logical. Whether response_time is required (default = FALSE) |
strict |
Logical. If TRUE, stops with error on validation failure. If FALSE, returns validation result with messages (default = FALSE) |
**Required columns:**
'target_present': Logical (TRUE/FALSE) indicating if target in lineup
'identification': Character or factor with values "suspect", "filler", or "reject"
'confidence': Numeric (if require_confidence = TRUE). Higher = more confident
**Optional columns:**
'participant_id': Unique identifier for each trial/participant
'response_time': Numeric response time in milliseconds
**Validation checks:**
All required columns present
Correct data types
No missing values in required columns
Valid identification values
Positive confidence values
At least some target-present and target-absent trials
If strict = FALSE, returns a list with:
valid: Logical indicating if data passes validation
messages: Character vector of validation messages/errors
warnings: Character vector of warnings (non-fatal issues)
If strict = TRUE, either returns TRUE invisibly or stops with error.
# Valid data valid_data <- data.frame( target_present = c(TRUE, TRUE, FALSE, FALSE), identification = c("suspect", "filler", "reject", "suspect"), confidence = c(5, 3, 2, 4) ) validate_lineup_data(valid_data) # Invalid data (missing column) invalid_data <- data.frame( target_present = c(TRUE, FALSE), identification = c("suspect", "filler") ) result <- validate_lineup_data(invalid_data, strict = FALSE) print(result$messages) # Strict validation (errors on failure) validate_lineup_data(valid_data, strict = TRUE)# Valid data valid_data <- data.frame( target_present = c(TRUE, TRUE, FALSE, FALSE), identification = c("suspect", "filler", "reject", "suspect"), confidence = c(5, 3, 2, 4) ) validate_lineup_data(valid_data) # Invalid data (missing column) invalid_data <- data.frame( target_present = c(TRUE, FALSE), identification = c("suspect", "filler") ) result <- validate_lineup_data(invalid_data, strict = FALSE) print(result$messages) # Strict validation (errors on failure) validate_lineup_data(valid_data, strict = TRUE)
Computes the large-sample variance of the log diagnosticity ratio for a lineup pair.
var_diag_ratio(lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2)var_diag_ratio(lineup_pres, lineup_abs, pos_pres, pos_abs, k1, k2)
lineup_pres |
A numeric vector of lineup choices for a lineup in which the target was present |
lineup_abs |
A numeric vector of lineup choices for a lineup in which the target was absent |
pos_pres |
A scalar, representing target position in TP lineup. Must be declared by user |
pos_abs |
A scalar, representing target position in TA lineup. Must be declared by user |
k1 |
Number of targets in TP lineup. Must be specified by user (scalar). |
k2 |
Number of targets in TA lineup. Must be specified by user (scalar). |
Returns 1/h - 1/N_TP + 1/f - 1/N_TA for positive suspect-ID counts h and f. This is on the log-ratio scale, as in Tredoux (1998), not the raw ratio scale. A delta-method variance for the raw ratio is the squared diagnosticity ratio times this value.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Data: lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(100, 1, 6)) pos_pres <- 3 pos_abs <- 4 #Call: var_d <- var_diag_ratio(lineup_pres, lineup_abs, pos_pres, pos_abs, 6, 6) var_d <- var_diag_ratio(lineup_pres, lineup_abs, 3, 4, 6, 6)#Data: lineup_pres <- round(runif(100, 1, 6)) lineup_abs <- round(runif(100, 1, 6)) pos_pres <- 3 pos_abs <- 4 #Call: var_d <- var_diag_ratio(lineup_pres, lineup_abs, pos_pres, pos_abs, 6, 6) var_d <- var_diag_ratio(lineup_pres, lineup_abs, 3, 4, 6, 6)
Function to compute variance of ln(d) for k lineup pairs
var_lnd(linedf)var_lnd(linedf)
linedf |
A dataframe of parameters for computing diagnosticity ratio |
To get linedf, use the diag_param helper function
diag_param returns a dataframe containing the following:
n11: Number of mock witnesses who identified the suspect in the target present condition
n21: Number of mock witnesses who did not identify the suspect in the target present condition
n12: Number of mock witnesses who identified the suspect in the target absent condition
n22: Number of mock witnesses who did not identify the suspect in the target absent condition
A dataframe containing the estimated variance of the log diagnosticity ratio for each lineup.
Malpass, R. S. (1981). Effective size and defendant bias in eyewitness identification lineups. Law and Human Behavior, 5(4), 299-309.
Malpass, R. S., Tredoux, C., & McQuiston-Surrett, D. (2007). Lineup construction and lineup fairness. In R. Lindsay, D. F. Ross, J. D. Read, & M. P. Toglia (Eds.), Handbook of Eyewitness Psychology, Vol. 2: Memory for people (pp. 155-178). Mahwah, NJ: Lawrence Erlbaum Associates.
Tredoux, C. G. (1998). Statistical inference on measures of lineup fairness. Law and Human Behavior, 22(2), 217-237.
Tredoux, C. (1999). Statistical considerations when determining measures of lineup size and lineup bias. Applied Cognitive Psychology, 13, S9-S26.
Wells, G. L.,Leippe, M. R., & Ostrom, T. M. (1979). Guidelines for empirically assessing the fairness of a lineup. Law and Human Behavior, 3(4), 285-293.
#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Call: var <- var_lnd(linedf)#Target present data: A <- rep(1:6, length.out = 100) B <- rep(1:5, length.out = 70) C <- rep(1:4, length.out = 20) lineup_pres_list <- list(A, B, C) rm(A, B, C) #Target absent data: A <- rep(6:1, length.out = 100) B <- rep(5:1, length.out = 70) C <- rep(4:1, length.out = 20) lineup_abs_list <- list(A, B, C) rm(A, B, C) # Suspect position for each TP/TA pair pos_list <- c(3, 2, 1) #Nominal size: k <- c(6, 5, 4) #Use diag param helper function to get data (n11, n21, n12, n22): linedf <- diag_param(lineup_pres_list, lineup_abs_list, pos_list, k) #Call: var <- var_lnd(linedf)