ML Foundations

Catch feature and target truncation with strict zip

Python's default zip stops at the shortest input. Use strict=True when paired ML data must have matching lengths.

Evidence and testing

Review record: Tensor Almanac automated evidence gate (no independent human review). Example versions, recorded outputs and limitations appear below.

Default zip can drop unmatched data

When feature rows and targets are stored in separate iterables, ordinary `zip(features, targets)` pairs entries by position. Python stops the iterator as soon as the shortest input is exhausted; extra items in a longer input are left out without an error. A training dataset assembled this way can therefore omit a tail of feature rows or labels while looking like a normal sequence of pairs. [1]

That behavior is useful when stopping at the shorter input is intentional, and it also works with infinite iterators. It is risky when equal lengths are part of the data contract. A successful loop is not evidence that every row was paired. [1]

References: [1]

Turn equal length into a checked invariant

On Python 3.10 and newer, pass `strict=True` when every input must contribute one item to each tuple. If one iterable ends before another, iteration raises `ValueError` instead of silently returning only the common prefix. The check is lazy: it occurs when iteration reaches the mismatch, so fully consume or materialize the checked pairs before running downstream work that should not start on incomplete data. [1]

The bounded example below uses four feature identifiers and three targets. Ordinary `zip` returns three pairs and leaves one identifier unpaired. Building a list from the strict iterator raises an error before that checked list can be used.

Executed with Python 3.12.14 standard library only on 2026-10-22.

"""Show how default zip can truncate mismatched feature and target lists."""

feature_ids = ["row-0", "row-1", "row-2", "row-3"]
targets = ["cat", "dog", "cat"]

ordinary_pairs = list(zip(feature_ids, targets))
print("default pairs:", ordinary_pairs)
print("unpaired feature IDs:", feature_ids[len(ordinary_pairs):])

try:
    checked_pairs = list(zip(feature_ids, targets, strict=True))
except ValueError as error:
    print("strict check:", error)
else:
    print("strict pairs:", checked_pairs)

assert len(ordinary_pairs) == 3
assert feature_ids[len(ordinary_pairs):] == ["row-3"]

Recorded output

default pairs: [('row-0', 'cat'), ('row-1', 'dog'), ('row-2', 'cat')]
unpaired feature IDs: ['row-3']
strict check: zip() argument 2 is shorter than argument 1

References: [1]

Use a different rule when lengths may differ

Strict pairing is appropriate only when equal length is genuinely required. If unequal inputs are expected and missing entries need explicit placeholders, Python provides `itertools.zip_longest`, which continues through the longest input and fills exhausted inputs with a chosen value. [2]

For large streams, remember that strict zip detects a mismatch during consumption rather than pre-scanning both streams. If an earlier pair has already triggered an external side effect, the later error cannot undo it. Validate sized collections before side effects, or make the consumer treat a mismatch as a failed batch. These are operational consequences of the iterator behavior, not guarantees that a later model or data-quality check will catch semantic misalignment.

References: [1], [2]

Sources

  1. Python built-in functions: zip
  2. Python itertools: zip_longest