Mask Engine
The mask engine provides boolean masking primitives for use inside
calculate_feature(). It lets a FeatureGroup selectively include or exclude
values without removing rows from the output.
For user-provided filter propagation (GlobalFilter, SingleFilter, row elimination), see Filter Data.
How the mask engine is wired
Each ComputeFramework provides a BaseMaskEngine subclass via its
mask_engine() classmethod. The framework sets features.mask_engine on the
FeatureSet before calculate_feature() is called. This wiring is independent
of features.filters and final_filters().
@classmethod
def calculate_feature(cls, data, features: FeatureSet):
engine = features.mask_engine # always available when the framework provides one
mask = engine.equal(data, "status", "active")
# ... apply mask
Primitives
Every BaseMaskEngine subclass implements these abstract classmethods:
| Method | Returns mask where... |
|---|---|
supported_data_type() |
(not a mask) the data container type this engine handles, e.g. pd.DataFrame |
equal(data, column, value) |
data[column] == value; None matches null and NaN rows |
greater_equal(data, column, value) |
data[column] >= value |
greater_than(data, column, value) |
data[column] > value |
less_equal(data, column, value) |
data[column] <= value |
less_than(data, column, value) |
data[column] < value |
is_in(data, column, values) |
data[column] is in values (list, tuple, set or frozenset); any other type (a string, a generator, a NumPy array or pandas Series) raises TypeError; an empty values matches nothing, and a None in values matches null and NaN rows |
combine(mask1, mask2) |
logical AND of two masks |
all_true(data) |
all True (no filtering), boolean-typed even at zero rows |
A null or NaN row never matches (a mask holds False there) unless the call targets them, as
equal(..., None) and is_in with None do; a NaN value counts as None; SQL conditions yield
NULL, which is treated as no match. The engines do not tell NaN from null, so use the framework's
native API directly when that distinction matters. data is the frame or relation being masked,
never None; the Polars lazy and DuckDB engines raise TypeError on None in the calls that
read its column types. When data is given, SQL mask engines raise ValueError if column is not an exact entry of data.columns (the check is skipped for None).
Convenience methods
Built from the primitives above. No per-engine implementation needed.
between(data, column, min_value, max_value, *, min_exclusive=False, max_exclusive=False)
Range check combining a lower and upper bound:
# Inclusive: value in [10, 100]
mask = engine.between(data, "value", 10, 100)
# Exclusive bounds: value in (10, 100)
mask = engine.between(data, "value", 10, 100, min_exclusive=True, max_exclusive=True)
# Half-open: value in [10, 100)
mask = engine.between(data, "value", 10, 100, max_exclusive=True)
all_of(data, masks)
AND-combine a list of masks. Returns an all-True mask if the list is empty.
mask = engine.all_of(data, [
engine.equal(data, "status", "active"),
engine.between(data, "value", 10, 100),
])
Masking patterns
Inline masking, skip row elimination
Set final_filters() = False to tell the framework not to remove rows.
Use the mask engine to selectively null out values before aggregation.
class MaskedRegionSum(FeatureGroup):
@classmethod
def final_filters(cls) -> bool:
return False # skip row elimination
@classmethod
def calculate_feature(cls, data, features: FeatureSet):
engine = features.mask_engine
mask = engine.equal(data, "status", "active")
masked = pc.if_else(mask, data["sales"], None)
# aggregate masked values, broadcast back to all rows
return pa.table({cls.get_class_name(): broadcast_sum(masked, data["region"])})
Result: all rows preserved, but only matching values contributed to the sum.
Inline masking + row elimination
Use the mask engine for conditional logic and return final_filters() = True
so the framework also eliminates non-matching rows afterward. You must preserve
the filter column in your output (see the
overlap contract in the filter docs).
class MaskedRegionSumActiveOnly(FeatureGroup):
@classmethod
def final_filters(cls) -> bool:
return True # also eliminate non-matching rows
@classmethod
def calculate_feature(cls, data, features: FeatureSet):
engine = features.mask_engine
mask = engine.equal(data, "status", "active")
masked = pc.if_else(mask, data["sales"], None)
sums = broadcast_sum(masked, data["region"])
# Return all rows; the framework will remove non-matching ones
return pa.table({
cls.get_class_name(): sums,
"status": data["status"], # preserve the filter column
})
Result: only matching rows remain, with aggregated values computed from masked data.
Pipeline data flow (inline masking)
Input:
region | status | value
A | active | 10
A | inactive | 20
B | active | 30
B | inactive | 40
Mask predicate: status == "active"
| Stage | Data |
|---|---|
Use features.mask_engine |
engine.equal(data, "status", "active") |
| Build mask | [True, False, True, False] |
| Apply mask to value column | masked_value = [10, NULL, 30, NULL] |
| Aggregate (sum by region, broadcast) | Region A: 10, Region B: 30 |
| Final result | [10, 10, 30, 30] (4 rows, all preserved) |
The framework skips run_final_filter() because final_filters() = False.