The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Package {rolescry}


Title: Name-Blind Variable-Role Detection by Data Signature
Version: 0.3.1
Description: Deterministic, name-blind detection of variable roles (group, outcome, survival or duration time and event, coupled binary-continuous pairs, calendar time index, paired and agreement measurements, repeated measures and panels, scale and rating items, subject or entity identifier, covariate) in tabular data from any domain – clinical, financial, social, experimental. Roles are assigned from each column's information-theoretic signature – Shannon entropy, normalized mutual information, and distributional shape – rather than from column names, so renaming columns to 'col_1', 'col_2', ... does not change the result ("Data inspice, non nomen"). An optional, capped name-based hint and automatic header-row detection are also provided. No large language models and no external data transmission. Extracted from the 'MDStatR' biostatistics engine; see Boynukara (2026) <doi:10.5281/zenodo.20707791>.
License: Apache License (== 2.0)
Encoding: UTF-8
RoxygenNote: 7.3.3
Depends: R (≥ 4.0.0)
Imports: stats, utils
Suggests: moments, diptest, stringdist, readxl, openxlsx, haven, testthat (≥ 3.0.0), knitr, rmarkdown, spelling
Config/testthat/edition: 3
VignetteBuilder: knitr
URL: https://github.com/canboynukara/rolescry
BugReports: https://github.com/canboynukara/rolescry/issues
Language: en-US
NeedsCompilation: no
Packaged: 2026-10-28 10:40:04 UTC; cboyn
Author: Can Boynukara ORCID iD [aut, cre, cph]
Maintainer: Can Boynukara <canboynukara1@gmail.com>
Repository: CRAN
Date/Publication: 2026-10-28 18:40:02 UTC

rolescry: Name-blind Variable-Role Detection

Description

Deterministic, name-blind detection of variable roles (group, outcome, survival time/event, paired, agreement, repeated measures, scale items, subject id, covariate) in tabular data, using information-theoretic signatures (Shannon entropy, normalized mutual information) and distributional shape rather than column names. The guiding principle is "Data inspice, non nomen" – inspect the data, not the name.

Details

The single public entry point is detect_roles(). Header-aware data loading is available via read_data(). No LLMs, no external data transmission; detection is >= 90 percent mathematical signature with an optional, capped (<= 10 percent) name bonus (see the name_bonus argument).

Developed from work on the author's unreleased MDStatR research code; see the README section "Relationship to MDStatR". rolescry requires only base R and has no MDStatR dependency.

Author(s)

Maintainer: Can Boynukara canboynukara1@gmail.com (ORCID) [copyright holder]

See Also

Useful links:


Normalized mutual information

Description

Computes the normalized mutual information (NMI) between two discrete variables, a name-blind, information-theoretic measure of association in [0, 1]. NMI is the mutual information divided by the smaller of the two marginal Shannon entropies; it is 0 for independent variables and 1 for a perfect (deterministic) association, and unlike a raw chi-squared it is comparable across variables with different numbers of levels.

Usage

compute_nmi(x, y = NULL)

Arguments

x

Either a two-way contingency table / matrix of counts, or a vector (factor, character, or numeric) of the first variable.

y

Optional. If x is a vector, the second variable's vector; a contingency table is formed via table(x, y) on complete cases. Ignored when x is already a table/matrix.

Value

A single numeric in [0, 1]. Returns 0 for degenerate input (fewer than two rows/columns, zero total, or near-zero marginal entropy).

Examples

set.seed(1)
g <- sample(c("A", "B", "C"), 200, replace = TRUE)
y <- ifelse(g == "A", "yes", sample(c("yes", "no"), 200, replace = TRUE))
compute_nmi(g, y)            # > 0: g carries information about y
compute_nmi(g, sample(g))    # ~0: shuffled -> independent


Detect the header row of a raw, unparsed table

Description

Given a raw table read with no header (every cell character), scores each of the first rows with a 7-signal weighted heuristic (alphabetic ratio, non-numeric ratio, uniqueness, normalized Shannon entropy, median string length, alpha-vs-next-row transition, fill completeness) and returns the most header-like row plus repaired, unique column names. Empty cells are upward-filled (merged-cell repair) and any still-empty name becomes col_<j>. Base + stats only; no file-format dependencies.

Usage

detect_header(raw, verbose = FALSE)

Arguments

raw

A data.frame or matrix of the raw sheet, read with header = FALSE so the header row appears as data.

verbose

Logical; emit the chosen row via message() if TRUE.

Value

A list with header_row (integer), score (numeric), names (repaired character vector, length == ncol(raw)), and all_scores.

See Also

read_data()

Examples

raw <- data.frame(
  V1 = c("age", "34", "51"),
  V2 = c("sex", "M", "F"),
  V3 = c("score", "8.1", "7.4"),
  stringsAsFactors = FALSE
)
detect_header(raw)$names


Detect variable roles by data signature, not by name

Description

