Skip to content

Processors

Every name on this page is importable from sci_etl_core.processors.

sci_etl_core.processors.base

Processor

Bases: ABC

Contract for one post-processing step over the exported table.

process abstractmethod

process(frame: DataFrame) -> DataFrame

Transform a dataframe and return the result.

ProcessorChain

ProcessorChain(steps: list[Processor])

Bases: Processor

Run processor steps in order, each on the previous step's output.

process

process(frame: DataFrame) -> DataFrame

Return frame after every step, or frame itself when there are no steps.

sci_etl_core.processors.normalization

KeyNormalizer

Bases: ABC

Contract for mapping an entity's name to the key its duplicates share.

normalize abstractmethod

normalize(raw_value: Any) -> str

Produce a canonical key used to match duplicate records.

Returns "" when no key can be formed; callers treat an empty key as "no identity" and never match on it.

DefaultKeyNormalizer

Bases: KeyNormalizer

Domain-agnostic key: case, width, spacing, and punctuation differences are ignored.

normalize

normalize(raw_value: Any) -> str

Casefold and NFKC-fold a value, keeping letters, marks, numbers and symbols.

Missing values and containers such as lists or dicts cannot form a key and normalize to "".

NormalizationStep

NormalizationStep(
    key_column: str,
    normalizer: KeyNormalizer,
    output_column: str = "_norm_key",
)

Bases: Processor

Add output_column holding normalizer's key for each row's key_column value.

process

process(frame: DataFrame) -> DataFrame

Return a copy of frame with the key column added.

Raises:

Type Description
KeyError

frame has no key_column.

sci_etl_core.processors.dedup

NeighborMatcher

Bases: ABC

Contract for finding near-duplicate rows that do not share a normalized key.

find_matches abstractmethod

find_matches(
    frame: DataFrame, threshold: float
) -> list[tuple[int, int]]

Return (keep_index, drop_index) pairs for rows considered duplicates.

DeduplicationStep

DeduplicationStep(
    norm_key_column: str,
    matcher: NeighborMatcher | None = None,
    match_threshold: float = 0.0,
    mergeable_columns: list[str] | None = None,
)

Bases: Processor

Collapse rows sharing a normalized key, then merge matched neighbors.

Rows with the same key become one row holding the first non-missing value of each column, ordered by key. Rows whose key is missing or empty carry no identity to match on, so each passes through as its own row, after the keyed rows, instead of being merged with every other keyless row.

Matched pairs only fill the kept row's gaps. When a pair names a row that was already merged away as the one to keep, its values flow into the row that absorbed it, so a chain of matches never discards data.

process

process(frame: DataFrame) -> DataFrame

sci_etl_core.processors.clustering

FeatureExtractor

Bases: ABC

Contract for choosing and scaling the numeric features that :class:ClusteringStep clusters on.

extract abstractmethod

extract(frame: DataFrame) -> tuple[ndarray, Index]

Return a feature matrix and the row index it corresponds to.

ClusteringStep

ClusteringStep(
    feature_extractor: FeatureExtractor,
    output_column: str = "cluster_id",
    eps: float = 1.0,
    min_samples: int = 2,
)

Bases: Processor

Label rows with a DBSCAN cluster id. Needs the cluster extra.

process

process(frame: DataFrame) -> DataFrame

Return a copy of frame with output_column added.

Noise points, rows the feature extractor leaves out, and every row when fewer than min_samples rows have features are labeled -1.

sci_etl_core.processors.quality

CompletenessStep

CompletenessStep(
    tracked_fields: list[str],
    output_column: str = "completeness_pct",
)

Bases: Processor

Add output_column with the percentage of tracked_fields each row fills.

process

process(frame: DataFrame) -> DataFrame

Return a copy of frame with the percentage, rounded to one decimal place.

Tracked fields missing from frame are ignored. When none is present, every row scores 0.0.

QualityFlagStep

QualityFlagStep(
    completeness_column: str = "completeness_pct",
    output_column: str = "quality_flag",
    review_threshold: float = 50.0,
)

Bases: Processor

Label each row from its completeness, as computed by :class:CompletenessStep.

process

process(frame: DataFrame) -> DataFrame

Return a copy of frame with the label column added.

A row at 100% is "Confirmed", one at or above review_threshold is "Needs Review", and any other row, including one without a completeness value, is "Low Confidence".

Raises:

Type Description
KeyError

frame has no completeness_column.

sci_etl_core.processors.validation

RecordValidator

Bases: ABC

Contract for a domain rule that decides whether an extracted entity is kept.

is_valid abstractmethod

is_valid(record: dict[str, Any]) -> bool

Return whether a raw extracted record should be kept.

KeywordExclusionValidator

KeywordExclusionValidator(
    key_field: str, forbidden_keywords: list[str]
)

Bases: RecordValidator

Reject an entity whose key_field is null-like or contains a forbidden keyword as whole words.

is_valid

is_valid(record: dict[str, Any]) -> bool

Return False for a missing, empty, or null-like key, or one containing a forbidden phrase.

Null-like values are null, none, unknown, n/a, and nan, in any case. Keys and keywords are split into runs of letters and runs of numbers after NFKC case folding, and a keyword matches only a contiguous sequence of whole runs, so "star" never matches "starburst".

NumericRangeValidator

NumericRangeValidator(
    field_ranges: dict[str, tuple[float, float]],
)

Bases: RecordValidator

Reject an entity whose value for a field lies outside that field's inclusive range.

is_valid

is_valid(record: dict[str, Any]) -> bool

Return False when a present value is out of range or cannot be read as a number.

A missing or None value passes, so completeness is left to other steps.

CompositeValidator

CompositeValidator(validators: list[RecordValidator])

Bases: RecordValidator

Keep an entity only when every validator keeps it.

is_valid

is_valid(record: dict[str, Any]) -> bool

Return whether all validators accept record, stopping at the first rejection.

sci_etl_core.processors.shaping

ValueClipStep

ValueClipStep(bounds: Mapping[str, tuple[float, float]])

Bases: Processor

Clamp numeric columns into closed ranges during post-processing.

Each column named in bounds is converted to numbers, with values that are not numeric becoming NaN, and then clipped to (low, high). This is the post-processing counterpart of the CSV exporter's numeric_clip. Columns missing from the frame are skipped, and the input frame is never modified.

Store the bounds to apply.

Raises:

Type Description
ValueError

A bound is NaN, or its low end exceeds its high end.

process

process(frame: DataFrame) -> DataFrame

TableLayoutStep

TableLayoutStep(
    sort_by: Sequence[tuple[str, bool]] = (),
    leading_columns: Sequence[str] = (),
    hidden_prefixes: Iterable[str] = (),
    reset_index: bool = False,
)

Bases: Processor

Sort rows and arrange columns for a published table.

Rows are sorted by the sort_by columns that exist in the frame, each ascending or descending as paired, with missing values last and ties kept in their input order. Columns whose name starts with any of hidden_prefixes are dropped, then the leading_columns that exist come first in the given order, followed by the rest in their input order. The input frame is never modified.

Store the layout.

sort_by pairs each column with True for ascending order. reset_index renumbers the rows from 0 after sorting.

Raises:

Type Description
ValueError

A column appears twice in sort_by or in leading_columns, or a hidden prefix is empty, which would hide every column.

process

process(frame: DataFrame) -> DataFrame