Skip to content

Commit cee1823

Browse files
committed
_
1 parent aeac521 commit cee1823

1 file changed

Lines changed: 30 additions & 14 deletions

File tree

skrub/_data_ops/_estimator.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,17 @@ def report(self, *, environment, mode, **full_report_kwargs):
289289
self._set_is_fitted(mode)
290290
return result
291291

292-
def _score(self, environment):
292+
def _score(self, environment, return_predictions=False):
293293
score_node = find_scoring_node(self.data_op)
294294
if score_node is None:
295-
return self._eval_in_mode("score", environment)
295+
result = self._eval_in_mode("score", environment)
296+
return (result, {}) if return_predictions else result
296297
estimator = self.__skrub_to_Xy_pipeline__(environment)
297298
cv_data = _compute_X_y_and_cv(self.data_op, environment)
298299
X, y = cv_data["X"], cv_data.get("y")
299-
return estimator._score(X, y, cast_to_float=False)
300+
return estimator._score(
301+
X, y, cast_to_float=False, return_predictions=return_predictions
302+
)
300303

301304
def __getattr__(self, name):
302305
if name not in supported_modes(self.data_op):
@@ -899,38 +902,51 @@ def _process_scores(scorer_info, scorer_output):
899902
name = "score"
900903
return [(name, scorer_output)]
901904

902-
def _score(self, X, y=None, cast_to_float=True):
905+
def _score(self, X, y=None, cast_to_float=True, return_predictions=False):
903906
score_node = find_scoring_node(self.data_op)
904907
if score_node is None:
905-
return self._eval_in_mode("score", X, y)
908+
result = self._eval_in_mode("score", X, y)
909+
return (result, {}) if return_predictions else result
906910
env = self._get_env(X, y)
907911
scorers = evaluate(
908912
score_node._skrub_impl.scorers, mode="fit_transform", environment=env
909913
)
910914
all_scores = []
911-
caching_estimator = _CachingXyPipeline(self.data_op, self.environment)
915+
predictions = env.get("_skrub_predictions", {})
916+
cache = {(k, id(X)): v for k, v in predictions.items()}
917+
caching_estimator = _CachingXyPipeline(
918+
self.data_op, self.environment, cache=cache
919+
)
912920
_copy_attr(self, caching_estimator, ["_is_fitted"])
913921
for scorer_info in scorers:
914922
scorer = self._prepare_scorer(scorer_info["scoring"], scorer_info["kwargs"])
915923
scorer_output = scorer(caching_estimator, X, y)
916924
all_scores.extend(self._process_scores(scorer_info, scorer_output))
925+
predictions.update({k: v for ((k, X_id), v) in cache.items() if X_id == id(X)})
917926
rename = unique_renaming()
918927
result = {rename(name): score for name, score in all_scores}
919-
if cast_to_float and len(result) == 1:
928+
if cast_to_float and len(result) == 1 and not return_predictions:
920929
# If there is a single score stick to scikit-learn interface which
921930
# returns a number.
922931
return next(iter(result.values()))
923-
return result
932+
return (result, predictions) if return_predictions else result
924933

925934

926935
class _CachingXyPipeline(_XyPipeline):
936+
def __init__(self, data_op, environment, cache):
937+
super().__init__(data_op, environment)
938+
self.cache = cache
939+
927940
def _eval_in_mode(self, mode, X, y=None):
928-
if not hasattr(self, "_cache"):
929-
self._cache = {}
930-
key = (mode, id(X), id(y))
931-
if key not in self._cache:
932-
self._cache[key] = super()._eval_in_mode(mode, X, y=y)
933-
return self._cache[key]
941+
if y is not None:
942+
return super()._eval_in_mode(mode, X, y=y)
943+
key = (mode, id(X))
944+
if key not in self.cache:
945+
self.cache[key] = super()._eval_in_mode(mode, X, y=y)
946+
return self.cache[key]
947+
948+
def _score(self, X, y=None):
949+
return super()._eval_in_mode("score", X, y=y)
934950

935951

936952
def _compute_X_y_and_cv(data_op, environment):

0 commit comments

Comments
 (0)