Inspects an already-loaded data frame and assigns each column (or group of columns) to statistical roles – group variable, continuous/binary outcome, survival time and event, paired and agreement measurement pairs, repeated measures, scale items, subject id, and covariates – using only the data information-theoretic signature (Shannon entropy, distributional shape, inter-column structure) and never the column names. Renaming columns to col_1, col_2, ... does not change the result (the name-blindness, or "turnusol", invariant).

Usage

detect_roles(data, name_bonus = NULL, verbose = FALSE)

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

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

Arguments

data

A data.frame (already loaded; for header-aware loading see read_data()).

name_bonus

Optional named list mapping role keys to character vectors of case-insensitive keyword regex fragments, e.g. list(group_var = c("treat", "arm"), outcome_binary = c("death")). Recognized keys: group_var, outcome_continuous, outcome_binary, subject_id, time_variable, event_variable. NULL (default) = pure signature detection.

verbose

Logical; if TRUE, emit per-role progress via message(). Default FALSE (silent).

x

A role_detection object.

...

Ignored.

object

A role_detection object.

Details

Detection is purely mathematical by default (name_bonus = NULL). When a keyword dictionary is supplied via name_bonus, column names act only as a small, capped tie-breaker (at most a +10 point nudge, i.e. <= 10 percent, applied to candidate selection for the group, outcome, subject-id and survival roles) – the reported confidence stays the mathematical signature. See rolescry_default_name_bonus() for a ready-made dictionary.

A column can hold more than one role (e.g. a measurement in both paired_pairs and agreement_pairs). The covariate role is residual: it lists the continuous, categorical and binary columns that no other role claimed. A role whose evidence does not single out a column is reported as not found rather than guessed.

The coupled_pair role (0.3.0) reports a dependent pair of one binary and one positive continuous column whose reading is ambiguous: it may be a survival (time, event) pair or a (cause, binary outcome) pair, and the data alone cannot say which. The survival roles are assigned instead only when censoring geometry singles the survival reading out. The entry carries continuous_col, binary_col and the two interpretations; the choice is the user's.

The time_index role (0.3.0) reports calendar columns: Date/POSIXct classes, or character columns whose every value parses as a date. The class and format are properties of the values, so name-blindness is preserved. On a balanced long panel whose entity/time axes are value-symmetric, the axes are reported as ambiguous (repeated_measures$panel_axes) unless serial dependence of the measures singles one reading out.

Value

An S3 object of class "role_detection": a list with

var_info

data.frame(column, type) – value-based column typing.

roles

named list; each entry has found, columns, score, max_score, pct, detected_by and a components score breakdown. Meaning of score/pct per role: group_var: score = pct = 100 * B (normalized-entropy ranking; max_score = 100; there is no description-length test). Paired/agreement pairs, ordinal scale items and the outcomes: score is in bits, max_score is NA, and pct is the display map 100 * (1 - 2^(-bits/8)). Survival, wide repeated measures and binary scale items (ported point scorers): score and max_score are points and pct is the points ratio.

value_types

named character vector of per-column value-type labels.

potential_pairs

list of candidate continuous column pairs with MDL pair bits and shift bits. The paired/agreement split is the sign of the shift bits, a BIC-type test of a mean difference between the two columns: agreement_pairs means "no detectable systematic shift", so a method-comparison pair with a real calibration bias is labeled paired_pairs, and the label depends on n. Exactly one pair is reported across the two roles.

n_obs, n_var

dataset dimensions.

See Also

read_data(), compute_nmi(), rolescry_default_name_bonus()

Examples

set.seed(1)
d <- data.frame(
  arm  = rep(c(0, 1), each = 50),
  pre  = rnorm(100, 10, 2),
  post = rnorm(100, 11, 2),
  resp = rbinom(100, 1, 0.4)
)
res <- detect_roles(d)
res
res$roles$group_var$columns


Align detected variable roles across sites

Description

Runs detect_roles() name-blind on each site and reports, per role, which column each site holds, plus descriptive consistency checks (value type, event coding, group-label overlap, range overlap, battery size). Alignment is by role only; column names appear only in the returned cells. The residual covariate role is excluded from map, checks and site_only. aligned means the detector found the role at every site; it does not establish that the columns measure the same thing; read checks (levels, Jaccard, ranges) before pooling. Cell order is alphabetical and carries no pairing direction.

Usage

harmonize_roles(sites)

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

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

Arguments

sites

A named list (unique, non-empty names) of at least two data.frames.

x, object

A role_harmonization object.

...

Ignored.

Value

An object of class "role_harmonization": a list with map (role, status, one column per site), checks (role, check, one column per site, flag), site_only (site, role, columns), n_sites and sites. Flags are type_mismatch, coding_mismatch and size_mismatch; no numeric threshold decides a flag. summary returns map.

Examples

set.seed(1)
mk <- function(n) data.frame(id = seq_len(n), arm = sample(c("a", "b"), n, TRUE),
                             x = rnorm(n), y = rnorm(n))
harmonize_roles(list(north = mk(60), south = mk(80)))

Read a data file with automatic header detection

Description

