Skip to content

Commit 66164aa

Browse files
author
miranov25
committed
Phase 13.10.ADF: register_evaluator for GroupByRegressionEvaluator integration
register_evaluator(name, evaluator, coord_columns, predictor_columns, overwrite) wraps any object with .evaluate(positions: dict) -> ndarray as an ADF alias function. Enables lazy evaluation and composition via add_alias('dy_corr', 'corr_I1(xM, driftM, dsectorM)'). Multi-predictor: auto-selects single predictor, raises ValueError if multiple without predictor_columns specified. Schema stores interface contract only — evaluator must be re-registered after load. 24 new tests, 1425 passed, 6 failed (pre-existing), no regressions. Review: GPT3 APPROVED.
1 parent 219382f commit 66164aa

2 files changed

Lines changed: 541 additions & 0 deletions

File tree

UTILS/dfextensions/AliasDataFrame/AliasDataFrame.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10175,6 +10175,101 @@ def register_polynomial_from_subframe(self, func_name, poly_spec,
1017510175
'coeff_select': coeff_cols,
1017610176
}
1017710177

10178+
def register_evaluator(self, name, evaluator, coord_columns,
10179+
predictor_columns=None, overwrite=False):
10180+
"""
10181+
Register an evaluator (e.g., GroupByRegressionEvaluator) for use in alias expressions.
10182+
10183+
The evaluator becomes callable in alias expressions using the
10184+
coordinate column names as arguments. This is a thin wrapper around
10185+
register_function() that adapts the evaluator's dict-based interface
10186+
to the positional-array interface used by alias evaluation.
10187+
10188+
Registered function names are in a separate namespace from aliases
10189+
and DataFrame columns — no collision possible.
10190+
10191+
Parameters
10192+
----------
10193+
name : str
10194+
Function name for alias expressions (e.g., 'corr_I1')
10195+
evaluator : object
10196+
Any object with .evaluate(positions: dict) -> np.ndarray or dict.
10197+
Typically GroupByRegressionEvaluator.
10198+
coord_columns : list of str
10199+
Column names that map to evaluator coordinate axes, in order.
10200+
These become the function arguments in alias expressions.
10201+
predictor_columns : list of str, optional
10202+
If evaluator returns dict of multiple predictors, select which
10203+
to return. If None and evaluator returns single predictor, uses it.
10204+
If None and evaluator returns multiple, raises ValueError.
10205+
overwrite : bool, default False
10206+
If True, allow replacing an existing registered function.
10207+
10208+
Raises
10209+
------
10210+
TypeError
10211+
If evaluator has no .evaluate() method.
10212+
ValueError
10213+
If evaluator returns multiple predictors and predictor_columns not set.
10214+
10215+
Example
10216+
-------
10217+
>>> adf.register_evaluator('corr_I1', evaluator, ['xM', 'driftM', 'dsectorM'])
10218+
>>> adf.add_alias('dy_corr', 'corr_I1(xM, driftM, dsectorM)')
10219+
>>> adf.materialize_alias('dy_corr')
10220+
10221+
>>> # Composition with polynomial — via alias chaining:
10222+
>>> adf.add_alias('dy_total', 'polIter0(xM,driftM,dsectorM,tgSlp) + corr_I1(xM,driftM,dsectorM)')
10223+
"""
10224+
col_names = list(coord_columns)
10225+
10226+
if not hasattr(evaluator, 'evaluate'):
10227+
raise TypeError(
10228+
f"Evaluator must have .evaluate(positions) method, "
10229+
f"got {type(evaluator).__name__}"
10230+
)
10231+
10232+
# Capture in closure for alias evaluation
10233+
_evaluator = evaluator
10234+
_col_names = col_names
10235+
_name = name
10236+
_predictor_columns = predictor_columns
10237+
10238+
def _eval_func(*arrays):
10239+
if len(arrays) != len(_col_names):
10240+
raise ValueError(
10241+
f"'{_name}' expects {len(_col_names)} arguments "
10242+
f"({', '.join(_col_names)}), got {len(arrays)}"
10243+
)
10244+
positions = {
10245+
col: np.asarray(arr, dtype=np.float64)
10246+
for col, arr in zip(_col_names, arrays)
10247+
}
10248+
result = _evaluator.evaluate(positions)
10249+
10250+
if isinstance(result, dict):
10251+
if _predictor_columns:
10252+
return result[_predictor_columns[0]]
10253+
elif len(result) == 1:
10254+
return next(iter(result.values()))
10255+
else:
10256+
raise ValueError(
10257+
f"Evaluator '{_name}' returns multiple predictors "
10258+
f"{list(result.keys())}. Specify predictor_columns to select one."
10259+
)
10260+
return result
10261+
10262+
self.register_function(name, _eval_func, overwrite=overwrite)
10263+
10264+
# Store in schema (interface contract only — evaluator not serialized)
10265+
if not self._schema.get('registered_functions'):
10266+
self._schema['registered_functions'] = {}
10267+
self._schema['registered_functions'][name] = {
10268+
'type': 'evaluator',
10269+
'coord_columns': col_names,
10270+
'predictor_columns': predictor_columns,
10271+
}
10272+
1017810273
def draw_help(self, plot_type=None):
1017910274
"""
1018010275
Print dfdraw parameter documentation.

0 commit comments

Comments
 (0)