@@ -342,18 +342,40 @@ def calculate_score(
342342 metric : Scorer ,
343343 scoring_functions : Optional [List [Scorer ]] = None
344344) -> Union [float , Dict [str , float ]]:
345+ """
346+ Returns a score (a magnitude that allows casting the
347+ optimization problem as a maximization one) for the
348+ given Auto-Sklearn Scorer object
349+
350+ Parameters
351+ ----------
352+ solution: np.ndarray
353+ The ground truth of the targets
354+ prediction: np.ndarray
355+ The best estimate from the model, of the given targets
356+ task_type: int
357+ To understand if the problem task is classification
358+ or regression
359+ metric: Scorer
360+ Object that host a function to calculate how good the
361+ prediction is according to the solution.
362+ scoring_functions: List[Scorer]
363+ A list of metrics to calculate multiple losses
364+ Returns
365+ -------
366+ float or Dict[str, float]
367+ """
345368 if task_type not in TASK_TYPES :
346369 raise NotImplementedError (task_type )
347370
348371 if scoring_functions :
349372 score_dict = dict ()
350373 if task_type in REGRESSION_TASKS :
351- # TODO put this into the regression metric itself
352- cprediction = sanitize_array (prediction )
353- for metric_ in scoring_functions :
374+ for metric_ in scoring_functions + [metric ]:
354375
355376 try :
356- score_dict [metric_ .name ] = metric_ ._sign * metric_ (solution , cprediction )
377+ score_dict [metric_ .name ] = _compute_scorer (
378+ metric_ , prediction , solution , task_type )
357379 except ValueError as e :
358380 print (e , e .args [0 ])
359381 if e .args [0 ] == "Mean Squared Logarithmic Error cannot be used when " \
@@ -363,13 +385,14 @@ def calculate_score(
363385 raise e
364386
365387 else :
366- for metric_ in scoring_functions :
388+ for metric_ in scoring_functions + [ metric ] :
367389
368390 # TODO maybe annotate metrics to define which cases they can
369391 # handle?
370392
371393 try :
372- score_dict [metric_ .name ] = metric_ ._sign * metric_ (solution , prediction )
394+ score_dict [metric_ .name ] = _compute_scorer (
395+ metric_ , prediction , solution , task_type )
373396 except ValueError as e :
374397 if e .args [0 ] == 'multiclass format is not supported' :
375398 continue
@@ -383,34 +406,10 @@ def calculate_score(
383406 else :
384407 raise e
385408
386- if metric .name not in score_dict .keys ():
387- score_dict [metric .name ] = get_metric_score (metric , prediction , solution , task_type )
388409 return score_dict
389410
390411 else :
391- return get_metric_score (metric , prediction , solution , task_type )
392-
393-
394- def get_metric_score (
395- metric_ : Scorer ,
396- prediction : np .ndarray ,
397- solution : np .ndarray ,
398- task_type : int
399- ) -> float :
400- # We match the behaviour of GridSearchCV
401- # In scikit learn, the exact value of the score_func
402- # is returned (not that of the 'Scorer' which might be
403- # negative in functions like mse, as scikit learn
404- # maximizes.) If an user wants to use GridSearchCV
405- # They are expected to pass neg_mean_squared_error
406- # For this reason we multiply back by metric_._sign
407- if task_type in REGRESSION_TASKS :
408- # TODO put this into the regression metric itself
409- cprediction = sanitize_array (prediction )
410- score = metric_ ._sign * metric_ (solution , cprediction )
411- else :
412- score = metric_ ._sign * metric_ (solution , prediction )
413- return score
412+ return _compute_scorer (metric , prediction , solution , task_type )
414413
415414
416415def calculate_loss (
@@ -422,26 +421,28 @@ def calculate_loss(
422421) -> Union [float , Dict [str , float ]]:
423422 """
424423 Returns a loss (a magnitude that allows casting the
425- optimization problem, as a minimization one) for the
424+ optimization problem as a minimization one) for the
426425 given Auto-Sklearn Scorer object
426+
427427 Parameters
428428 ----------
429- solution: np.ndarray
430- The ground truth of the targets
431- prediction: np.ndarray
432- The best estimate from the model, of the given targets
433- task_type: int
434- To understand if the problem task is classification
435- or regression
436- metric: Scorer
437- Object that host a function to calculate how good the
438- prediction is according to the solution.
439- scoring_functions: List[Scorer]
440- A list of metrics to calculate multiple losses
429+ solution: np.ndarray
430+ The ground truth of the targets
431+ prediction: np.ndarray
432+ The best estimate from the model, of the given targets
433+ task_type: int
434+ To understand if the problem task is classification
435+ or regression
436+ metric: Scorer
437+ Object that host a function to calculate how good the
438+ prediction is according to the solution.
439+ scoring_functions: List[Scorer]
440+ A list of metrics to calculate multiple losses
441+
441442 Returns
442443 -------
443- float or Dict[str, float]
444- A loss function for each of the provided scorer objects
444+ float or Dict[str, float]
445+ A loss function for each of the provided scorer objects
445446 """
446447 score = calculate_score (
447448 solution = solution ,
@@ -463,7 +464,80 @@ def calculate_loss(
463464 # maybe metric argument is not in scoring_functions
464465 # so append it to the list. Rather than check if such
465466 # is the case, redefining loss_dict[metric] is less expensive
466- loss_dict [metric_ .name ] = metric_ ._optimum - metric_ . _sign * score [metric_ .name ]
467+ loss_dict [metric_ .name ] = metric_ ._optimum - score [metric_ .name ]
467468 return loss_dict
468469 else :
469- return metric ._optimum - metric ._sign * cast (float , score )
470+ rval = metric ._optimum - cast (float , score )
471+ return rval
472+
473+
474+ def calculate_metric (
475+ metric : Scorer ,
476+ prediction : np .ndarray ,
477+ solution : np .ndarray ,
478+ task_type : int
479+ ) -> float :
480+ """
481+ Returns a metric for the given Auto-Sklearn Scorer object.
482+ It's direction is determined by the metric itself.
483+
484+ Parameters
485+ ----------
486+ solution: np.ndarray
487+ The ground truth of the targets
488+ prediction: np.ndarray
489+ The best estimate from the model, of the given targets
490+ task_type: int
491+ To understand if the problem task is classification
492+ or regression
493+ metric: Scorer
494+ Object that host a function to calculate how good the
495+ prediction is according to the solution.
496+
497+ Returns
498+ -------
499+ float
500+ """
501+ score = _compute_scorer (
502+ solution = solution ,
503+ prediction = prediction ,
504+ metric = metric ,
505+ task_type = task_type ,
506+ )
507+ return metric ._sign * score
508+
509+
510+ def _compute_scorer (
511+ metric : Scorer ,
512+ prediction : np .ndarray ,
513+ solution : np .ndarray ,
514+ task_type : int
515+ ) -> float :
516+ """
517+ Returns a score (a magnitude that allows casting the
518+ optimization problem as a maximization one) for the
519+ given Auto-Sklearn Scorer object
520+
521+ Parameters
522+ ----------
523+ solution: np.ndarray
524+ The ground truth of the targets
525+ prediction: np.ndarray
526+ The best estimate from the model, of the given targets
527+ task_type: int
528+ To understand if the problem task is classification
529+ or regression
530+ metric: Scorer
531+ Object that host a function to calculate how good the
532+ prediction is according to the solution.
533+ Returns
534+ -------
535+ float
536+ """
537+ if task_type in REGRESSION_TASKS :
538+ # TODO put this into the regression metric itself
539+ cprediction = sanitize_array (prediction )
540+ score = metric (solution , cprediction )
541+ else :
542+ score = metric (solution , prediction )
543+ return score
0 commit comments