# Buggy code
def hyperparameter_tune(
    self, X, y, k_fold, scheduler_options=None, compute_base_preds=True, **kwargs
):
    if len(self.models) != 0:
        raise ValueError(
            "self.models must be empty to call hyperparameter_tune, value: %s"
            % self.models
        )

    if len(self.models) == 0:
        if (
            self.feature_types_metadata is None
        ):  # TODO: This is probably not the best way to do this
            feature_types_raw = defaultdict(list)
            feature_types_raw["float"] = self.stack_columns
            feature_types_special = defaultdict(list)
            feature_types_special["stack"] = self.stack_columns
            self.feature_types_metadata = FeatureTypesMetadata(
                feature_types_raw=feature_types_raw,
                feature_types_special=feature_types_special,
            )
        else:
            self.feature_types_metadata = copy.deepcopy(self.feature_types_metadata)
            self.feature_types_metadata.feature_types_raw["float"] += self.stack_columns
            self.feature_types_metadata.feature_types_special["stack"] += (
                self.stack_columns
            )
    self.model_base.feature_types_metadata = (
        self.feature_types_metadata
    )  # TODO: Move this

    # TODO: Preprocess data here instead of repeatedly
    X = self.preprocess(
        X=X, preprocess=False, fit=True, compute_base_preds=compute_base_preds
    )
    kfolds = generate_kfold(
        X=X,
        y=y,
        n_splits=k_fold,
        stratified=self.is_stratified(),
        random_state=self._random_state,
        n_repeats=1,
    )

    train_index, test_index = kfolds[0]
    X_train, X_val = X.iloc[train_index, :], X.iloc[test_index, :]
    y_train, y_val = y.iloc[train_index], y.iloc[test_index]
    orig_time = scheduler_options[1]["time_out"]
    scheduler_options[1]["time_out"] = (
        orig_time * 0.8
    )  # TODO: Scheduler doesn't early stop on final model, this is a safety net. Scheduler should be updated to early stop
    hpo_models, hpo_model_performances, hpo_results = (
        self.model_base.hyperparameter_tune(
            X_train=X_train,
            y_train=y_train,
            X_val=X_val,
            y_val=y_val,
            scheduler_options=scheduler_options,
            **kwargs,
        )
    )
    scheduler_options[1]["time_out"] = orig_time

    stackers = {}
    stackers_performance = {}
    for i, (model_name, model_path) in enumerate(hpo_models.items()):
        child: AbstractModel = self._child_type.load(path=model_path)
        y_pred_proba = child.predict_proba(X_val)

        # TODO: Create new StackerEnsemble Here
        stacker = copy.deepcopy(self)
        stacker.name = stacker.name + os.path.sep + str(i)
        stacker.set_contexts(self.path_root + stacker.name + os.path.sep)

        if self.problem_type == MULTICLASS:
            oof_pred_proba = np.zeros(shape=(len(X), len(y.unique())))
        else:
            oof_pred_proba = np.zeros(shape=len(X))
        oof_pred_model_repeats = np.zeros(shape=len(X))
        oof_pred_proba[test_index] += y_pred_proba
        oof_pred_model_repeats[test_index] += 1

        stacker.model_base = None
        child.set_contexts(stacker.path + child.name + os.path.sep)
        stacker.save_model_base(child.convert_to_template())

        stacker._k = k_fold
        stacker._k_fold_end = 1
        stacker._n_repeats = 1
        stacker._oof_pred_proba = oof_pred_proba
        stacker._oof_pred_model_repeats = oof_pred_model_repeats
        child.name = child.name + "_fold_0"
        child.set_contexts(stacker.path + child.name + os.path.sep)
        if not self.save_bagged_folds:
            child.model = None
        if stacker.low_memory:
            stacker.save_child(child, verbose=False)
            stacker.models.append(child.name)
        else:
            stacker.models.append(child)
        stacker.val_score = child.val_score
        stacker._add_child_times_to_bag(model=child)

        stacker.save()
        stackers[stacker.name] = stacker.path
        stackers_performance[stacker.name] = stacker.val_score

    # TODO: hpo_results likely not correct because no renames
    return stackers, stackers_performance, hpo_results


