PyIAML 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iaml/__init__.py +56 -0
- iaml/actionable.py +11 -0
- iaml/actionables/__init__.py +21 -0
- iaml/actionables/boosting/__init__.py +4 -0
- iaml/actionables/boosting/act_adaboost.py +59 -0
- iaml/actionables/cleaning/__init__.py +26 -0
- iaml/actionables/cleaning/act_categorical_imputer.py +124 -0
- iaml/actionables/cleaning/act_count_vectorizer.py +204 -0
- iaml/actionables/cleaning/act_drop_categorical_column.py +51 -0
- iaml/actionables/cleaning/act_drop_date_column.py +48 -0
- iaml/actionables/cleaning/act_drop_high_cardinality_categorical.py +337 -0
- iaml/actionables/cleaning/act_drop_numerical_column.py +75 -0
- iaml/actionables/cleaning/act_drop_textual_column.py +51 -0
- iaml/actionables/cleaning/act_encode_target_column.py +56 -0
- iaml/actionables/cleaning/act_frequency_encoder.py +127 -0
- iaml/actionables/cleaning/act_hashing_vectorizer.py +186 -0
- iaml/actionables/cleaning/act_knn_imputer.py +152 -0
- iaml/actionables/cleaning/act_mean_column.py +79 -0
- iaml/actionables/cleaning/act_mice.py +464 -0
- iaml/actionables/cleaning/act_missing_count_feature.py +109 -0
- iaml/actionables/cleaning/act_missing_indicator.py +124 -0
- iaml/actionables/cleaning/act_onehot.py +65 -0
- iaml/actionables/cleaning/act_ordinal_encoder.py +177 -0
- iaml/actionables/cleaning/act_rare_category_grouper.py +173 -0
- iaml/actionables/cleaning/act_simple_imputer.py +109 -0
- iaml/actionables/cleaning/act_split_date.py +68 -0
- iaml/actionables/cleaning/act_target_encoder.py +274 -0
- iaml/actionables/cleaning/act_text_normalizer.py +241 -0
- iaml/actionables/cleaning/act_tf_idf.py +80 -0
- iaml/actionables/cleaning/act_word2vec.py +150 -0
- iaml/actionables/features_precleaning/__init__.py +12 -0
- iaml/actionables/features_precleaning/act_coerce_numeric_strings.py +194 -0
- iaml/actionables/features_precleaning/act_date_converter.py +99 -0
- iaml/actionables/features_precleaning/act_drop_bad_quality_rows.py +77 -0
- iaml/actionables/features_precleaning/act_drop_duplicate_rows.py +131 -0
- iaml/actionables/features_precleaning/act_drop_high_missing_columns.py +94 -0
- iaml/actionables/features_precleaning/act_drop_id_like_columns.py +294 -0
- iaml/actionables/features_precleaning/act_normalize_column_names.py +157 -0
- iaml/actionables/features_precleaning/act_sentinel_to_na_n.py +270 -0
- iaml/actionables/features_precleaning/act_trim_space.py +79 -0
- iaml/actionables/features_preprocessing/__init__.py +18 -0
- iaml/actionables/features_preprocessing/act_cyclical_date_encoding.py +212 -0
- iaml/actionables/features_preprocessing/act_fast_ica.py +161 -0
- iaml/actionables/features_preprocessing/act_feature_agglomeration.py +90 -0
- iaml/actionables/features_preprocessing/act_k_bins_discretizer.py +207 -0
- iaml/actionables/features_preprocessing/act_k_means_features.py +296 -0
- iaml/actionables/features_preprocessing/act_kernel_pca.py +143 -0
- iaml/actionables/features_preprocessing/act_log_transformer.py +122 -0
- iaml/actionables/features_preprocessing/act_nystroem.py +100 -0
- iaml/actionables/features_preprocessing/act_pca.py +77 -0
- iaml/actionables/features_preprocessing/act_polynomial_features.py +86 -0
- iaml/actionables/features_preprocessing/act_power_transformer.py +106 -0
- iaml/actionables/features_preprocessing/act_quantile_transformer.py +114 -0
- iaml/actionables/features_preprocessing/act_rbf_sampler.py +88 -0
- iaml/actionables/features_preprocessing/act_select_percentile.py +112 -0
- iaml/actionables/features_preprocessing/act_sparse_random_projection.py +157 -0
- iaml/actionables/features_preprocessing/act_truncated_svd.py +137 -0
- iaml/actionables/features_selection/__init__.py +8 -0
- iaml/actionables/features_selection/act_permutation_importance_selector.py +421 -0
- iaml/actionables/features_selection/act_remove_high_correlated_column.py +70 -0
- iaml/actionables/features_selection/act_remove_low_variance_column.py +74 -0
- iaml/actionables/features_selection/act_rfe.py +214 -0
- iaml/actionables/features_selection/act_select_from_model.py +325 -0
- iaml/actionables/features_selection/act_select_k_best.py +181 -0
- iaml/actionables/features_selection/act_vif_selector.py +130 -0
- iaml/actionables/imbalance/__init__.py +10 -0
- iaml/actionables/imbalance/act_adasyn.py +150 -0
- iaml/actionables/imbalance/act_borderline_smote.py +171 -0
- iaml/actionables/imbalance/act_near_miss.py +158 -0
- iaml/actionables/imbalance/act_random_over_sampling.py +60 -0
- iaml/actionables/imbalance/act_random_under_sampler.py +135 -0
- iaml/actionables/imbalance/act_smote.py +162 -0
- iaml/actionables/imbalance/act_smote_tomek.py +182 -0
- iaml/actionables/imbalance/act_smoteenn.py +193 -0
- iaml/actionables/imbalance/act_tomek_links.py +138 -0
- iaml/actionables/normalize/__init__.py +6 -0
- iaml/actionables/normalize/act_max_abs_scaler.py +78 -0
- iaml/actionables/normalize/act_minmax_scaler.py +56 -0
- iaml/actionables/normalize/act_normalizer.py +95 -0
- iaml/actionables/normalize/act_robust_scaler.py +111 -0
- iaml/actionables/normalize/act_standard_scaler.py +55 -0
- iaml/actionables/predictors/__init__.py +6 -0
- iaml/actionables/predictors/_xgboost.py +16 -0
- iaml/actionables/predictors/classifier/__init__.py +26 -0
- iaml/actionables/predictors/classifier/act_bagging_classifier.py +113 -0
- iaml/actionables/predictors/classifier/act_bernoulli_nb.py +89 -0
- iaml/actionables/predictors/classifier/act_catboost_classifier.py +135 -0
- iaml/actionables/predictors/classifier/act_complement_nb.py +106 -0
- iaml/actionables/predictors/classifier/act_decision_tree_classifier.py +117 -0
- iaml/actionables/predictors/classifier/act_extra_trees_classifier.py +115 -0
- iaml/actionables/predictors/classifier/act_gaussian_nb.py +53 -0
- iaml/actionables/predictors/classifier/act_hist_gradient_boosting_classifier.py +144 -0
- iaml/actionables/predictors/classifier/act_knn.py +86 -0
- iaml/actionables/predictors/classifier/act_light_gbm_classifier.py +211 -0
- iaml/actionables/predictors/classifier/act_linear_discriminant_analysis.py +63 -0
- iaml/actionables/predictors/classifier/act_linear_svc.py +134 -0
- iaml/actionables/predictors/classifier/act_logistic_regression.py +92 -0
- iaml/actionables/predictors/classifier/act_mlp_classifier.py +107 -0
- iaml/actionables/predictors/classifier/act_multinomial_nb.py +76 -0
- iaml/actionables/predictors/classifier/act_passive_aggressive_classifier.py +141 -0
- iaml/actionables/predictors/classifier/act_quadratic_discriminant_analysis.py +72 -0
- iaml/actionables/predictors/classifier/act_randomforest.py +113 -0
- iaml/actionables/predictors/classifier/act_ridge_classifier.py +116 -0
- iaml/actionables/predictors/classifier/act_sgd_classifier.py +149 -0
- iaml/actionables/predictors/classifier/act_svm_svc.py +88 -0
- iaml/actionables/predictors/classifier/act_xgboost.py +111 -0
- iaml/actionables/predictors/regressor/__init__.py +27 -0
- iaml/actionables/predictors/regressor/act_ada_boost_regressor.py +75 -0
- iaml/actionables/predictors/regressor/act_ard_regression.py +95 -0
- iaml/actionables/predictors/regressor/act_catboost_regressor.py +134 -0
- iaml/actionables/predictors/regressor/act_decision_tree_regressor.py +111 -0
- iaml/actionables/predictors/regressor/act_elastic_net_regressor.py +109 -0
- iaml/actionables/predictors/regressor/act_extra_trees_regressor.py +113 -0
- iaml/actionables/predictors/regressor/act_gaussian_process_regressor.py +55 -0
- iaml/actionables/predictors/regressor/act_gboost_regressor.py +95 -0
- iaml/actionables/predictors/regressor/act_hist_gradient_boosting_regressor.py +105 -0
- iaml/actionables/predictors/regressor/act_huber_regressor.py +101 -0
- iaml/actionables/predictors/regressor/act_knn_regressor.py +86 -0
- iaml/actionables/predictors/regressor/act_lasso_regressor.py +103 -0
- iaml/actionables/predictors/regressor/act_light_gbm_regressor.py +201 -0
- iaml/actionables/predictors/regressor/act_linear_regression.py +43 -0
- iaml/actionables/predictors/regressor/act_mlp_regressor.py +104 -0
- iaml/actionables/predictors/regressor/act_poisson_regressor.py +111 -0
- iaml/actionables/predictors/regressor/act_quantile_regressor.py +87 -0
- iaml/actionables/predictors/regressor/act_randomforest_regressor.py +116 -0
- iaml/actionables/predictors/regressor/act_ransac_regressor.py +106 -0
- iaml/actionables/predictors/regressor/act_ridge_regressor.py +107 -0
- iaml/actionables/predictors/regressor/act_sgd_regressor.py +106 -0
- iaml/actionables/predictors/regressor/act_svm_svr.py +81 -0
- iaml/actionables/predictors/regressor/act_xgboost_regressor.py +97 -0
- iaml/actionables/predictors/survival/__init__.py +12 -0
- iaml/actionables/predictors/survival/act_aalen_additive_model.py +83 -0
- iaml/actionables/predictors/survival/act_cox.py +110 -0
- iaml/actionables/predictors/survival/act_coxnet_survival_analysis.py +134 -0
- iaml/actionables/predictors/survival/act_extra_survival_trees.py +101 -0
- iaml/actionables/predictors/survival/act_fast_survival_svm.py +102 -0
- iaml/actionables/predictors/survival/act_gradient_boosting_survival_analysis.py +93 -0
- iaml/actionables/predictors/survival/act_random_survival_forest.py +91 -0
- iaml/actionables/predictors/survival/act_survival_component_wise_gboost.py +80 -0
- iaml/actionables/predictors/survival/act_survival_tree.py +120 -0
- iaml/actionables/predictors/survival/act_survival_xgboost.py +9 -0
- iaml/actionables/predictors/survival/act_weibull_aft.py +230 -0
- iaml/cache.py +61 -0
- iaml/cache_keys.py +57 -0
- iaml/candidate.py +736 -0
- iaml/core_dispatcher.py +125 -0
- iaml/data_type.py +11 -0
- iaml/dataset.py +506 -0
- iaml/decorators/__init__.py +3 -0
- iaml/decorators/all.py +4 -0
- iaml/decorators/is_step.py +45 -0
- iaml/decorators/runner.py +100 -0
- iaml/explanation.py +112 -0
- iaml/iaml.py +1072 -0
- iaml/iaml_pipeline.py +600 -0
- iaml/logger.py +138 -0
- iaml/meta_explorer_step.py +62 -0
- iaml/meta_ordered_step.py +28 -0
- iaml/meta_partial_explorer_step.py +34 -0
- iaml/meta_singleton.py +24 -0
- iaml/metastep.py +211 -0
- iaml/metric.py +111 -0
- iaml/metric_plot.py +82 -0
- iaml/metrics/__init__.py +21 -0
- iaml/metrics/_classification.py +28 -0
- iaml/metrics/_survival_times.py +22 -0
- iaml/metrics/accuracy_metric.py +59 -0
- iaml/metrics/balanced_accuracy_metric.py +67 -0
- iaml/metrics/brier_score.py +90 -0
- iaml/metrics/classification_error_metric.py +66 -0
- iaml/metrics/concordance_index_ipcw.py +84 -0
- iaml/metrics/concordance_index_metric.py +67 -0
- iaml/metrics/cumulative_dynamic_auc.py +119 -0
- iaml/metrics/f1_score_metric.py +71 -0
- iaml/metrics/integrated_brier_score.py +98 -0
- iaml/metrics/integrated_brier_score_loss.py +41 -0
- iaml/metrics/mean_absolute_error_metric.py +46 -0
- iaml/metrics/mean_squared_error_metric.py +46 -0
- iaml/metrics/mean_squared_log_error_metric.py +49 -0
- iaml/metrics/median_absolute_error_metric.py +48 -0
- iaml/metrics/precision_metric.py +63 -0
- iaml/metrics/r2_score_metric.py +45 -0
- iaml/metrics/recall_metric.py +65 -0
- iaml/metrics/roc_auc_metric.py +50 -0
- iaml/metrics/specificity_metric.py +44 -0
- iaml/metrics/specificity_multiclass_metric.py +55 -0
- iaml/metrics/specificity_multilabel_metric.py +60 -0
- iaml/optimizers/__init__.py +5 -0
- iaml/optimizers/bayesian_optimizer.py +193 -0
- iaml/optimizers/genetic_optimizer.py +284 -0
- iaml/optimizers/optimizer.py +31 -0
- iaml/optimizers/random_optimizer.py +101 -0
- iaml/plot.py +138 -0
- iaml/plots/__init__.py +32 -0
- iaml/plots/bar_plot.py +141 -0
- iaml/plots/box_plot.py +166 -0
- iaml/plots/class_prediction_error_plot.py +37 -0
- iaml/plots/classification_report_plot.py +35 -0
- iaml/plots/confusion_matrix_plot.py +34 -0
- iaml/plots/correlation_heatmap_plot.py +201 -0
- iaml/plots/cumulative_hazard_plot.py +72 -0
- iaml/plots/density_plot.py +210 -0
- iaml/plots/histogram_plot.py +179 -0
- iaml/plots/kaplan_meier_comparison_plot.py +89 -0
- iaml/plots/line_plot.py +70 -0
- iaml/plots/missingness_heatmap_plot.py +203 -0
- iaml/plots/outlier_plot.py +217 -0
- iaml/plots/pair_plot.py +228 -0
- iaml/plots/precision_recall_curve_plot.py +86 -0
- iaml/plots/prediction_error_plot.py +34 -0
- iaml/plots/qq_plot.py +220 -0
- iaml/plots/residual_plot.py +38 -0
- iaml/plots/roc_dynamique_curve_plot.py +79 -0
- iaml/plots/rocauc_plot.py +96 -0
- iaml/plots/shap_plot.py +187 -0
- iaml/plots/target_distribution_plot.py +241 -0
- iaml/plots/violin_plot.py +206 -0
- iaml/predictor.py +139 -0
- iaml/reference.py +65 -0
- iaml/shared_cache.py +90 -0
- iaml/sklearn_preprocessor.py +74 -0
- iaml/splitters/__init__.py +3 -0
- iaml/splitters/kfold_splitter.py +32 -0
- iaml/splitters/random_splitter.py +26 -0
- iaml/stack.py +39 -0
- iaml/statistic.py +66 -0
- iaml/statistics/__init__.py +77 -0
- iaml/statistics/anova_statistic.py +80 -0
- iaml/statistics/cardinality_ratio_statistic.py +63 -0
- iaml/statistics/category_cooccurrence_statistic.py +79 -0
- iaml/statistics/chi_square_statistic.py +81 -0
- iaml/statistics/coef_variation_statistic.py +72 -0
- iaml/statistics/correlation_with_target.py +105 -0
- iaml/statistics/count.py +72 -0
- iaml/statistics/data_type_summary_statistic.py +74 -0
- iaml/statistics/duplicate_row_statistic.py +56 -0
- iaml/statistics/effect_size_statistic.py +129 -0
- iaml/statistics/entropy_statistic.py +69 -0
- iaml/statistics/event_rate_statistic.py +52 -0
- iaml/statistics/grouped_mean_statistic.py +60 -0
- iaml/statistics/iqr_statistic.py +66 -0
- iaml/statistics/kurtosis.py +50 -0
- iaml/statistics/mad_statistic.py +66 -0
- iaml/statistics/mean.py +61 -0
- iaml/statistics/median_statistic.py +61 -0
- iaml/statistics/minmax.py +60 -0
- iaml/statistics/missing_rate_statistic.py +62 -0
- iaml/statistics/mode.py +47 -0
- iaml/statistics/most_frequent_ratio.py +81 -0
- iaml/statistics/outlier_count_iqr_statistic.py +76 -0
- iaml/statistics/quantile.py +59 -0
- iaml/statistics/range.py +53 -0
- iaml/statistics/rare_category_rate.py +92 -0
- iaml/statistics/skewness.py +53 -0
- iaml/statistics/stdev.py +50 -0
- iaml/statistics/summary_table_statistic.py +60 -0
- iaml/statistics/time_by_group_statistic.py +83 -0
- iaml/statistics/time_summary_statistic.py +56 -0
- iaml/statistics/top_k_value_counts.py +68 -0
- iaml/statistics/unique_count_statistic.py +57 -0
- iaml/statistics/value_counts.py +63 -0
- iaml/statistics/variance.py +51 -0
- iaml/statistics/violin.py +63 -0
- iaml/step.py +600 -0
- iaml/step_cache.py +87 -0
- iaml/step_wrapper.py +79 -0
- iaml/timed_pool_executor.py +492 -0
- iaml/type_of_target.py +68 -0
- iaml/void_step.py +101 -0
- iaml/worker_manager.py +169 -0
- iaml/wrapper/__init__.py +4 -0
- iaml/wrapper/wrap_basic_gridsearch.py +68 -0
- iaml/wrapper/wrap_genetic_gridsearch.py +293 -0
- iaml/wrapper/wrap_iterative_gridsearch.py +399 -0
- pyiaml-1.0.0.dist-info/METADATA +802 -0
- pyiaml-1.0.0.dist-info/RECORD +279 -0
- pyiaml-1.0.0.dist-info/WHEEL +5 -0
- pyiaml-1.0.0.dist-info/licenses/LICENSE +674 -0
- pyiaml-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""[STEP] Target mean encoding with internal cross-validation."""
|
|
2
|
+
import textwrap
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from sklearn.model_selection import KFold, StratifiedKFold
|
|
8
|
+
|
|
9
|
+
from ...actionable import Actionable
|
|
10
|
+
from ...candidate import Candidate
|
|
11
|
+
from ...data_type import DataType
|
|
12
|
+
from ...dataset import Dataset
|
|
13
|
+
from ...decorators.all import is_step
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@is_step('cleaning')
|
|
17
|
+
class ActTargetEncoder(Actionable):
|
|
18
|
+
"""[STEP] Encode categorical features by target mean with internal CV."""
|
|
19
|
+
|
|
20
|
+
name: str = 'Target mean encoding'
|
|
21
|
+
_description: str = textwrap.dedent('''\
|
|
22
|
+
Encode categorical columns with the mean of the target using internal CV.''')
|
|
23
|
+
_description_long: str = textwrap.dedent('''\
|
|
24
|
+
Replace each category with the mean target value computed on training folds only.
|
|
25
|
+
This yields out-of-fold encodings for the training data to reduce leakage. For new
|
|
26
|
+
data, mappings learned on the full training set are used and unseen categories are
|
|
27
|
+
mapped to the global target mean. Non-numeric targets are factorized first.''')
|
|
28
|
+
_usage: str = "Use when categorical features have target signal and you want numeric encoding vs ActCountVectorizer. Applicable to supervised data with categorical columns and enough rows. Avoid when leakage risk is high or categories are too sparse; prefer ActDropHighCardinalityCategorical."
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
self.columns: list[str] = []
|
|
32
|
+
self.encodings: dict[str, dict[Any, float]] = {}
|
|
33
|
+
self.fallback_values: dict[str, float] = {}
|
|
34
|
+
self.global_mean: float = 0.0
|
|
35
|
+
self._train_encoded: pd.DataFrame | None = None
|
|
36
|
+
self._train_X: pd.DataFrame | None = None
|
|
37
|
+
|
|
38
|
+
self.configuration = {
|
|
39
|
+
'n_splits': {
|
|
40
|
+
'description': 'Number of CV folds used to compute out-of-fold encodings.',
|
|
41
|
+
'default': 5
|
|
42
|
+
},
|
|
43
|
+
'shuffle': {
|
|
44
|
+
'description': 'Shuffle rows before splitting into folds.',
|
|
45
|
+
'default': True
|
|
46
|
+
},
|
|
47
|
+
'random_state': {
|
|
48
|
+
'description': 'Random seed used when shuffling.',
|
|
49
|
+
'default': 42
|
|
50
|
+
},
|
|
51
|
+
'smoothing': {
|
|
52
|
+
'description': 'Smoothing strength towards the global mean.',
|
|
53
|
+
'default': 1.0
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def fit(self, dataset: Dataset) -> Actionable:
|
|
58
|
+
self.columns = self._select_columns(dataset)
|
|
59
|
+
self.encodings = {}
|
|
60
|
+
self.fallback_values = {}
|
|
61
|
+
self.global_mean = 0.0
|
|
62
|
+
self._train_encoded = None
|
|
63
|
+
self._train_X = None
|
|
64
|
+
self.explanations = []
|
|
65
|
+
|
|
66
|
+
if not self.columns or dataset.X.empty or dataset.y is None:
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
y_series = pd.Series(dataset.y, index=dataset.X.index)
|
|
70
|
+
y_numeric = self._coerce_target(y_series)
|
|
71
|
+
if y_numeric.empty:
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
prior = float(y_numeric.mean())
|
|
75
|
+
if np.isnan(prior):
|
|
76
|
+
prior = 0.0
|
|
77
|
+
self.global_mean = prior
|
|
78
|
+
|
|
79
|
+
n_rows = len(dataset.X)
|
|
80
|
+
n_splits = self._coerce_int(self.get_config('n_splits'), 5)
|
|
81
|
+
n_splits = min(max(n_splits, 2), n_rows) if n_rows > 1 else 1
|
|
82
|
+
shuffle = bool(self.get_config('shuffle'))
|
|
83
|
+
random_state = self._coerce_int(self.get_config('random_state'), 42)
|
|
84
|
+
smoothing = self._coerce_float(self.get_config('smoothing'), 1.0)
|
|
85
|
+
|
|
86
|
+
use_cv = n_rows >= 2 and n_splits >= 2
|
|
87
|
+
splits = []
|
|
88
|
+
if use_cv:
|
|
89
|
+
splits = self._build_splits(
|
|
90
|
+
dataset.X,
|
|
91
|
+
y_series,
|
|
92
|
+
dataset.type_of_target,
|
|
93
|
+
n_splits,
|
|
94
|
+
shuffle,
|
|
95
|
+
random_state
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if use_cv and not splits:
|
|
99
|
+
use_cv = False
|
|
100
|
+
|
|
101
|
+
encoded_train = pd.DataFrame(index=dataset.X.index, columns=self.columns, dtype=float)
|
|
102
|
+
|
|
103
|
+
for column in self.columns:
|
|
104
|
+
series = dataset.X[column]
|
|
105
|
+
mapping_full = self._fit_mapping(series, y_numeric, prior, smoothing)
|
|
106
|
+
self.encodings[column] = mapping_full
|
|
107
|
+
self.fallback_values[column] = prior
|
|
108
|
+
|
|
109
|
+
if use_cv:
|
|
110
|
+
oof = pd.Series(index=dataset.X.index, dtype=float)
|
|
111
|
+
for train_idx, val_idx in splits:
|
|
112
|
+
mapping_fold = self._fit_mapping(
|
|
113
|
+
series.iloc[train_idx],
|
|
114
|
+
y_numeric.iloc[train_idx],
|
|
115
|
+
prior,
|
|
116
|
+
smoothing
|
|
117
|
+
)
|
|
118
|
+
encoded = series.iloc[val_idx].map(mapping_fold)
|
|
119
|
+
oof.iloc[val_idx] = encoded
|
|
120
|
+
oof = oof.fillna(prior).astype(float)
|
|
121
|
+
else:
|
|
122
|
+
oof = series.map(mapping_full).fillna(prior).astype(float)
|
|
123
|
+
|
|
124
|
+
encoded_train[column] = oof
|
|
125
|
+
|
|
126
|
+
self.explanations.append(
|
|
127
|
+
f"Target-encoded `{column}` using {len(mapping_full)} categories."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
self._train_encoded = encoded_train
|
|
131
|
+
self._train_X = dataset.X
|
|
132
|
+
|
|
133
|
+
return self
|
|
134
|
+
|
|
135
|
+
def transform(self, X: pd.DataFrame) -> pd.DataFrame:
|
|
136
|
+
if not self.columns or X.empty:
|
|
137
|
+
return X
|
|
138
|
+
|
|
139
|
+
if self._train_X is not None and X is self._train_X and self._train_encoded is not None:
|
|
140
|
+
for column in self.columns:
|
|
141
|
+
if column in X.columns and column in self._train_encoded.columns:
|
|
142
|
+
X[column] = self._train_encoded[column].reindex(X.index).astype(float)
|
|
143
|
+
return X
|
|
144
|
+
|
|
145
|
+
for column in self.columns:
|
|
146
|
+
if column not in X.columns:
|
|
147
|
+
continue
|
|
148
|
+
mapping = self.encodings.get(column, {})
|
|
149
|
+
fallback = self.fallback_values.get(column, self.global_mean)
|
|
150
|
+
fallback = self._coerce_float(fallback, self.global_mean)
|
|
151
|
+
X[column] = X[column].map(mapping).fillna(fallback).astype(float)
|
|
152
|
+
|
|
153
|
+
return X
|
|
154
|
+
|
|
155
|
+
def suitable(self, dataset: Dataset) -> bool:
|
|
156
|
+
if dataset.y is None or dataset.X.empty:
|
|
157
|
+
return False
|
|
158
|
+
if dataset.type_of_target == 'survival':
|
|
159
|
+
return False
|
|
160
|
+
return bool(self._select_columns(dataset))
|
|
161
|
+
|
|
162
|
+
def priorize(self, candidate: Candidate = None) -> float:
|
|
163
|
+
if candidate is None or candidate.dataset.X.empty or candidate.dataset.y is None:
|
|
164
|
+
return 0.0
|
|
165
|
+
|
|
166
|
+
columns = self._select_columns(candidate.dataset)
|
|
167
|
+
if not columns:
|
|
168
|
+
return 0.0
|
|
169
|
+
|
|
170
|
+
total_rows = len(candidate.dataset.X)
|
|
171
|
+
if total_rows <= 0:
|
|
172
|
+
return 0.0
|
|
173
|
+
|
|
174
|
+
unique_counts = candidate.dataset.X[columns].nunique(dropna=True)
|
|
175
|
+
avg_cardinality = float((unique_counts / total_rows).mean())
|
|
176
|
+
|
|
177
|
+
total_columns = candidate.dataset.X.shape[1] or 1
|
|
178
|
+
cat_ratio = len(columns) / total_columns
|
|
179
|
+
|
|
180
|
+
return min(1.0, max(cat_ratio, avg_cardinality))
|
|
181
|
+
|
|
182
|
+
def _select_columns(self, dataset: Dataset) -> list[str]:
|
|
183
|
+
columns = dataset.get_columns_names_by_type(DataType.CATEGORICAL)
|
|
184
|
+
category_columns = list(dataset.X.select_dtypes(include=['category']).columns)
|
|
185
|
+
seen = set()
|
|
186
|
+
ordered = []
|
|
187
|
+
for column in columns + category_columns:
|
|
188
|
+
if column in dataset.X.columns and column not in seen:
|
|
189
|
+
ordered.append(column)
|
|
190
|
+
seen.add(column)
|
|
191
|
+
return ordered
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _coerce_target(y: pd.Series) -> pd.Series:
|
|
195
|
+
if y.empty:
|
|
196
|
+
return y.astype(float)
|
|
197
|
+
if pd.api.types.is_bool_dtype(y):
|
|
198
|
+
return y.astype(float)
|
|
199
|
+
if pd.api.types.is_numeric_dtype(y):
|
|
200
|
+
return y.astype(float)
|
|
201
|
+
codes, _ = pd.factorize(y, sort=True)
|
|
202
|
+
numeric = pd.Series(codes, index=y.index, dtype=float)
|
|
203
|
+
numeric[codes < 0] = np.nan
|
|
204
|
+
return numeric
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _fit_mapping(
|
|
208
|
+
series: pd.Series,
|
|
209
|
+
target: pd.Series,
|
|
210
|
+
prior: float,
|
|
211
|
+
smoothing: float
|
|
212
|
+
) -> dict[Any, float]:
|
|
213
|
+
if series.empty:
|
|
214
|
+
return {}
|
|
215
|
+
|
|
216
|
+
series_values = series.astype(object)
|
|
217
|
+
grouped = target.groupby(series_values).agg(['mean', 'count'])
|
|
218
|
+
if grouped.empty:
|
|
219
|
+
return {}
|
|
220
|
+
|
|
221
|
+
smoothing = float(smoothing)
|
|
222
|
+
if smoothing <= 0:
|
|
223
|
+
smooth = grouped['mean']
|
|
224
|
+
else:
|
|
225
|
+
smooth = (grouped['mean'] * grouped['count'] + prior * smoothing) \
|
|
226
|
+
/ (grouped['count'] + smoothing)
|
|
227
|
+
|
|
228
|
+
return smooth.to_dict()
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def _coerce_int(value: Any, default: int) -> int:
|
|
232
|
+
try:
|
|
233
|
+
return int(value)
|
|
234
|
+
except (TypeError, ValueError):
|
|
235
|
+
return default
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def _coerce_float(value: Any, default: float) -> float:
|
|
239
|
+
try:
|
|
240
|
+
return float(value)
|
|
241
|
+
except (TypeError, ValueError):
|
|
242
|
+
return default
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def _build_splits(
|
|
246
|
+
X: pd.DataFrame,
|
|
247
|
+
y: pd.Series,
|
|
248
|
+
target_type: str,
|
|
249
|
+
n_splits: int,
|
|
250
|
+
shuffle: bool,
|
|
251
|
+
random_state: int
|
|
252
|
+
) -> list[tuple[np.ndarray, np.ndarray]]:
|
|
253
|
+
if n_splits < 2:
|
|
254
|
+
return []
|
|
255
|
+
|
|
256
|
+
is_classification = target_type not in (
|
|
257
|
+
None,
|
|
258
|
+
'continuous',
|
|
259
|
+
'continuous-multioutput',
|
|
260
|
+
'survival'
|
|
261
|
+
)
|
|
262
|
+
if is_classification:
|
|
263
|
+
try:
|
|
264
|
+
splitter = StratifiedKFold(
|
|
265
|
+
n_splits=n_splits,
|
|
266
|
+
shuffle=shuffle,
|
|
267
|
+
random_state=random_state
|
|
268
|
+
)
|
|
269
|
+
return list(splitter.split(X, y))
|
|
270
|
+
except ValueError:
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
splitter = KFold(n_splits=n_splits, shuffle=shuffle, random_state=random_state)
|
|
274
|
+
return list(splitter.split(X))
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""[STEP] Normalize text columns before vectorization."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
import string
|
|
6
|
+
import textwrap
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS
|
|
10
|
+
|
|
11
|
+
from ...actionable import Actionable
|
|
12
|
+
from ...candidate import Candidate
|
|
13
|
+
from ...data_type import DataType
|
|
14
|
+
from ...dataset import Dataset
|
|
15
|
+
from ...decorators.all import is_step
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_PUNCTUATION_RE = re.compile(rf"[{re.escape(string.punctuation)}]+")
|
|
19
|
+
_MULTISPACE_RE = re.compile(r"\s+")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@is_step('cleaning')
|
|
23
|
+
class ActTextNormalizer(Actionable):
|
|
24
|
+
"""[STEP] Normalize text columns before vectorization."""
|
|
25
|
+
|
|
26
|
+
name: str = 'Text normalizer'
|
|
27
|
+
_usage: str = 'Use when normalizing TEXT/SHORT_TEXT before ActCountVectorizer. Applicable to free-form text columns needing consistent casing, punctuation, or stopword cleanup. Avoid when text must remain verbatim or ActCountVectorizer settings already cover normalization.'
|
|
28
|
+
_description: str = textwrap.dedent('''\
|
|
29
|
+
Normalize text columns with lowercasing, punctuation removal, and stopword filtering.''')
|
|
30
|
+
_description_long: str = textwrap.dedent('''\
|
|
31
|
+
Prepare text columns for downstream vectorizers by standardizing casing, removing
|
|
32
|
+
punctuation, dropping common stopwords, and cleaning up extra whitespace. This step
|
|
33
|
+
targets columns typed as TEXT or SHORT_TEXT and keeps the processing deterministic.''')
|
|
34
|
+
|
|
35
|
+
def __init__(self) -> None:
|
|
36
|
+
self.configuration = {
|
|
37
|
+
'lowercase': {
|
|
38
|
+
'description': 'Lowercase text before normalization.',
|
|
39
|
+
'default': True
|
|
40
|
+
},
|
|
41
|
+
'remove_punctuation': {
|
|
42
|
+
'description': 'Replace punctuation characters with spaces.',
|
|
43
|
+
'default': True
|
|
44
|
+
},
|
|
45
|
+
'remove_stopwords': {
|
|
46
|
+
'description': 'Remove stopwords from text.',
|
|
47
|
+
'default': True
|
|
48
|
+
},
|
|
49
|
+
'stopwords': {
|
|
50
|
+
'description': 'Stopwords to remove ("english", "none", or a list).',
|
|
51
|
+
'default': 'english'
|
|
52
|
+
},
|
|
53
|
+
'collapse_whitespace': {
|
|
54
|
+
'description': 'Collapse repeated whitespace and strip edges.',
|
|
55
|
+
'default': True
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
self.columns: list[str] = []
|
|
60
|
+
self.stop_words: set[str] = set()
|
|
61
|
+
self._stopword_pattern: re.Pattern | None = None
|
|
62
|
+
self._lowercase: bool = True
|
|
63
|
+
self._remove_punctuation: bool = True
|
|
64
|
+
self._remove_stopwords: bool = True
|
|
65
|
+
self._collapse_whitespace: bool = True
|
|
66
|
+
|
|
67
|
+
def fit(self, dataset: Dataset) -> Actionable:
|
|
68
|
+
self.columns = self._select_columns(dataset)
|
|
69
|
+
self.explanations = []
|
|
70
|
+
|
|
71
|
+
self._lowercase = self._coerce_bool(self.get_config('lowercase'), True)
|
|
72
|
+
self._remove_punctuation = self._coerce_bool(self.get_config('remove_punctuation'), True)
|
|
73
|
+
self._remove_stopwords = self._coerce_bool(self.get_config('remove_stopwords'), True)
|
|
74
|
+
self._collapse_whitespace = self._coerce_bool(self.get_config('collapse_whitespace'), True)
|
|
75
|
+
|
|
76
|
+
self.stop_words = self._normalize_stopwords(self._resolve_stopwords())
|
|
77
|
+
self._stopword_pattern = self._build_stopword_pattern(self.stop_words)
|
|
78
|
+
|
|
79
|
+
if not self.columns or dataset.X.empty:
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
if not self._has_enabled_operations():
|
|
83
|
+
self.columns = []
|
|
84
|
+
self.explanations.append('Text normalizer skipped: no enabled operations.')
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
operations = self._operations_summary()
|
|
88
|
+
for column in self.columns:
|
|
89
|
+
self.explanations.append(
|
|
90
|
+
f"Normalized text column **`{column}`** with {', '.join(operations)}."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
def transform(self, X: pd.DataFrame) -> pd.DataFrame:
|
|
96
|
+
if not self.columns or not self._has_enabled_operations():
|
|
97
|
+
return X
|
|
98
|
+
|
|
99
|
+
for column in self.columns:
|
|
100
|
+
if column not in X.columns:
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
series = X[column].fillna('').astype(str)
|
|
104
|
+
|
|
105
|
+
if self._lowercase:
|
|
106
|
+
series = series.str.lower()
|
|
107
|
+
if self._remove_punctuation:
|
|
108
|
+
series = series.str.replace(_PUNCTUATION_RE, ' ', regex=True)
|
|
109
|
+
if self._stopword_pattern is not None:
|
|
110
|
+
series = series.str.replace(self._stopword_pattern, ' ', regex=True)
|
|
111
|
+
if self._collapse_whitespace:
|
|
112
|
+
series = series.str.replace(_MULTISPACE_RE, ' ', regex=True).str.strip()
|
|
113
|
+
|
|
114
|
+
X[column] = series
|
|
115
|
+
|
|
116
|
+
return X
|
|
117
|
+
|
|
118
|
+
def suitable(self, dataset: Dataset) -> bool:
|
|
119
|
+
if dataset.X.empty:
|
|
120
|
+
return False
|
|
121
|
+
if not self._select_columns(dataset):
|
|
122
|
+
return False
|
|
123
|
+
return self._has_enabled_operations(configured_only=True)
|
|
124
|
+
|
|
125
|
+
def priorize(self, candidate: Candidate = None) -> float:
|
|
126
|
+
if candidate is None or candidate.dataset.X.empty:
|
|
127
|
+
return 0.0
|
|
128
|
+
|
|
129
|
+
columns = self._select_columns(candidate.dataset)
|
|
130
|
+
if not columns or not self._has_enabled_operations(configured_only=True):
|
|
131
|
+
return 0.0
|
|
132
|
+
|
|
133
|
+
total_columns = candidate.dataset.X.shape[1] or 1
|
|
134
|
+
return min(1.0, len(columns) / total_columns)
|
|
135
|
+
|
|
136
|
+
def _select_columns(self, dataset: Dataset) -> list[str]:
|
|
137
|
+
columns = dataset.get_columns_names_by_type([DataType.TEXT, DataType.SHORT_TEXT])
|
|
138
|
+
return [column for column in columns if column in dataset.X.columns]
|
|
139
|
+
|
|
140
|
+
def _resolve_stopwords(self) -> set[str]:
|
|
141
|
+
if not self._coerce_bool(self.get_config('remove_stopwords'), True):
|
|
142
|
+
return set()
|
|
143
|
+
|
|
144
|
+
stopwords = self.get_config('stopwords')
|
|
145
|
+
if stopwords is None:
|
|
146
|
+
return set()
|
|
147
|
+
|
|
148
|
+
if isinstance(stopwords, str):
|
|
149
|
+
normalized = stopwords.strip().lower()
|
|
150
|
+
if normalized in {'', 'none', 'false', 'off', 'no'}:
|
|
151
|
+
return set()
|
|
152
|
+
if normalized in {'english', 'sklearn'}:
|
|
153
|
+
return set(ENGLISH_STOP_WORDS)
|
|
154
|
+
tokens = re.split(r'[,;\s]+', stopwords.strip())
|
|
155
|
+
return {token for token in tokens if token}
|
|
156
|
+
|
|
157
|
+
if isinstance(stopwords, (list, tuple, set, frozenset)):
|
|
158
|
+
return {
|
|
159
|
+
str(word).strip()
|
|
160
|
+
for word in stopwords
|
|
161
|
+
if word is not None and str(word).strip()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return set()
|
|
165
|
+
|
|
166
|
+
def _normalize_stopwords(self, stopwords: set[str]) -> set[str]:
|
|
167
|
+
if not stopwords:
|
|
168
|
+
return set()
|
|
169
|
+
|
|
170
|
+
normalized: set[str] = set()
|
|
171
|
+
for word in stopwords:
|
|
172
|
+
token = str(word).strip()
|
|
173
|
+
if not token:
|
|
174
|
+
continue
|
|
175
|
+
if self._lowercase:
|
|
176
|
+
token = token.lower()
|
|
177
|
+
if self._remove_punctuation:
|
|
178
|
+
token = _PUNCTUATION_RE.sub(' ', token)
|
|
179
|
+
token = _MULTISPACE_RE.sub(' ', token).strip()
|
|
180
|
+
if not token:
|
|
181
|
+
continue
|
|
182
|
+
normalized.update(token.split())
|
|
183
|
+
|
|
184
|
+
return normalized
|
|
185
|
+
|
|
186
|
+
def _build_stopword_pattern(self, stopwords: set[str]) -> re.Pattern | None:
|
|
187
|
+
if not stopwords:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
escaped = [re.escape(word) for word in stopwords if word]
|
|
191
|
+
if not escaped:
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
escaped.sort(key=len, reverse=True)
|
|
195
|
+
flags = re.IGNORECASE if not self._lowercase else 0
|
|
196
|
+
pattern = r'\b(?:' + '|'.join(escaped) + r')\b'
|
|
197
|
+
return re.compile(pattern, flags=flags)
|
|
198
|
+
|
|
199
|
+
def _operations_summary(self) -> list[str]:
|
|
200
|
+
operations: list[str] = []
|
|
201
|
+
if self._lowercase:
|
|
202
|
+
operations.append('lowercasing')
|
|
203
|
+
if self._remove_punctuation:
|
|
204
|
+
operations.append('punctuation removal')
|
|
205
|
+
if self._stopword_pattern is not None:
|
|
206
|
+
operations.append('stopword filtering')
|
|
207
|
+
if self._collapse_whitespace:
|
|
208
|
+
operations.append('whitespace cleanup')
|
|
209
|
+
if not operations:
|
|
210
|
+
operations.append('normalization')
|
|
211
|
+
return operations
|
|
212
|
+
|
|
213
|
+
def _has_enabled_operations(self, configured_only: bool = False) -> bool:
|
|
214
|
+
if configured_only:
|
|
215
|
+
lowercase = self._coerce_bool(self.get_config('lowercase'), True)
|
|
216
|
+
remove_punctuation = self._coerce_bool(self.get_config('remove_punctuation'), True)
|
|
217
|
+
collapse_whitespace = self._coerce_bool(self.get_config('collapse_whitespace'), True)
|
|
218
|
+
remove_stopwords = self._coerce_bool(self.get_config('remove_stopwords'), True)
|
|
219
|
+
if lowercase or remove_punctuation or collapse_whitespace:
|
|
220
|
+
return True
|
|
221
|
+
if remove_stopwords:
|
|
222
|
+
return bool(self._resolve_stopwords())
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
if self._lowercase or self._remove_punctuation or self._collapse_whitespace:
|
|
226
|
+
return True
|
|
227
|
+
return self._stopword_pattern is not None
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def _coerce_bool(value: object, default: bool) -> bool:
|
|
231
|
+
if isinstance(value, bool):
|
|
232
|
+
return value
|
|
233
|
+
if isinstance(value, str):
|
|
234
|
+
normalized = value.strip().lower()
|
|
235
|
+
if normalized in {'true', '1', 'yes', 'y'}:
|
|
236
|
+
return True
|
|
237
|
+
if normalized in {'false', '0', 'no', 'n'}:
|
|
238
|
+
return False
|
|
239
|
+
if value is None:
|
|
240
|
+
return default
|
|
241
|
+
return bool(value)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""[STEP] Vectorize textual columns with TF-IDF"""
|
|
2
|
+
import textwrap
|
|
3
|
+
from typing import Any
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
|
6
|
+
from ...actionable import Actionable
|
|
7
|
+
from ...dataset import Dataset
|
|
8
|
+
from ...candidate import Candidate
|
|
9
|
+
from ...decorators.all import is_step
|
|
10
|
+
from ...data_type import DataType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@is_step('cleaning')
|
|
14
|
+
class ActTfIdf(Actionable):
|
|
15
|
+
"""[STEP] Vectorize textual columns with TF-IDF"""
|
|
16
|
+
|
|
17
|
+
name: str = 'TF-IDF'
|
|
18
|
+
_description: str = textwrap.dedent('''\
|
|
19
|
+
Process "Term Frequency / Inversed Document Frequency"
|
|
20
|
+
over a list of textual columns''')
|
|
21
|
+
_description_long: str = textwrap.dedent('''\
|
|
22
|
+
This algorithm is used to evaluate the importance of a word inside
|
|
23
|
+
it\'s corpus. A word with a lot of repetitions will
|
|
24
|
+
have more importance than a word appearing once.''')
|
|
25
|
+
_usage: str = 'Use when short text needs weighted term features; prefer over ActCountVectorizer for damping frequent terms. Applicable to short text columns. Avoid when you need raw counts or want to drop the column (ActCountVectorizer, ActDropCategoricalColumn).'
|
|
26
|
+
refs: list[dict[str, Any]] = [
|
|
27
|
+
{
|
|
28
|
+
'year': 1972,
|
|
29
|
+
'name': 'A STATISTICAL INTERPRETATION OF TERM SPECIFICITY AND ITS APPLICATION \
|
|
30
|
+
IN RETRIEVAL',
|
|
31
|
+
'authors': [
|
|
32
|
+
'Karen Sparck Jones'
|
|
33
|
+
],
|
|
34
|
+
'doi': 'https://doi.org/10.1108/eb026526',
|
|
35
|
+
'publisher': 'Journal of Documentation Vol.21, No.1, page 11--21'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
self.columns: list[tuple[str, TfidfVectorizer]] = None
|
|
41
|
+
|
|
42
|
+
def fit(self, dataset: Dataset) -> Actionable:
|
|
43
|
+
self.columns = []
|
|
44
|
+
for column in dataset.get_columns_names_by_type([DataType.SHORT_TEXT]):
|
|
45
|
+
values = dataset.X[column].fillna('')
|
|
46
|
+
vectorizer = TfidfVectorizer().fit(values)
|
|
47
|
+
self.columns.append((column, vectorizer))
|
|
48
|
+
|
|
49
|
+
feature_names = { c: v.get_feature_names_out() for c, v in self.columns }
|
|
50
|
+
self.explanations = [
|
|
51
|
+
f'Encoded text column **`{c}`** into **{len(v)}** new columns.'
|
|
52
|
+
for c, v in feature_names.items() if len(v) > 0
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
def suitable(self, dataset: Dataset) -> bool:
|
|
58
|
+
return bool(dataset.get_columns_names_by_type(DataType.SHORT_TEXT))
|
|
59
|
+
|
|
60
|
+
def transform(self, X: pd.DataFrame) -> pd.DataFrame:
|
|
61
|
+
"""Apply TF-IDF on textual columns.
|
|
62
|
+
|
|
63
|
+
:param pd.DataFrame x: DataFrame to transform.
|
|
64
|
+
:return: Transformed dataset.
|
|
65
|
+
"""
|
|
66
|
+
# Without Reset index, the join with vector_df will create NAN (index mismatch)
|
|
67
|
+
X = X.reset_index(drop=True)
|
|
68
|
+
|
|
69
|
+
for name, vectorizer in self.columns:
|
|
70
|
+
transformed = vectorizer.transform(X[name].fillna(''))
|
|
71
|
+
|
|
72
|
+
new_names = list(map(lambda x: "_".join([name, x]), vectorizer.get_feature_names_out())) # pylint: disable=cell-var-from-loop
|
|
73
|
+
vector_df = pd.DataFrame(transformed.todense(), columns=new_names)
|
|
74
|
+
|
|
75
|
+
X = pd.concat([X, vector_df], axis=1).drop([name], axis=1)
|
|
76
|
+
|
|
77
|
+
return X
|
|
78
|
+
|
|
79
|
+
def priorize(self, candidate: Candidate = None) -> float:
|
|
80
|
+
return 0.4
|