CLI Output Formats¶
The scoring and saved-score commands support human-readable summaries, interoperable text formats, and lossless NumPy archives:
| Format | Destination | Best for |
|---|---|---|
text |
file, compressed file, or standard output | Interactive review |
json |
file, compressed file, or standard output | Structured metadata and web tooling |
csv |
file, compressed file, or standard output | Row-oriented statistics workflows |
npz |
explicit .npz file |
Fast, typed Python and NumPy workflows |
CSV rows and JSON respondent arrays are written forward-only with bounded output
allocation. JSON converts non-finite numbers to null, and CSV emits empty
cells. NPZ preserves NumPy dtypes and non-finite values exactly without object
arrays or pickling. For text formats, .gz, .bz2, and .xz suffixes select
gzip, bzip2, and XZ compression from the Python standard library. XZ output uses
its fast low-memory preset to limit compression workspace.
Text previews honor --top N (default 10). Small previews select respondents
in bounded batches instead of sorting every result. Screening ranks higher
flag counts first, composite output ranks higher scores first, and response-time
output follows its suspicious tail. Equal ranking values retain original input
row order, including ties at the preview boundary. Composite and timing previews
omit non-finite scores. Use --top 0 for summary metadata without respondent rows.
Regular output files in every format are completed in a temporary directory beside their destination, then replaced atomically. Serialization failures, compression-finalization failures, and handled interruptions leave previous results intact and clean up staged output. Successful writes retain existing permission bits and update symbolic-link targets without replacing the links. For compressed output through a symbolic link, the requested path's suffix selects the compressor. Standard output, named pipes, and device paths stream directly and can contain partial output after a failure.
Screen and composite commands report soft per-index failures on standard error
without mixing warnings into standard output. Their JSON results retain an
errors object, while NPZ archives use aligned error_names and
error_messages vectors. Text repeats the failures inline. CSV stays
row-oriented and carries no global metadata, so retain standard error when its
diagnostics matter.
NumPy archives¶
Write archives from any scoring command:
ier screen responses.npy --indices irv longstring --format npz --output screening.npz
ier screen responses.npy --indices irv longstring --format npz --compress --output compact.npz
ier composite responses.csv --format npz --output composite.npz
ier response-time timings.csv --format npz --output timing.npz
NPZ output requires --output with a .npz suffix. It cannot target standard
output or an additional compression layer; the NPZ container is already a ZIP
archive.
Members are uncompressed by default for speed. Add --compress to any fresh or
saved-score command to use streaming DEFLATE compression inside the NPZ
container; this option requires --format npz. Compression preserves every
array and metadata field and requires no schema change or special loading
option. Repeated scores, flags, and IDs can become much smaller, while
continuous scores often compress modestly and take more CPU to write and load.
--compress uses DEFLATE level 1 by default; add --compress-level 1-9 to
trade write time for size (9 is smallest). Any level loads the same way.
Archives use the same atomic file replacement as the text formats.
Load every archive with pickling disabled:
import numpy as np
with np.load("screening.npz", allow_pickle=False) as result:
print(result["schema_version"].item())
print(result["result_type"].item())
print(result["index_names"])
irv_scores = result["score__irv"]
irv_flags = result["flag__irv"]
For validated score reuse, prefer the public save/load pair. The writer creates a compact score-only archive; full CLI archives retain the additional flags and decision metadata documented below.
from ier import load_score_archive, save_score_archive, screen_scores
save_score_archive(
"raw-scores.npz",
{"irv": [0.1, 0.7], "longstring": [3.0, 8.0]},
respondent_ids=["case-1", "case-2"],
compressed=True,
)
saved = load_score_archive("raw-scores.npz")
updated = screen_scores(
saved["scores"], errors=saved["errors"], n_respondents=saved["n_respondents"], percentile=99
)
print(saved["respondent_ids"])
print(saved["errors"])
save_score_archive() validates the destination, result type, registered index
names, aligned vectors, optional IDs, and soft failures before opening the file.
It streams compatible arrays without constructing a respondent-by-index matrix;
the shared atomic boundary protects the destination from later I/O failures.
Every public archive writer accepts the Boolean compressed option, which
defaults to False, and an optional compression_level from 1 to 9 for
compressed output. Archive respondent IDs and error
messages cannot end with a NUL character because fixed-width NumPy Unicode
would silently truncate that character; embedded NULs are preserved. Invalid
Unicode codepoints in external archive metadata produce a contextual error.
load_score_archive() always disables pickling and validates the complete
schema. It accepts compact public archives, screen CLI archives, and composite
CLI archives written with --include-components. Aggregate-only composite and
response-time archives do not contain reusable registered-index vectors and are
rejected with a contextual error.
Screen archives can also retain a run where every selected index failed. The
stored respondent count, identifiers, and nonblank failure messages remain
available even though scores is empty. Supply n_respondents explicitly to
save_score_archive() and screen_scores() for this case. screen-scores uses
the archived count automatically and emits aligned zero-coverage results in every
format; --strict still rejects retained failures. Composite reuse requires
at least one available component vector.
Reuse those scores directly from the CLI for sensitivity analysis:
ier screen-scores screening.npz --index-percentile irv=99 --min-flags 2 --format json
ier composite-scores screening.npz --indices irv longstring --weight irv=2 \
--include-components --percentile 95 --format npz --output reweighted.npz
These commands never read the original responses or run an index. They apply new
decisions to raw scores, preserve respondent IDs, and support every output
format above. Defaults use all saved scores; --indices selects and orders a
subset of available scores and excludes unselected failures. Selecting a failed
or absent index produces an error explaining why no scores are available.
Without a subset, historical index failures are retained in output and reported
on standard error; --strict rejects them before writing. Composite output
retains only failures for composite-enabled indices, although strict validation
still audits every failure before conversion. Saved screening-only scores must
be excluded from composites with --indices or skipped with --skip-unsupported,
which names the skipped indices on standard error.
Input matrix options, worker counts, and best_subset are unavailable for saved
scores. Select the desired component names explicitly. Composite archives need
--include-components when they are first written to remain reusable. Input and
output may use the same archive path; replacement occurs only after the new
result is complete.
Common schema¶
All archives use schema version 1 and include:
| Key | Value |
|---|---|
schema_version |
Integer scalar |
result_type |
screen, composite, or response_time |
n_respondents |
Integer scalar |
respondent_ids |
Optional Unicode vector when --id-column is used |
Row positions are respondent identifiers when respondent_ids is absent.
Screen schema¶
screen archives include n_indices, min_flags, index_names, thresholds,
threshold_sources, percentiles, flag_counts, valid_index_counts,
consensus_eligible, and consensus_flags.
When --min-valid-indices is supplied, the scalar min_valid_indices records
the completeness requirement. Numeric thresholds and percentile settings align
with index_names; NaN represents a fixed/presence rule without a percentile,
or a presence-based index without a numeric threshold. threshold_sources
distinguishes those cases. JSON exposes equivalent mappings and uses null for
unset values. CSV includes valid_index_count and consensus_eligible columns
for every respondent but intentionally omits global cutoff metadata.
Each successful index has a score__NAME float vector and flag__NAME boolean
vector. Summary values use summary_columns, summary_statistics,
summary_n_flagged, summary_n_valid, summary_n_unavailable, and
summary_flag_rate. The rate uses valid scores as its denominator and is NaN
when an index has no available scores. JSON exposes the same values inside each
index summary and uses null for an unavailable rate. Soft failures are stored
in aligned error_names and error_messages Unicode vectors.
save_screen_archive() writes this schema from Python, and
load_screen_archive() restores the complete ScreenResult from either writer.
The loader recomputes each flag__NAME vector from its score, threshold, and
source, then checks the respondent counts, consensus decisions, and summary
counts, so the restored result can be plotted or reported without rescoring.
Composite schema¶
composite archives include the method string scalar, standardized boolean
scalar, and respondent-aligned scores float vector. When --weight is
supplied, aligned weight_names and weights vectors record the explicit
overrides; selected indices not listed there use weight 1. When
--min-valid-indices is supplied, min_valid_indices records the integer
completeness requirement. JSON composite output uses equivalent method,
standardized, and optional weights and min_valid_indices fields. Text also
records the effective standardization setting. Aligned error_names and
error_messages vectors preserve soft failures; JSON uses the errors object.
When --threshold or --percentile is supplied, NPZ and JSON add threshold,
threshold_source, and respondent-aligned boolean flags. Percentile output
also records the requested percentile. CSV adds composite_flag, while text
reports the cutoff, source, flagged count, and row-level flag. These fields are
absent when flagging is not requested.
With --include-probability, NPZ and JSON add respondent-aligned
probabilities plus probability_scale="uncalibrated_logistic". CSV adds
composite_probability, and text adds the value to each ranked row. The vector
is transformed from the already-computed aggregate scores without rescoring
indices. It is absent by default. Flag thresholds remain in the original
composite-score units even when probability output is included.
With --include-components, composite NPZ adds index_names,
valid_index_counts, and one score__NAME float vector per successful index.
JSON adds indices_used, valid_index_counts, and a component_scores object;
CSV adds valid_index_count and NAME_score columns; text shows the same fields
for ranked respondents. These are raw public index scores before direction
correction, standardization, and weighting. The aggregate scores vector is
unchanged.
Response-time schema¶
response-time archives include metric, flag_direction, threshold,
respondent-aligned scores, and boolean flags.
metric |
flag_direction |
Scores |
|---|---|---|
mean, median, sd, min |
low |
Per-respondent timing summaries |
consistency |
low |
Coefficients of variation |
mixture |
high |
Fast-component probabilities |
effort |
low |
Response time effort (RTE) proportions |
effort extends the allowed metric values without a schema change, so archives
from earlier releases load unchanged; earlier readers reject only the new
metric value. Fixed effort cutoffs, including the default 0.90, flag RTE
strictly below the threshold and verify under the tie-exclusive rule. An effort
archive's threshold and every available score must lie between 0 and 1, the
range of RTE, so an item time saved as an effort cutoff is rejected.
Load and reflag the retained scores through the public validated boundary:
from ier import (
load_response_time_archive,
response_time_score_flags,
save_response_time_archive,
)
saved = load_response_time_archive("timing.npz")
revised = response_time_score_flags(
saved["scores"],
threshold=1.0,
direction=saved["flag_direction"],
)
save_response_time_archive(
"revised-timing.npz",
saved["scores"],
revised,
threshold=1.0,
metric=saved["metric"],
flag_direction=saved["flag_direction"],
respondent_ids=saved["respondent_ids"],
)
load_response_time_archive() disables pickling and verifies schema version,
metric and direction compatibility, finite cutoff metadata, the RTE range of
effort cutoffs and scores, aligned score and flag vectors, optional respondent
identifiers, and agreement between stored flags and their cutoff rule. It accepts both inclusive fixed-threshold flags and
tie-exclusive percentile flags. response_time_score_flags() includes ties at a
fixed cutoff; to apply the strict effort rule to a new cutoff in Python, compare
the saved effort scores directly, as in saved["scores"] < 0.8, which never
flags unavailable scores.
save_response_time_archive() writes the same CLI-compatible schema and checks
all scores, Boolean flags, cutoff metadata, direction rules, and optional
identifiers before opening the destination. It streams the validated vectors
without stacking or adding a runtime dependency.
Reflag timing archives or convert them to another output format from the CLI:
ier response-time-scores timing.npz --threshold 1.0 --format csv --output revised.csv
ier response-time-scores timing.npz --percentile 1 --format npz --output stricter.npz
ier response-time-scores timing.npz --format json --output timing.json.gz
ier response-time-scores effort.npz --threshold 0.8 --format json
response-time-scores validates the archive before using its retained scores,
metric, suspicious-tail direction, and respondent IDs. It never reads the
original timing matrix or refits a mixture. Direct metrics, consistency, and
effort use the low tail; mixture probabilities use the high tail. Choose either
--threshold for an inclusive fixed cutoff or --percentile for a strict
sample-percentile cutoff that excludes ties. Effort is the exception to the
inclusive fixed rule: a fixed RTE cutoff must lie between 0 and 1 and flags
scores strictly below it, on both timing commands. These options are mutually
exclusive on both timing commands.
With neither cutoff option, the saved threshold and exact flags are preserved,
including decisions for scores tied at the cutoff. This allows conversion to
JSON, CSV, or text without changing the original decision. All output formats,
compressed text destinations, and --top previews are supported. The input and
NPZ output may share a path; atomic replacement protects the original archive
until the revised result is complete. Matrix selection and metric-fitting
options are unavailable for saved timing scores.
Consumers should reject unsupported future schema_version values rather than
assuming their layout is unchanged.
Input inspection¶
ier inspect DATA loads a response matrix with the same input options as the
scoring commands and reports how it was parsed, without scoring any index:
ier inspect export.csv --id-column ResponseId --item-pattern 'Q*' --missing-value NA
ier inspect responses.csv --format json --output inspection.json
Text output is a short summary that lists at most ten item names and the ten
most-missing items. --format json writes one object with every item, and
--output writes either format with the same atomic replacement and .gz,
.bz2, or .xz compression as result files.
| Key | Value |
|---|---|
source |
Input path, or standard input |
input_format |
delimited or npy |
delimiter |
Field delimiter; null for whitespace-separated or .npy input |
delimiter_detection |
explicit (--delimiter), sniffer (comma, tab, or semicolon found by csv.Sniffer), fallback (chosen from the first records' fields), or whitespace; null for .npy input |
header |
present (declared with --header present or implied by named columns), auto-detected (non-numeric first cell), or absent |
id_column |
--id-column name, or null |
n_columns |
Input columns, including identifier and unselected columns |
n_respondents, n_items |
Shape of the matrix that scoring commands would use |
item_names |
Selected header names in scoring order, or null without a header |
item_positions |
1-based input column of each selected item |
missing_cells, missing_by_item |
Missing cells overall and for each selected item |
infinite_cells |
Infinite cells, which are left out of the value summary |
observed_min, observed_max |
Smallest and largest finite responses, or null when there are none |
distinct_values |
Number of distinct finite responses |
non_integer_values |
Whether any finite response has a fractional part |
respondents_at_min, respondents_at_max |
Respondents with at least one response at each observed extreme |
suggested_options |
--scale-min=VALUE and --scale-max=VALUE arguments for the observed extremes, or an empty list |
warnings |
Human-readable cautions about the observed scale |
When --scale-min and --scale-max are omitted, scale-aware indices infer the
response scale from the observed extremes, so suggested_options reproduces
the bounds a scoring command would use. Pass the bounds explicitly when the
questionnaire's scale is wider. Each entry is one complete argument that
scoring commands accept as written; the = form keeps a negative value in
exponent notation, such as -1e-05, from being read as another option.
warnings notes an observed extreme used by
fewer than 1% of respondents, a single distinct value, no finite responses, or
infinite cells.
Index catalog¶
ier indices lists the registry metadata that index_catalog() returns, one
entry per registered index in registry order:
ier indices
ier indices --format json --output indices.json
ier indices --format csv --output indices.csv
| Key | Value |
|---|---|
flag_direction |
high or low: the suspicious tail of the index |
flag_mode |
percentile for tail cutoffs, or present when any available score flags |
default_screen |
Whether screen scores the index when --indices is omitted |
default_composite |
Whether composite includes the index when --indices is omitted |
composite_enabled |
Whether composites accept the index |
required_options |
IndexOptions fields that must all be set before the index can run |
alternative_options |
Groups of interchangeable IndexOptions fields; at least one field in each group must be set |
uses_keyed_responses |
Whether the index reads responses with --reverse-keyed-items reverse-scored |
JSON writes {"n_indices": N, "indices": {NAME: METADATA}}, with
required_options as an array of field names and alternative_options as an
array of arrays, for example
[["infrequency_expected_responses", "infrequency_acceptable_ranges"]]. CSV
writes an index column followed by these keys, with True/False booleans,
comma-separated required_options, and alternative_options groups separated
by ; with the fields inside a group separated by |; an empty cell means no
options. Text writes the same rows tab-separated under the shorter headings
index, direction, flag_mode, screen_default, composite,
composite_default, required_options, alternative_options, and
keyed_responses, with
yes/no booleans and - for no options. --output accepts the same .gz,
.bz2, and .xz suffixes and atomic replacement as result files.