99import numpy as np
1010import optuna
1111import pandas as pd
12+ from pydantic import validate_arguments
1213from sklearn .impute import MissingIndicator
1314from sklearn .metrics import mean_squared_error
1415from sklearn .preprocessing import LabelEncoder
@@ -61,6 +62,7 @@ def default(self, obj: Any) -> Any:
6162
6263
6364class HyperbandOptimizer :
65+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
6466 def __init__ (
6567 self ,
6668 name : str ,
@@ -112,6 +114,7 @@ def _hash_dict(self, name: str, dict_val: dict) -> str:
112114 {"name" : name , "val" : dict_val }, sort_keys = True , cls = NpEncoder
113115 )
114116
117+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
115118 def _sample_model (self , name : str , n : int ) -> list :
116119 hashed = self ._hash_dict (name , {})
117120 result : List [Tuple ] = []
@@ -132,19 +135,22 @@ def _sample_model(self, name: str, n: int) -> list:
132135
133136 return result
134137
138+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
135139 def _sample (self , n : int ) -> list :
136140 results = []
137141 for name in self .seeds :
138142 results .extend (self ._sample_model (name , n ))
139143 return results
140144
141- def _baseline (self , X : pd .DataFrame , y : pd .DataFrame ) -> None :
145+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
146+ def _baseline (self , X : pd .DataFrame , y : pd .Series ) -> None :
142147 for seed in self .seeds :
143148 self ._eval_params (seed , X , y , hyperparam_search_iterations = 1 )
144149 # TODO: balance methods
145150
151+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
146152 def _eval_params (
147- self , model_name : str , X : pd .DataFrame , y : pd .DataFrame , ** params : Any
153+ self , model_name : str , X : pd .DataFrame , y : pd .Series , ** params : Any
148154 ) -> float :
149155 model = self .predictions .get (model_name , ** params )
150156 for n_folds in [2 , 1 ]:
@@ -171,9 +177,8 @@ def _eval_params(
171177
172178 return score
173179
174- def evaluate (
175- self , X : pd .DataFrame , y : pd .DataFrame
176- ) -> Tuple [PredictionPlugin , float ]:
180+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
181+ def evaluate (self , X : pd .DataFrame , y : pd .Series ) -> Tuple [PredictionPlugin , float ]:
177182 self ._reset ()
178183 self ._baseline (X , y )
179184
@@ -236,6 +241,7 @@ def evaluate(
236241
237242
238243class BayesianOptimizer :
244+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
239245 def __init__ (
240246 self ,
241247 name : str ,
@@ -272,11 +278,12 @@ def __init__(
272278 self .best_candidate = self .seeds [0 ]
273279 self .best_params : dict = {}
274280
281+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
275282 def evaluate_plugin (
276283 self ,
277284 plugin_name : str ,
278285 X : pd .DataFrame ,
279- y : pd .DataFrame ,
286+ y : pd .Series ,
280287 prev_best_score : float ,
281288 ) -> tuple :
282289 # BO evaluation for a single plugin
@@ -333,8 +340,9 @@ def objective(trial: optuna.Trial) -> float:
333340
334341 return study .best_value , study .best_trial .params
335342
343+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
336344 def evaluate (
337- self , X_train : pd .DataFrame , y_train : pd .DataFrame
345+ self , X_train : pd .DataFrame , y_train : pd .Series
338346 ) -> Tuple [PredictionPlugin , float ]:
339347 best_score = self .failure_score
340348
@@ -366,6 +374,7 @@ def evaluate(
366374
367375
368376class SimpleOptimizer :
377+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
369378 def __init__ (
370379 self ,
371380 name : str ,
@@ -398,8 +407,9 @@ def __init__(
398407 for seed in self .seeds :
399408 self .model_best_score [seed ] = - np .inf
400409
410+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
401411 def _eval_params (
402- self , model_name : str , X : pd .DataFrame , y : pd .DataFrame , ** params : Any
412+ self , model_name : str , X : pd .DataFrame , y : pd .Series , ** params : Any
403413 ) -> float :
404414 model = self .predictions .get (
405415 model_name , random_state = self .random_state , ** params
@@ -430,9 +440,8 @@ def _eval_params(
430440
431441 return score
432442
433- def evaluate (
434- self , X : pd .DataFrame , y : pd .DataFrame
435- ) -> Tuple [PredictionPlugin , float ]:
443+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
444+ def evaluate (self , X : pd .DataFrame , y : pd .Series ) -> Tuple [PredictionPlugin , float ]:
436445 for seed in self .seeds :
437446 self ._eval_params (seed , X , y )
438447 log .info (
@@ -447,6 +456,7 @@ def evaluate(
447456
448457
449458class IterativeErrorCorrection :
459+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
450460 def __init__ (
451461 self ,
452462 study : str ,
@@ -530,10 +540,12 @@ def intersect_or_right(left: list, right: list) -> list:
530540 "regression_seed" : reg ,
531541 }
532542
543+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
533544 def _setup (self , X : pd .DataFrame ) -> pd .DataFrame :
534545 # Encode the categorical columns
535546 # Reset internal caches
536547 X = pd .DataFrame (X ).copy ()
548+ X .columns = X .columns .map (str )
537549
538550 self .mask = self ._missing_indicator (X )
539551
@@ -590,25 +602,29 @@ def _setup(self, X: pd.DataFrame) -> pd.DataFrame:
590602
591603 return X
592604
605+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
593606 def _tear_down (self , X : pd .DataFrame ) -> pd .DataFrame :
594607 # Revert the encoding after processing the data
595608 for col in self .encoders :
596609 X [col ] = self .encoders [col ].inverse_transform (X [col ].astype (int ))
597610
598611 return X
599612
613+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
600614 def _get_neighbors_for_col (self , col : str ) -> list :
601615 covs = list (self .all_cols )
602616 covs .remove (col )
603617
604618 return covs
605619
620+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
606621 def _is_same_type (self , lhs : str , rhs : str ) -> bool :
607622 ltype = lhs in self .categorical_cols
608623 rtype = rhs in self .categorical_cols
609624
610625 return ltype == rtype
611626
627+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
612628 def _check_similar (self , X : pd .DataFrame , col : str ) -> Any :
613629 if not self .select_lazy :
614630 return None
@@ -630,6 +646,7 @@ def _check_similar(self, X: pd.DataFrame, col: str) -> Any:
630646
631647 return None
632648
649+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
633650 def _optimize_model_for_column (self , X : pd .DataFrame , col : str ) -> float :
634651 # BO evaluation for a single column
635652 if self .mask [col ].sum () == 0 :
@@ -666,6 +683,7 @@ def _optimize_model_for_column(self, X: pd.DataFrame, col: str) -> float:
666683
667684 return score
668685
686+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
669687 def _optimize (self , X : pd .DataFrame ) -> float :
670688 # BO evaluation to select the best models for each columns
671689 if self .select_model_by_iteration :
@@ -677,6 +695,7 @@ def _optimize(self, X: pd.DataFrame) -> float:
677695
678696 return iteration_score
679697
698+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
680699 def _impute_single_column (
681700 self , X : pd .DataFrame , col : str , train : bool
682701 ) -> pd .DataFrame :
@@ -723,14 +742,17 @@ def _get_imputation_order(self) -> list:
723742 random .shuffle (self .imputation_order )
724743 return self .imputation_order
725744
745+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
726746 def _initial_imputation (self , X : pd .DataFrame ) -> pd .DataFrame :
727747 # Use baseline imputer for initial values
728748 return self .baseline_imputer .fit_transform (X )
729749
750+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
730751 def _is_categorical (self , X : pd .DataFrame , col : str ) -> bool :
731752 # Helper for filtering categorical columns
732753 return len (X [col ].unique ()) < self .class_threshold
733754
755+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
734756 def _missing_indicator (self , X : pd .DataFrame ) -> pd .DataFrame :
735757 # Helper for generating missingness mask
736758 return pd .DataFrame (
@@ -739,6 +761,7 @@ def _missing_indicator(self, X: pd.DataFrame) -> pd.DataFrame:
739761 index = X .index ,
740762 )
741763
764+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
742765 def _fit_transform_inner_optimization (self , X : pd .DataFrame ) -> pd .DataFrame :
743766 log .info (" > HyperImpute using inner optimization" )
744767 best_obj_score = - 10e10
@@ -780,6 +803,7 @@ def _fit_transform_inner_optimization(self, X: pd.DataFrame) -> pd.DataFrame:
780803
781804 return X
782805
806+ @validate_arguments (config = dict (arbitrary_types_allowed = True ))
783807 def fit_transform (self , X : pd .DataFrame ) -> pd .DataFrame :
784808 # Run imputation
785809 X = self ._setup (X )
0 commit comments