mixle.data package¶
Data layer for mixle – a typed, structure-aware way to get records into the encoder contract.
The core abstraction is DataSource: a lazy, typed (Schema),
structured (SampleStructure) reference to data. It is purely additive –
seq_encode(list) and seq_encode(rdd) are unchanged; a DataSource is simply a third accepted
input that funnels into the same encoder contract. The DataFrame / graph / RDD adapters below remain
input/representation helpers (not probability distributions), so they live outside mixle.stats.
- exchangeability_check(data, *, alpha=0.01, n_perm=200, seed=0, max_records=10_000)[source]
Test whether row ORDER carries information (see module docstring).
alphais deliberately strict (0.01): the check should flag clear violations, not manufacture warnings from noise. A dataset too small to have testing power (n < 20), with no numeric field in its record shape (dict/tuple/list/scalar rows are all supported), or whose only numeric field(s) are entirely non-finite (NaN/Inf) is reportedinconclusive– untested, never a silent, vacuous “exchangeable”. A field containing SOME non-finite values is reportedinvalidand excluded from testing; any other, clean numeric field in the same record shape is still tested normally and can still produce a real verdict.
- class ExchangeabilityReport(label, fields=<factory>, n_examined=0, max_records=None, bounded=False, multiplicity=<factory>)[source]
Bases:
objectThe verdict per numeric field, plus the aggregate label the preconditions record.
- Parameters:
- multiplicity: dict[str, Any]
How the multiplicity across the primary test family was corrected, and how large that family was. Recorded because the label is only interpretable against it (MXR-080-1887).
- property exchangeable: bool
Return
Trueonly when the probes ran and did not reject exchangeability.This is FAILURE TO REJECT, not certification (MXR-080-1887). The probes test for a monotone trend and for a between-halves shift; data can violate exchangeability in ways neither probe looks for, and a real violation can go undetected for want of power.
Truemeans “these tests, at this corrected level, found no order signal” – it is not evidence the assumption holds.multiplicityrecords the correction the verdict was decided under, without which the level is not interpretable.Falseboth for a detectedtrend/shiftAND forinconclusive(nothing could be tested) – a caller that only inspects this boolean can never mistake “we could not check” for “we checked and found no signal”.
- class DataSource(*args, **kwargs)[source]
Bases:
ProtocolA lazy, typed, structured source of encoder-ready records.
- records()[source]
Yield raw records compatible with an encoder’s input type.
- class MaterializedSource(data, structure=EXCHANGEABLE, schema=None)[source]
Bases:
objectAn in-memory
DataSourcewrapping aSequence– what a bare list becomes.- Parameters:
data (Sequence[Any])
structure (SampleStructure)
schema (Schema | None)
- records()[source]
Return conformed records from the owned replayable snapshot.
- materialize()[source]
Return a caller-owned list copy of the conformed snapshot.
- partition(n, *, by=None)[source]
Split into
nstructure-safe sub-sources (group-aware for partially-exchangeable data).
- class LazySource(factory, structure=EXCHANGEABLE, schema=None, length=None)[source]
Bases:
objectA
DataSourcethat defers reading to a records factory and materializes on demand.Connectors (Parquet, SQL, CSV, …) return one of these so
open(...)does no I/O until the data is actually encoded; the records are read (and schema-coerced) once and cached.- Parameters:
factory (Any)
structure (SampleStructure)
schema (Schema | None)
length (int | None)
- materialize()[source]
Return a caller-owned list copy of the immutable, conformed cache.
- records()[source]
Return an iterator over the cached materialized records.
- partition(n, *, by=None)[source]
Materialize and split into
nstructure-aware in-memory sources.
- as_source(data, structure=EXCHANGEABLE, schema=None)[source]
Coerce
datato aDataSource(pass a source through; wrap a sequence as materialized).- Parameters:
data (Any)
structure (SampleStructure)
schema (Schema | None)
- Return type:
DataSource
- open_source(kind, *args, **kwargs)
Open a
DataSourceby connector kind.Examples:
open("csv", path),open("parquet", path, columns=[...]),open("sql", url, query="select ..."). The connector module is imported lazily, and a missing driver raises a clearpip install mixle[extra]message.
- class Schema(fields, container_value=False)[source]
Bases:
objectAn ordered set of typed fields.
- conform_record(record)[source]
Coerce one record (a scalar for a 1-field schema, else a tuple/dict) to the schema’s types.
- conform(records)[source]
Coerce every record in
recordsto this schema (raising clear errors on mismatch).
- static for_model(model)[source]
Best-effort schema a model expects, from its
fields/sources+ child distributions.Formalizes the duck-probe in the DataFrame adapter: a record/composite model exposes
fieldsandsourcesplus child distributions whose support fixes each field’s logical type; a bare leaf yields a single field typed by its support (discrete -> Count, continuous -> Real, …).- Parameters:
model (Any)
- Return type:
Schema
- class Field(name, type)[source]
Bases:
objectA named, typed column.
- Parameters:
name (str)
type (FieldType)
- class FieldType[source]
Bases:
objectBase logical type: a canonical NumPy dtype plus a coercion to the encoder-ready Python value.
- numpy_dtype
alias of
float64
- class Real[source]
Bases:
FieldTypeA real-valued scalar.
- numpy_dtype
alias of
float64
- class Count[source]
Bases:
FieldTypeA non-negative integer count logical type.
- numpy_dtype
alias of
int64
- coerce(value)[source]
Coerce
valueto a non-negative Pythonint, validating exactness first.Bare
int(value)used to silently truncate (1.9->1), silently acceptTrue/False(bool is anintsubclass in Python) as1/0, and silently accept negative values – turning a data problem into a wrong-but-plausible-looking observation instead of an error. A numeric string ("42") is still accepted, since that is common, legitimate connector input (CSV/SQL text columns); a bool, a non-integral value, a negative value, or a non-finite (NaN/Inf) value is rejected instead of silently coerced.
- class Integer[source]
Bases:
FieldTypeA signed integer logical type.
- numpy_dtype
alias of
int64
- class Categorical(categories=None, numpy_dtype=<class 'numpy.object_'>)[source]
Bases:
FieldTypeA categorical label, optionally over a fixed set of
categories.- numpy_dtype
alias of
object_
- class Vector(dim=None, numpy_dtype=<class 'numpy.float64'>)[source]
Bases:
FieldTypeA fixed- or free-length real vector.
- numpy_dtype
alias of
float64
- coerce(value)[source]
Coerce
valueto a one-dimensionalfloat64array and validatedimwhen set.A multi-dimensional input is flattened in C order ONLY when
dimis set and the element count matches it. Declaring the flat length is what makes the reshape explicit rather than silent: sources that emit blocks – PatchSampler hands back apatch_size-shaped patch – have a genuinely rectangular observation whose model-facing form is the flat vector, and with no field kind for N-D arrays the alternative was to refuse them outright. Without a declareddimthe shape is still an error, because then nothing states what length the caller expected and a silent flatten could hide a transposed or mis-sliced block.
- class Timestamp[source]
Bases:
FieldTypeA point in time (datetime / numpy datetime64 / ISO string / POSIX seconds).
- class Optional(inner=<factory>)[source]
Bases:
FieldTypeA value that may be missing (
Nonepasses through; otherwise the inner type coerces).- Parameters:
inner (FieldType)
- property numpy_dtype: Any
Return the NumPy dtype of the wrapped field type.
- class Nested(schema, numpy_dtype=<class 'numpy.object_'>)[source]
Bases:
FieldTypeA sub-record with its own
Schema.- Parameters:
schema (Schema)
numpy_dtype (Any)
- numpy_dtype
alias of
object_
- class SampleStructure(kind, by=None)[source]
Bases:
objectThe joint structure of a dataset’s records (an exchangeability class).
- property strides_records: bool
True if records may be strided/shuffled across partitions (everything but grouped data).
- class GroupingPolicy(policy_id, version, function)[source]
Bases:
objectA callable grouping rule with durable identity and an explicit semantics version.
- partially_exchangeable(by)[source]
Return a
PARTIALLY_EXCHANGEABLEstructure grouped by field name or key functionby.- Parameters:
by (str | GroupingPolicy)
- Return type:
SampleStructure
- grouping_policy(policy_id, version, function)[source]
Bind a callable grouping rule to a stable, versioned provenance identity.
- dataset_hash(data, *, sort=False, max_records=None)[source]
Hex SHA-256 fingerprint of
data(a sequence of records or aDataSource).sort=False(default) is order-sensitive (exact training sequence).sort=Truecombines per-record hashes commutatively for an order-insensitive fingerprint.max_recordstruncates (the count and whether truncation actually happened are both mixed in, so a hash of a truncated prefix never collides with a hash of a genuinely complete dataset of that same visible length).
- model_hash(model)[source]
Hex SHA-256 fingerprint of a fitted model’s parameters (its serialized state).
Stable across processes: hashes the canonical form of
to_serializable(model), so the same model always yields the same hash and two models hash equal iff their serialized parameters match. Used to fingerprint a checkpoint and chain EM iteration lineage (seemixle.inference.production.provenance).
- check_dataset(model, data, *, sample=1000, check_support=True, raise_on_error=False)[source]
Validate
dataagainst the schema/supportmodelexpects (over the firstsamplerecords).Records both type-coercion failures (wrong shape/dtype for a field) and, when
check_supportis True, support violations (a value the model assigns probability 0 ->-inflog-density). Withraise_on_errorthe first batch of issues is raised as aValueError.samplemust be a positive integer. ADataReportis meant to certify that the sampled records were actually inspected, sosample=0used to check nothing and still returnDataReport(ok=True, n_checked=0)– a clean bill of health for data nobody looked at – and a negativesampleused to fail deep insideitertools.islicewith an error that never mentionssampleat all. Both are now rejected clearly, immediately, before any work starts.
- class DataReport(ok, n_checked, schema, issues=<factory>)[source]
Bases:
objectResult of checking sample records against a model-derived schema and support.
- save_encoded(encoded, path, *, encoder=None)[source]
Write
encoded(the output ofencoder.seq_encode(...)) topath; return its hex digest.encoderis required and records the encoder’s versioned structural signature – not just its class name – so a load against a structurally different encoder is flagged. Comparing class names alone would let e.g. a one-fieldCompositeDataEncoderbe accepted for a two-fieldCompositeDataEncoderrequest, since both share the same class name. The returned digest (also stored in the header) covers this signature as well as the body – see_envelope_digest().
- load_encoded(path, *, encoder=None, trusted=None)[source]
Load encoded data written by
save_encoded(), verifying its integrity digest.The header is parsed as JSON (never pickle) and the envelope digest – header fields and body together, see
_envelope_digest()– is checked BEFORE the body is unpickled, so a truncated, corrupted, or header-tampered file is rejected before any deserialization runs. The requiredencoderargument’s structural signature must match the one recorded at save time (elseValueError). This deliberately checks more than the encoder’s class: aDataSequenceEncodersubclass is expected to make__str__structural (mirroring what its__eq__checks) – e.g.CompositeDataEncoder.__str__recurses into its component encoders and so reflects field count, andDiagonalGaussianDataEncoder.__str__includes itsdim– so a shape mismatch under the same class name (e.g. a one-field vs. two-fieldCompositeDataEncoder) is caught, not just a mismatch in class.trusted=Trueis required, and is the caller’s statement thatpathis under their control. The body is pickle, so loading it is equivalent to executing whatever is in the file, and NONE of the checks above change that: the digest is computed from and stored inside the same file, so it detects truncation and header tampering but is worthless against a file replaced wholesale – a forged artifact carries a valid digest for its own contents and a matching encoder signature, and executes on load (MXR-080-1873). The module docstring said as much and the parameter did not exist, so the warning was addressed to whoever read the source rather than to whoever called the function. Making it an argument puts the decision at the call site, where the provenance ofpathis actually known.
- class GraphDataEncoder(directed=False, fallback_assignments=None)[source]
Bases:
DataSequenceEncoderEncode graph observations as canonical adjacency/assignment objects.
- seq_encode(x)[source]
Encode graph-like observations into graph observation records.
- row_count(x)[source]
Return the number of canonical graph observations in an encoded payload.
- class GraphObservation(adjacency, block_assignments=None)[source]
Bases:
objectCanonical binary graph observation used by graph encoders.
- dataframe_records(df, fields=None, as_dict=False, *, _dict_keys='logical')[source]
Convert DataFrame columns into observation records for
seq_encode.A single selected field becomes scalar observations. Multiple selected fields become tuple observations in the requested field order, matching the data shape expected by composite distributions. When
as_dict=True, each row is returned as a mapping keyed by the selected source field names.Missing cells are canonicalized by what the column holds, not by its dtype backend: a gap in a column of numbers (any int/uint/float dtype – numpy, nullable or pyarrow – and an
objectcolumn whose present values are all numbers) becomesNaN; a gap in any other column becomesNone. SoNaN,None,pd.NAandpd.NaTall collapse onto the one marker that column’s numpy-backed spelling would have produced;dfanddf.convert_dtypes()mark every gap the same way, and theOptionalDistributionfitted from either frame carries the samemissing_valueand scores both (campaign four, T2-02). Present values are NOT respelled – anInt64column still yieldsintwhere itsfloat64twin yieldsfloat– so the two frames can still select different families.That covers both directions of the same defect: the numeric one was loud (a ContractError on the whole batch), the string one silent (pandas 3 spells a plain
strcolumn’s gapNaNand its nullable twin’spd.NA, and the mismatched sentinel scored-infper row with no error). Gaps nested INSIDE a cell – a tuple/list/mapping observation – are left as the caller wrote them apart from pandas’ own sentinels; they are the wrapped field’s data, not this column’s gap.
- seq_encode_dataframe(df, fields=None, encoder=None, estimator=None, model=None, num_chunks=1, chunk_size=None)[source]
Sequence-encode selected DataFrame columns with the ordinary stats API.
The frame-shaped counterpart of
mixle.stats.seq_encode(), which consumes RECORDS and refuses a frame outright (“expected a sequence of 2-tuples, got DataFrame”).optimize/fitaccept a DataFrame, so the two entry points disagree about what a dataset is and the conversion is the caller’s job: either encode the frame here, or convert it once withdataframe_records()and hand the records toseq_encodeyourself. Gaps are canonicalized exactly asdataframe_records()canonicalizes them.
- sample_rdd(sc, dist, count_per_split, num_splits, seed=None)[source]
Sample independent draws from a distribution into a Spark RDD.
- sample_seq_as_rdd(sc, dist, seq_len, count_per_split, num_splits, seed=None)[source]
Sample fixed-length sequences from a distribution into a Spark RDD.
- take_sample(rdd, with_replacement, n, seed=None)[source]
Take a deterministic-order random sample from a Spark RDD.
Raises
ValueErrorifwith_replacementisFalseandrddhas fewer thannelements: Spark’s ownRDD.takeSample(False, n, ...)silently caps the result at the RDD’s true cardinality in that case (returning every element instead of the requested count) rather than raising, so this checks the returned length itself and fails loudly with the shortfall instead of silently handing the caller fewer records than they asked for (MXR-080-0066).with_replacementsampling always returns exactlynelements and is unaffected.
Subpackages¶
- mixle.data.sources package
- Submodules
- mixle.data.sources.array_source module
- mixle.data.sources.arrow_source module
- mixle.data.sources.graph_source module
- mixle.data.sources.hadoop_source module
- mixle.data.sources.mongo_source module
- mixle.data.sources.pandas_source module
- mixle.data.sources.spark_source module
- mixle.data.sources.sql_source module
- mixle.data.sources.text_source module
- Submodules