Reads a tabular file into a data.frame, detecting the header row with detect_header() (the data is first read with no header so the header row is visible as data). Delimited text (.csv/.tsv) is read with base R and always works; spreadsheet and statistical formats use optional packages and degrade gracefully with an actionable error if the package is absent.

Usage

read_data(
  path,
  header = NULL,
  sheet = NULL,
  na_strings = c("", "NA", "N/A", "n/a", "na", "NULL", "null", "."),
  verbose = FALSE
)

Arguments

path

Path to the file.

header

Optional integer giving the 1-based header row to use directly, bypassing detection. NULL (default) auto-detects.

sheet

Optional sheet name/index for Excel files.

na_strings

Character vector of tokens mapped to NA before type conversion.

verbose

Logical; emit the detected header row via message().

Details

Supported: .csv, .tsv/.tab (base); .xlsx/.xls/.xlsm (Suggests: readxl or openxlsx); .sav/.sas7bdat/.dta (Suggests: haven, header intrinsic); .rds (base, returned as stored).

Value

A data.frame with detected column names and per-column types inferred via type.convert.

See Also

detect_header(), detect_roles()

Examples

tmp <- tempfile(fileext = ".csv")
writeLines(c("age,sex,score", "34,M,8.1", "51,F,7.4"), tmp)
read_data(tmp)
file.remove(tmp)


Default name-bonus keyword dictionary

Description

Returns a ready-made, ASCII-English keyword dictionary suitable for the name_bonus argument of detect_roles(). It externalizes the hard-coded keyword lists that lived inside the original MDStatR engine (group/treatment, outcome, survival-time and event, subject-id terms) into a plain, inspectable, locale-neutral list.

Usage

rolescry_default_name_bonus(domain = c("clinical", "finance", "all"))

Arguments

domain

Keyword domain: "clinical" (default), "finance", or "all" (the union). The dictionaries differ only in vocabulary; the capped tie-break mechanics are identical.

Details

Passing this turns column names into a small, capped tie-breaker only (\le 10 percent of the selection score); the mathematical signature still dominates (>= 90 percent), satisfying the name-blindness contract. Detection without it (name_bonus = NULL) is purely mathematical.

Value

A named list of character vectors (regex fragments), with keys group_var, outcome_continuous, outcome_binary, subject_id, time_variable, event_variable.

Examples

nb <- rolescry_default_name_bonus()
names(nb)
set.seed(1)
d <- data.frame(
  treatment_arm = rep(c("A", "B"), each = 60),
  biomarker     = rnorm(120),
  death         = rbinom(120, 1, 0.3)
)
detect_roles(d, name_bonus = nb)$roles$group_var$columns


Verify a declared analysis plan against the data signature

Description

Checks a user-declared assignment of variable roles against what the data themselves can support. The claim is mapped to column positions once and the data are then examined name-blind (the same information-theoretic signature used by detect_roles()). Each claimed role gets one verdict: contradicted (a falsification backed by a value statistic), consistent (the data support it) or unverifiable (sub_reason identifiability or coverage). Structure the blind detector finds that the claim does not mention is listed separately.

Usage

verify_roles(data, claim)

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

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

Arguments

data

A data.frame with unique, non-empty column names.

claim

A named list. Names are role keys: scalar roles (subject_id, group_var, outcome_continuous, outcome_binary, time_variable, event_variable, time_index) take one column name; set roles (repeated_measures, scale_items, covariate) take one or more; pair roles (coupled_pair, paired_pairs, agreement_pairs) take a length-2 character vector or a list of them.

x, object

A role_verification object.

...

Ignored.

Details

contradicted is issued only for these falsifications, each backed by a value statistic: subject_id whose values repeat while a detected long panel has a different entity cluster; a paired_pairs/agreement_pairs pair that shares no information beyond the independence noise floor, whose members differ more than they agree, or with a constant member; a coupled_pair whose members are independent; an event column that is not a two-level or 0/1/2 coding, or negative follow-up times; repeated_measures that are not the measures of a detected long panel, or a wide set with no common factor; ordinal scale_items with no common factor, or binary items forming a one-hot partition; a time_index that is not a calendar column; a group_var that is all-distinct or calendar; a non-numeric outcome_continuous; an outcome_binary without exactly two observed values. Absence of a time-event association is not a falsification (constant-hazard censoring makes the event independent of the observed time) and is reported as unverifiable(coverage). A consistent pair means the two columns share information and their difference is smaller than their mean. Each claimed pair is judged on its own. A survival claim (time_variable plus event_variable) is one claim shown as two rows and is counted once in counts.

Value

An object of class "role_verification": a list with table (role, columns, verdict, sub_reason, statistic, reason, blind), unclaimed (role, columns, statistic), counts (claims, testable, contradicted, consistent, unverifiable), n_obs and n_var.

Examples

set.seed(1)
rct <- data.frame(id = 1:60, arm = rep(c("a", "b"), 30),
                  pre = rnorm(60), post = rnorm(60))
verify_roles(rct, list(subject_id = "id", group_var = "arm",
                       paired_pairs = c("pre", "post")))

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.