# Traceback (ValueError)
File delimiter for ../input/house-prices-advanced-regression-techniques/test.csv inferred as ',' (comma). If this is incorrect, please manually load the data as a pandas DataFrame.
Loaded data from: ../input/house-prices-advanced-regression-techniques/test.csv | Columns = 80 / 80 | Rows = 1459 -> 1459
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-48-91455d7b9940> in <module>
1 test_data = task.Dataset(file_path=os.path.join(in_dir, job, 'test.csv'))
2
----> 3 preds = model.predict(test_data)

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/autogluon/task/tabular_prediction/predictor.py in predict(self, dataset, model, as_pandas, use_pred_cache, add_to_pred_cache)
130         """
131         dataset = self.__get_dataset(dataset)
--> 132         return self._learner.predict(X=dataset, model=model, as_pandas=as_pandas, use_pred_cache=use_pred_cache, add_to_pred_cache=add_to_pred_cache)
133
134     def predict_proba(self, dataset, model=None, as_pandas=False, as_multiclass=False):

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/autogluon/utils/tabular/ml/learner/abstract_learner.py in predict(self, X, model, as_pandas, use_pred_cache, add_to_pred_cache)
127
128         if len(X_cache_miss) > 0:
--> 129             y_pred_proba = self.predict_proba(X=X_cache_miss, model=model, inverse_transform=False)
130             problem_type = self.trainer_problem_type or self.problem_type
131             y_pred = get_pred_from_proba(y_pred_proba=y_pred_proba, problem_type=problem_type)

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/autogluon/utils/tabular/ml/learner/abstract_learner.py in predict_proba(self, X, model, as_pandas, as_multiclass, inverse_transform)
92     # TODO: Add pred_proba_cache functionality as in predict()
93     def predict_proba(self, X: DataFrame, model=None, as_pandas=False, as_multiclass=False, inverse_transform=True):
---> 94         X = self.transform_features(X)
95         y_pred_proba = self.load_trainer().predict_proba(X, model=model)
96         if inverse_transform:

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/autogluon/utils/tabular/ml/learner/abstract_learner.py in transform_features(self, X)
216     def transform_features(self, X):
217         for feature_generator in self.feature_generators:
--> 218             X = feature_generator.transform(X)
219         return X
220

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/autogluon/utils/tabular/utils/decorators.py in inner1(*args, **kwargs)
12         begin = time.time()
13
---> 14         output = func(*args, **kwargs)
15
16         # storing time after function execution

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/autogluon/utils/tabular/features/abstract_feature_generator.py in transform(self, X)
183             raise ValueError(f'Required columns are missing from the provided dataset. Missing columns: {missing_cols}')
184
--> 185         X = X.astype(self.features_init_types)
186         X.reset_index(drop=True, inplace=True)
187         X_features = self.generate_features(X)

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/generic.py in astype(self, dtype, copy, errors, **kwargs)
5863                     results.append(
5864                         col.astype(
-> 5865                             dtype=dtype[col_name], copy=copy, errors=errors, **kwargs
5866                         )
5867                     )

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/generic.py in astype(self, dtype, copy, errors, **kwargs)
5880             # else, only a single dtype is given
5881             new_data = self._data.astype(
-> 5882                 dtype=dtype, copy=copy, errors=errors, **kwargs
5883             )
5884             return self._constructor(new_data).__finalize__(self)

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/internals/managers.py in astype(self, dtype, **kwargs)
579
580     def astype(self, dtype, **kwargs):
--> 581         return self.apply("astype", dtype=dtype, **kwargs)
582
583     def convert(self, **kwargs):

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/internals/managers.py in apply(self, f, axes, filter, do_integrity_check, consolidate, **kwargs)
436                     kwargs[k] = obj.reindex(b_items, axis=axis, copy=align_copy)
437
--> 438             applied = getattr(b, f)(**kwargs)
439             result_blocks = _extend_blocks(applied, result_blocks)
440

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/internals/blocks.py in astype(self, dtype, copy, errors, values, **kwargs)
557
558     def astype(self, dtype, copy=False, errors="raise", values=None, **kwargs):
--> 559         return self._astype(dtype, copy=copy, errors=errors, values=values, **kwargs)
560
561     def _astype(self, dtype, copy=False, errors="raise", values=None, **kwargs):

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/internals/blocks.py in _astype(self, dtype, copy, errors, values, **kwargs)
641                     # _astype_nansafe works fine with 1-d only
642                     vals1d = values.ravel()
--> 643                     values = astype_nansafe(vals1d, dtype, copy=True, **kwargs)
644
645                 # TODO(extension)

~/miniconda3/envs/autogluon/lib/python3.7/site-packages/pandas/core/dtypes/cast.py in astype_nansafe(arr, dtype, copy, skipna)
698         if not np.isfinite(arr).all():
699             raise ValueError(
--> 700                 "Cannot convert non-finite values (NA or inf) to " "integer"
701             )
702

ValueError: Cannot convert non-finite values (NA or inf) to integer

# Fixed code
def hyperparameter_tune(
    self, X, y, k_fold, scheduler_options=None, compute_base_preds=True, **kwargs
):
    if len(self.models) != 0:
        raise ValueError(
            "self.models must be empty to call hyperparameter_tune, value: %s"
            % self.models
        )

    if len(self.models) == 0:
        type_map_raw = {column: R_FLOAT for column in self.stack_columns}
        type_group_map_special = {S_STACK: self.stack_columns}
        stacker_feature_metadata = FeatureMetadata(
            type_map_raw=type_map_raw, type_group_map_special=type_group_map_special
        )
        if (
            self.feature_metadata is None
        ):  # TODO: This is probably not the best way to do this
            self.feature_metadata = stacker_feature_metadata
        else:
            self.feature_metadata = self.feature_metadata.join_metadata(
                stacker_feature_metadata
            )
    self.model_base.feature_metadata = self.feature_metadata  # TODO: Move this

    # TODO: Preprocess data here instead of repeatedly
    X = self.preprocess(
        X=X, preprocess=False, fit=True, compute_base_preds=compute_base_preds
    )
    kfolds = generate_kfold(
        X=X,
        y=y,
        n_splits=k_fold,
        stratified=self.is_stratified(),
        random_state=self._random_state,
        n_repeats=1,
    )

    train_index, test_index = kfolds[0]
    X_train, X_val = X.iloc[train_index, :], X.iloc[test_index, :]
    y_train, y_val = y.iloc[train_index], y.iloc[test_index]
    orig_time = scheduler_options[1]["time_out"]
    scheduler_options[1]["time_out"] = (
        orig_time * 0.8
    )  # TODO: Scheduler doesn't early stop on final model, this is a safety net. Scheduler should be updated to early stop
    hpo_models, hpo_model_performances, hpo_results = (
        self.model_base.hyperparameter_tune(
            X_train=X_train,
            y_train=y_train,
            X_val=X_val,
            y_val=y_val,
            scheduler_options=scheduler_options,
            **kwargs,
        )
    )
    scheduler_options[1]["time_out"] = orig_time

    stackers = {}
    stackers_performance = {}
    for i, (model_name, model_path) in enumerate(hpo_models.items()):
        child: AbstractModel = self._child_type.load(path=model_path)
        y_pred_proba = child.predict_proba(X_val)

        # TODO: Create new StackerEnsemble Here
        stacker = copy.deepcopy(self)
        stacker.name = stacker.name + os.path.sep + str(i)
        stacker.set_contexts(self.path_root + stacker.name + os.path.sep)

        if self.problem_type == MULTICLASS:
            oof_pred_proba = np.zeros(shape=(len(X), len(y.unique())))
        else:
            oof_pred_proba = np.zeros(shape=len(X))
        oof_pred_model_repeats = np.zeros(shape=len(X))
        oof_pred_proba[test_index] += y_pred_proba
        oof_pred_model_repeats[test_index] += 1

        stacker.model_base = None
        child.set_contexts(stacker.path + child.name + os.path.sep)
        stacker.save_model_base(child.convert_to_template())

        stacker._k = k_fold
        stacker._k_fold_end = 1
        stacker._n_repeats = 1
        stacker._oof_pred_proba = oof_pred_proba
        stacker._oof_pred_model_repeats = oof_pred_model_repeats
        child.name = child.name + "_fold_0"
        child.set_contexts(stacker.path + child.name + os.path.sep)
        if not self.save_bagged_folds:
            child.model = None
        if stacker.low_memory:
            stacker.save_child(child, verbose=False)
            stacker.models.append(child.name)
        else:
            stacker.models.append(child)
        stacker.val_score = child.val_score
        stacker._add_child_times_to_bag(model=child)

        stacker.save()
        stackers[stacker.name] = stacker.path
        stackers_performance[stacker.name] = stacker.val_score

    # TODO: hpo_results likely not correct because no renames
    return stackers, stackers_performance, hpo_results