Skip to contents

Read-only, pure companion to [`.binary_columns_to_dataset()`] / [`.aggregated_columns_to_dataset()`]. Runs the *same* scanning rules those loaders use internally (including their real dedup-via-`unique()` and blank-identifier-row skipping) and reports what would be silently collapsed or skipped, instead of collapsing/skipping it. It never mutates `headers`/`rows`, never lower-cases an item before recording it, and never calls into `VennDataset` construction – a purely descriptive pass.

Usage

analyze_data_quality(
  headers,
  rows,
  mode = c("binary", "aggregated"),
  prefix_cols = 1L
)

Arguments

headers

Character vector of column headers (as returned by `.parse_table()$headers`).

rows

List of character vectors, one per data row (as returned by `.parse_table()$rows`).

mode

`"binary"` (default) or `"aggregated"`.

prefix_cols

Number of leading metadata columns in binary mode (default 1). Only column 1 is ever read as the item identifier, matching `.binary_columns_to_dataset()`. Ignored when `mode = "aggregated"`.

Value

A list with: * `duplicates_removed` – list of `list(column, column_name, count, examples)` entries, one per column that has duplicates (columns without duplicates are omitted). `column` is the 1-based index into `headers`. `examples` holds up to 5 item strings, each captured at its 2nd occurrence, in encounter order. * `empty_cells_skipped` – single integer count of blank cells found while scanning (scope depends on mode; see divergences above). * `case_collisions` – list of `list(items = character())` entries, each holding 2+ distinct case-sensitive spellings that share a lower-cased form, in first-appearance order. Purely descriptive: item identity is never folded or merged anywhere in this function. * `has_warnings` – `TRUE` if any of the above found something.

Details

## Mirroring R's real loader behaviour (not TS/Python byte-parity)

* **Aggregated mode**: like [`.aggregated_columns_to_dataset()`], a whole trimmed cell is treated as *one* item – there is no delimiter-splitting in R's real loader, so none happens here either. * **Binary mode**: like [`.binary_columns_to_dataset()`], only column 1 is read as the item identifier (any extra `prefix_cols` beyond the first are metadata the loader itself never reads). Rows whose identifier is blank after trimming are skipped **entirely** – not scored for duplicates, case collisions, or empty cells – exactly mirroring `valid_idx` in `.binary_columns_to_dataset()`. Duplicates are scored **per set column** (mirroring the loader's own `unique()` applied independently to each set's truthy identifiers), not as one flat id-column entry – R has no "row contributes if any column is truthy" concept the way the TS array-based loader does, so unlike TS/Python, ALL non-blank-id rows count toward case-collision scope regardless of whether any of their set cells are truthy (this matches `item_order_seen <- unique(item_ids[valid_idx])`, which is computed before/independent of the truthy test).

Examples

headers <- c("Gene", "SetA", "SetB")
rows <- list(c("g1", "1", "0"), c("g1", "1", "1"), c("", "1", "0"))
report <- analyze_data_quality(headers, rows, mode = "binary")
report$has_warnings
#> [1] TRUE