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
iaml/statistic.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""[STATISTIC] Parent of descriptive statistics."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .dataset import Dataset
|
|
8
|
+
from .reference import Reference
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Statistic:
|
|
12
|
+
"""[STATISTIC] Parent of descriptive statistics."""
|
|
13
|
+
|
|
14
|
+
name: str = ""
|
|
15
|
+
"""Name of the statistic."""
|
|
16
|
+
|
|
17
|
+
_description: str = ""
|
|
18
|
+
"""Short description of the statistic."""
|
|
19
|
+
|
|
20
|
+
_description_long: str = ""
|
|
21
|
+
"""Long description of the statistic."""
|
|
22
|
+
|
|
23
|
+
refs: list[dict[str, Any]] = []
|
|
24
|
+
"""List of references for this statistic."""
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def all_subclasses(cls) -> list['Statistic']:
|
|
28
|
+
"""Return all statistic subclasses."""
|
|
29
|
+
subclasses = cls.__subclasses__()
|
|
30
|
+
for subclass in subclasses:
|
|
31
|
+
subclasses += subclass.all_subclasses()
|
|
32
|
+
return subclasses
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def get_refs(cls) -> list[Reference]:
|
|
36
|
+
"""Get bibliography references."""
|
|
37
|
+
if hasattr(cls, 'refs'):
|
|
38
|
+
return [Reference(ref, cls.__name__) for ref in cls.refs]
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def description(self) -> str:
|
|
43
|
+
"""Return the short description."""
|
|
44
|
+
return self._description.replace('\n', '')
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def description_long(self) -> str:
|
|
48
|
+
"""Return the long description."""
|
|
49
|
+
return self._description_long.replace('\n', '')
|
|
50
|
+
|
|
51
|
+
def explain(self) -> str:
|
|
52
|
+
"""Describe statistic."""
|
|
53
|
+
return self.description_long
|
|
54
|
+
|
|
55
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
56
|
+
"""Compute the statistic on the dataset."""
|
|
57
|
+
raise NotImplementedError('Subclass must implement abstract method')
|
|
58
|
+
|
|
59
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
60
|
+
"""Return True if the statistic applies to the dataset."""
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def name(self) -> str:
|
|
65
|
+
"""Return the statistic formatted name."""
|
|
66
|
+
return ' '.join(x.title() for x in str(self).split('_'))
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""All statistics to describe original dataset."""
|
|
2
|
+
from .count import CountStatistic
|
|
3
|
+
from .mean import MeanStatistic
|
|
4
|
+
from .minmax import BoundStatistic
|
|
5
|
+
from .mode import ModeStatistic
|
|
6
|
+
from .quantile import QuantileStatistic
|
|
7
|
+
from .range import RangeStatistic
|
|
8
|
+
from .stdev import StdevStatistic
|
|
9
|
+
from .value_counts import ValueCountsStatistic
|
|
10
|
+
from .variance import VarianceStatistic
|
|
11
|
+
from .violin import ViolinStatistic
|
|
12
|
+
|
|
13
|
+
from .summary_table_statistic import SummaryTableStatistic
|
|
14
|
+
from .data_type_summary_statistic import DataTypeSummaryStatistic
|
|
15
|
+
from .duplicate_row_statistic import DuplicateRowStatistic
|
|
16
|
+
from .unique_count_statistic import UniqueCountStatistic
|
|
17
|
+
from .missing_rate_statistic import MissingRateStatistic
|
|
18
|
+
from .cardinality_ratio_statistic import CardinalityRatioStatistic
|
|
19
|
+
from .median_statistic import MedianStatistic
|
|
20
|
+
from .iqr_statistic import IQRStatistic
|
|
21
|
+
from .mad_statistic import MADStatistic
|
|
22
|
+
from .coef_variation_statistic import CoefVariationStatistic
|
|
23
|
+
from .outlier_count_iqr_statistic import OutlierCountIQRStatistic
|
|
24
|
+
from .skewness import SkewnessStatistic
|
|
25
|
+
from .kurtosis import KurtosisStatistic
|
|
26
|
+
from .top_k_value_counts import TopKValueCountsStatistic
|
|
27
|
+
from .entropy_statistic import EntropyStatistic
|
|
28
|
+
from .rare_category_rate import RareCategoryRateStatistic
|
|
29
|
+
from .most_frequent_ratio import MostFrequentRatioStatistic
|
|
30
|
+
from .category_cooccurrence_statistic import CategoryCooccurrenceStatistic
|
|
31
|
+
from .grouped_mean_statistic import GroupedMeanStatistic
|
|
32
|
+
from .effect_size_statistic import EffectSizeStatistic
|
|
33
|
+
from .anova_statistic import ANOVAStatistic
|
|
34
|
+
from .chi_square_statistic import ChiSquareStatistic
|
|
35
|
+
from .correlation_with_target import CorrelationWithTargetStatistic
|
|
36
|
+
from .event_rate_statistic import EventRateStatistic
|
|
37
|
+
from .time_summary_statistic import TimeSummaryStatistic
|
|
38
|
+
from .time_by_group_statistic import TimeByGroupStatistic
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"CountStatistic",
|
|
42
|
+
"MeanStatistic",
|
|
43
|
+
"BoundStatistic",
|
|
44
|
+
"ModeStatistic",
|
|
45
|
+
"QuantileStatistic",
|
|
46
|
+
"RangeStatistic",
|
|
47
|
+
"StdevStatistic",
|
|
48
|
+
"ValueCountsStatistic",
|
|
49
|
+
"VarianceStatistic",
|
|
50
|
+
"ViolinStatistic",
|
|
51
|
+
"SummaryTableStatistic",
|
|
52
|
+
"DataTypeSummaryStatistic",
|
|
53
|
+
"DuplicateRowStatistic",
|
|
54
|
+
"UniqueCountStatistic",
|
|
55
|
+
"MissingRateStatistic",
|
|
56
|
+
"CardinalityRatioStatistic",
|
|
57
|
+
"MedianStatistic",
|
|
58
|
+
"IQRStatistic",
|
|
59
|
+
"MADStatistic",
|
|
60
|
+
"CoefVariationStatistic",
|
|
61
|
+
"OutlierCountIQRStatistic",
|
|
62
|
+
"SkewnessStatistic",
|
|
63
|
+
"KurtosisStatistic",
|
|
64
|
+
"TopKValueCountsStatistic",
|
|
65
|
+
"EntropyStatistic",
|
|
66
|
+
"RareCategoryRateStatistic",
|
|
67
|
+
"MostFrequentRatioStatistic",
|
|
68
|
+
"CategoryCooccurrenceStatistic",
|
|
69
|
+
"GroupedMeanStatistic",
|
|
70
|
+
"EffectSizeStatistic",
|
|
71
|
+
"ANOVAStatistic",
|
|
72
|
+
"ChiSquareStatistic",
|
|
73
|
+
"CorrelationWithTargetStatistic",
|
|
74
|
+
"EventRateStatistic",
|
|
75
|
+
"TimeSummaryStatistic",
|
|
76
|
+
"TimeByGroupStatistic",
|
|
77
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""[STATISTIC] ANOVA."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from scipy import stats
|
|
7
|
+
|
|
8
|
+
from ..dataset import Dataset
|
|
9
|
+
from ..data_type import DataType
|
|
10
|
+
from ..statistic import Statistic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ANOVAStatistic(Statistic):
|
|
14
|
+
"""[STATISTIC] ANOVA."""
|
|
15
|
+
|
|
16
|
+
name: str = "ANOVA"
|
|
17
|
+
_description: str = textwrap.dedent("""\
|
|
18
|
+
ANOVA tests whether numerical feature means differ across classes.
|
|
19
|
+
""")
|
|
20
|
+
_description_long: str = textwrap.dedent("""\
|
|
21
|
+
ANOVA computes one-way p-values for numerical columns across classes.
|
|
22
|
+
Per-class columns report ANOVA p-values comparing each class against the
|
|
23
|
+
rest of the data, while the `_all` suffix reports the overall ANOVA p-value.
|
|
24
|
+
""")
|
|
25
|
+
refs: list[dict] = []
|
|
26
|
+
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
return 'anova'
|
|
29
|
+
|
|
30
|
+
def _anova_pvalue(self, groups: list[pd.Series]) -> float | None:
|
|
31
|
+
cleaned = []
|
|
32
|
+
for values in groups:
|
|
33
|
+
if values is None:
|
|
34
|
+
continue
|
|
35
|
+
series = values.dropna()
|
|
36
|
+
if series.size > 1:
|
|
37
|
+
cleaned.append(series.to_numpy())
|
|
38
|
+
if len(cleaned) < 2:
|
|
39
|
+
return None
|
|
40
|
+
result = stats.f_oneway(*cleaned)
|
|
41
|
+
pvalue = float(result.pvalue)
|
|
42
|
+
if pd.isna(pvalue):
|
|
43
|
+
return None
|
|
44
|
+
return pvalue
|
|
45
|
+
|
|
46
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
47
|
+
"""Compute ANOVA p-values for numerical columns."""
|
|
48
|
+
if dataset.type_of_target in ['survival', 'continuous']:
|
|
49
|
+
return pd.DataFrame()
|
|
50
|
+
|
|
51
|
+
numeric_columns = dataset.get_columns_names_by_type(DataType.NUMERIC)
|
|
52
|
+
if not numeric_columns:
|
|
53
|
+
return pd.DataFrame()
|
|
54
|
+
|
|
55
|
+
class_labels = list(pd.unique(dataset.y))
|
|
56
|
+
class_masks = {label: (dataset.y == label) for label in class_labels}
|
|
57
|
+
|
|
58
|
+
data = []
|
|
59
|
+
columns = []
|
|
60
|
+
|
|
61
|
+
for col in numeric_columns:
|
|
62
|
+
col_values = dataset.X[col]
|
|
63
|
+
overall_groups = [col_values[mask] for mask in class_masks.values()]
|
|
64
|
+
overall_pvalue = self._anova_pvalue(overall_groups)
|
|
65
|
+
|
|
66
|
+
for label in ['all'] + class_labels:
|
|
67
|
+
columns.append(f"{col}_{label}")
|
|
68
|
+
if label == 'all':
|
|
69
|
+
data.append(overall_pvalue)
|
|
70
|
+
else:
|
|
71
|
+
mask = class_masks[label]
|
|
72
|
+
class_values = col_values[mask]
|
|
73
|
+
rest_values = col_values[~mask]
|
|
74
|
+
data.append(self._anova_pvalue([class_values, rest_values]))
|
|
75
|
+
|
|
76
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
77
|
+
|
|
78
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
79
|
+
"""Does this statistic apply to the dataset?"""
|
|
80
|
+
return dataset.type_of_target not in ['survival', 'continuous']
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""[STATISTIC] Cardinality Ratio."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from ..dataset import Dataset
|
|
8
|
+
from ..data_type import DataType
|
|
9
|
+
from ..statistic import Statistic
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CardinalityRatioStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Cardinality Ratio."""
|
|
14
|
+
|
|
15
|
+
name: str = "Cardinality Ratio"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Cardinality ratio measures the ratio of unique values to total rows.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Cardinality ratio measures the ratio of unique values to total rows,
|
|
21
|
+
optionally per class for classification.
|
|
22
|
+
""")
|
|
23
|
+
refs: list[dict] = []
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return 'cardinality_ratio'
|
|
27
|
+
|
|
28
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
29
|
+
"""Compute cardinality ratios for each column."""
|
|
30
|
+
if dataset.type_of_target == 'survival':
|
|
31
|
+
return pd.DataFrame()
|
|
32
|
+
|
|
33
|
+
data = []
|
|
34
|
+
columns = []
|
|
35
|
+
|
|
36
|
+
if dataset.type_of_target == 'continuous':
|
|
37
|
+
n_rows = int(dataset.X.shape[0])
|
|
38
|
+
for col in dataset.X.columns:
|
|
39
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
40
|
+
columns.append(col)
|
|
41
|
+
n_unique = dataset.X[col].nunique(dropna=True)
|
|
42
|
+
ratio = n_unique / n_rows if n_rows else 0.0
|
|
43
|
+
data.append(ratio)
|
|
44
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
45
|
+
|
|
46
|
+
class_labels = list(pd.unique(dataset.y))
|
|
47
|
+
for col in dataset.X.columns:
|
|
48
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
49
|
+
for label in ['all'] + class_labels:
|
|
50
|
+
columns.append(f"{col}_{label}")
|
|
51
|
+
if label == 'all':
|
|
52
|
+
values = dataset.X[col]
|
|
53
|
+
else:
|
|
54
|
+
values = dataset.X.loc[dataset.y == label][col]
|
|
55
|
+
n_rows = int(values.shape[0])
|
|
56
|
+
n_unique = values.nunique(dropna=True)
|
|
57
|
+
ratio = n_unique / n_rows if n_rows else 0.0
|
|
58
|
+
data.append(ratio)
|
|
59
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
60
|
+
|
|
61
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
62
|
+
"""Does this statistic apply to the dataset?"""
|
|
63
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""[STATISTIC] Category Cooccurrence."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import itertools
|
|
5
|
+
import textwrap
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from ..dataset import Dataset
|
|
9
|
+
from ..data_type import DataType
|
|
10
|
+
from ..statistic import Statistic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CategoryCooccurrenceStatistic(Statistic):
|
|
14
|
+
"""[STATISTIC] Category Cooccurrence."""
|
|
15
|
+
|
|
16
|
+
name: str = "Category Cooccurrence"
|
|
17
|
+
_description: str = textwrap.dedent("""\
|
|
18
|
+
Category cooccurrence counts value pairs between categorical column pairs.
|
|
19
|
+
""")
|
|
20
|
+
_description_long: str = textwrap.dedent("""\
|
|
21
|
+
Category cooccurrence counts value pairs between categorical column pairs,
|
|
22
|
+
optionally per class for classification.
|
|
23
|
+
""")
|
|
24
|
+
refs: list[dict] = []
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
return 'category_cooccurrence'
|
|
28
|
+
|
|
29
|
+
def _select_columns(self, dataset: Dataset) -> list[str]:
|
|
30
|
+
columns = dataset.get_columns_names_by_type(DataType.CATEGORICAL)
|
|
31
|
+
category_columns = list(dataset.X.select_dtypes(include=['category']).columns)
|
|
32
|
+
seen = set()
|
|
33
|
+
ordered = []
|
|
34
|
+
for column in columns + category_columns:
|
|
35
|
+
if column in dataset.X.columns and column not in seen:
|
|
36
|
+
ordered.append(column)
|
|
37
|
+
seen.add(column)
|
|
38
|
+
return ordered
|
|
39
|
+
|
|
40
|
+
def _cooccurrence(self, frame: pd.DataFrame, col_a: str, col_b: str) -> list[tuple[tuple, int]]:
|
|
41
|
+
values = frame[[col_a, col_b]].dropna()
|
|
42
|
+
if values.empty:
|
|
43
|
+
return []
|
|
44
|
+
counts = values.groupby([col_a, col_b], sort=True).size()
|
|
45
|
+
return [((idx_a, idx_b), int(count)) for (idx_a, idx_b), count in counts.items()]
|
|
46
|
+
|
|
47
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
48
|
+
"""Compute category cooccurrence for categorical column pairs."""
|
|
49
|
+
if dataset.type_of_target == 'survival':
|
|
50
|
+
return pd.DataFrame()
|
|
51
|
+
|
|
52
|
+
columns = self._select_columns(dataset)
|
|
53
|
+
if len(columns) < 2:
|
|
54
|
+
return pd.DataFrame()
|
|
55
|
+
|
|
56
|
+
pairs = list(itertools.combinations(columns, 2))
|
|
57
|
+
data = []
|
|
58
|
+
df_columns = []
|
|
59
|
+
|
|
60
|
+
if dataset.type_of_target == 'continuous':
|
|
61
|
+
for col_a, col_b in pairs:
|
|
62
|
+
df_columns.append(f"{col_a}__{col_b}")
|
|
63
|
+
data.append(self._cooccurrence(dataset.X, col_a, col_b))
|
|
64
|
+
return pd.DataFrame([data], index=[str(self)], columns=df_columns)
|
|
65
|
+
|
|
66
|
+
class_labels = list(pd.unique(dataset.y))
|
|
67
|
+
for col_a, col_b in pairs:
|
|
68
|
+
for label in ['all'] + class_labels:
|
|
69
|
+
df_columns.append(f"{col_a}__{col_b}_{label}")
|
|
70
|
+
if label == 'all':
|
|
71
|
+
frame = dataset.X
|
|
72
|
+
else:
|
|
73
|
+
frame = dataset.X.loc[dataset.y == label]
|
|
74
|
+
data.append(self._cooccurrence(frame, col_a, col_b))
|
|
75
|
+
return pd.DataFrame([data], index=[str(self)], columns=df_columns)
|
|
76
|
+
|
|
77
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
78
|
+
"""Does this statistic apply to the dataset?"""
|
|
79
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""[STATISTIC] Chi-square."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from scipy.stats import chi2_contingency
|
|
7
|
+
|
|
8
|
+
from ..dataset import Dataset
|
|
9
|
+
from ..data_type import DataType
|
|
10
|
+
from ..statistic import Statistic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ChiSquareStatistic(Statistic):
|
|
14
|
+
"""[STATISTIC] Chi-square."""
|
|
15
|
+
|
|
16
|
+
name: str = "Chi-square"
|
|
17
|
+
_description: str = textwrap.dedent("""\
|
|
18
|
+
Chi-square measures association between categorical columns and classes.
|
|
19
|
+
""")
|
|
20
|
+
_description_long: str = textwrap.dedent("""\
|
|
21
|
+
Chi-square computes the chi-square test statistic for each categorical column
|
|
22
|
+
against the target classes. The `_all` suffix reports the multi-class statistic,
|
|
23
|
+
while per-class values compare a class against the rest.
|
|
24
|
+
""")
|
|
25
|
+
refs: list[dict] = []
|
|
26
|
+
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
return 'chi_square'
|
|
29
|
+
|
|
30
|
+
def _select_columns(self, dataset: Dataset) -> list[str]:
|
|
31
|
+
columns = dataset.get_columns_names_by_type(DataType.CATEGORICAL)
|
|
32
|
+
category_columns = list(dataset.X.select_dtypes(include=['category']).columns)
|
|
33
|
+
seen = set()
|
|
34
|
+
ordered = []
|
|
35
|
+
for column in columns + category_columns:
|
|
36
|
+
if column in dataset.X.columns and column not in seen:
|
|
37
|
+
ordered.append(column)
|
|
38
|
+
seen.add(column)
|
|
39
|
+
return ordered
|
|
40
|
+
|
|
41
|
+
def _chi2_statistic(self, feature: pd.Series, target: pd.Series) -> float | None:
|
|
42
|
+
if not isinstance(target, pd.Series):
|
|
43
|
+
target = pd.Series(target, index=feature.index)
|
|
44
|
+
mask = feature.notna() & target.notna()
|
|
45
|
+
if not mask.any():
|
|
46
|
+
return None
|
|
47
|
+
table = pd.crosstab(feature[mask], target[mask])
|
|
48
|
+
if table.empty or table.shape[0] < 2 or table.shape[1] < 2:
|
|
49
|
+
return None
|
|
50
|
+
stat, _, _, _ = chi2_contingency(table, correction=False)
|
|
51
|
+
if pd.isna(stat):
|
|
52
|
+
return None
|
|
53
|
+
return float(stat)
|
|
54
|
+
|
|
55
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
56
|
+
"""Compute chi-square statistics for categorical columns."""
|
|
57
|
+
if dataset.type_of_target in ['survival', 'continuous']:
|
|
58
|
+
return pd.DataFrame()
|
|
59
|
+
|
|
60
|
+
columns = self._select_columns(dataset)
|
|
61
|
+
if not columns:
|
|
62
|
+
return pd.DataFrame()
|
|
63
|
+
|
|
64
|
+
class_labels = list(pd.unique(dataset.y))
|
|
65
|
+
data = []
|
|
66
|
+
df_columns = []
|
|
67
|
+
|
|
68
|
+
for col in columns:
|
|
69
|
+
for label in ['all'] + class_labels:
|
|
70
|
+
df_columns.append(f"{col}_{label}")
|
|
71
|
+
if label == 'all':
|
|
72
|
+
data.append(self._chi2_statistic(dataset.X[col], dataset.y))
|
|
73
|
+
else:
|
|
74
|
+
binary_target = pd.Series(dataset.y == label, index=dataset.X.index)
|
|
75
|
+
data.append(self._chi2_statistic(dataset.X[col], binary_target))
|
|
76
|
+
|
|
77
|
+
return pd.DataFrame([data], index=[str(self)], columns=df_columns)
|
|
78
|
+
|
|
79
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
80
|
+
"""Does this statistic apply to the dataset?"""
|
|
81
|
+
return dataset.type_of_target not in ['survival', 'continuous']
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""[STATISTIC] Coefficient of variation."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from ..dataset import Dataset
|
|
8
|
+
from ..data_type import DataType
|
|
9
|
+
from ..statistic import Statistic
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CoefVariationStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Coefficient of variation."""
|
|
14
|
+
|
|
15
|
+
name: str = "Coef Variation"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Coefficient of variation measures relative dispersion (std/mean).
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Coefficient of variation measures relative dispersion (std/mean),
|
|
21
|
+
optionally per class for classification.
|
|
22
|
+
""")
|
|
23
|
+
refs: list[dict] = []
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return 'coef_variation'
|
|
27
|
+
|
|
28
|
+
def _coef_variation(self, values: pd.Series) -> float | None:
|
|
29
|
+
mean = values.mean()
|
|
30
|
+
if pd.isna(mean) or mean == 0:
|
|
31
|
+
return None
|
|
32
|
+
stdev = values.std()
|
|
33
|
+
if pd.isna(stdev):
|
|
34
|
+
return None
|
|
35
|
+
return stdev / mean
|
|
36
|
+
|
|
37
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
38
|
+
"""Compute coefficient of variation for numerical columns."""
|
|
39
|
+
if dataset.type_of_target == 'survival':
|
|
40
|
+
return pd.DataFrame()
|
|
41
|
+
|
|
42
|
+
data = []
|
|
43
|
+
columns = []
|
|
44
|
+
|
|
45
|
+
if dataset.type_of_target == 'continuous':
|
|
46
|
+
for col in dataset.X.columns:
|
|
47
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
48
|
+
columns.append(col)
|
|
49
|
+
if dataset.columns_types[col][1] == DataType.NUMERIC:
|
|
50
|
+
data.append(self._coef_variation(dataset.X[col]))
|
|
51
|
+
else:
|
|
52
|
+
data.append(None)
|
|
53
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
54
|
+
|
|
55
|
+
class_labels = list(pd.unique(dataset.y))
|
|
56
|
+
for col in dataset.X.columns:
|
|
57
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
58
|
+
for label in ['all'] + class_labels:
|
|
59
|
+
columns.append(f"{col}_{label}")
|
|
60
|
+
if dataset.columns_types[col][1] == DataType.NUMERIC:
|
|
61
|
+
if label == 'all':
|
|
62
|
+
values = dataset.X[col]
|
|
63
|
+
else:
|
|
64
|
+
values = dataset.X.loc[dataset.y == label][col]
|
|
65
|
+
data.append(self._coef_variation(values))
|
|
66
|
+
else:
|
|
67
|
+
data.append(None)
|
|
68
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
69
|
+
|
|
70
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
71
|
+
"""Does this statistic apply to the dataset?"""
|
|
72
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""[STATISTIC] Correlation with target."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from ..dataset import Dataset
|
|
8
|
+
from ..data_type import DataType
|
|
9
|
+
from ..statistic import Statistic
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CorrelationWithTargetStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Correlation with target."""
|
|
14
|
+
|
|
15
|
+
name: str = "Correlation with target"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Correlation (continuous targets) or eta (classification) for numeric columns.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
For continuous targets, this reports Pearson correlations between each numeric
|
|
21
|
+
feature and the target. For classification targets, this reports the eta
|
|
22
|
+
correlation ratio between each numeric feature and the target classes. The
|
|
23
|
+
`_all` suffix reports the multi-class statistic, while per-class values compare
|
|
24
|
+
a class against the rest.
|
|
25
|
+
""")
|
|
26
|
+
refs: list[dict] = []
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
return 'correlation_with_target'
|
|
30
|
+
|
|
31
|
+
def _pearson_corr(self, feature: pd.Series, target: pd.Series) -> float | None:
|
|
32
|
+
if not isinstance(target, pd.Series):
|
|
33
|
+
target = pd.Series(target, index=feature.index)
|
|
34
|
+
values = pd.to_numeric(feature, errors='coerce')
|
|
35
|
+
target_values = pd.to_numeric(target, errors='coerce')
|
|
36
|
+
mask = values.notna() & target_values.notna()
|
|
37
|
+
if mask.sum() < 2:
|
|
38
|
+
return None
|
|
39
|
+
if values[mask].nunique() < 2 or target_values[mask].nunique() < 2:
|
|
40
|
+
return None
|
|
41
|
+
corr = values[mask].corr(target_values[mask])
|
|
42
|
+
if pd.isna(corr):
|
|
43
|
+
return None
|
|
44
|
+
return float(corr)
|
|
45
|
+
|
|
46
|
+
def _eta(self, feature: pd.Series, target: pd.Series) -> float | None:
|
|
47
|
+
if not isinstance(target, pd.Series):
|
|
48
|
+
target = pd.Series(target, index=feature.index)
|
|
49
|
+
mask = feature.notna() & target.notna()
|
|
50
|
+
if not mask.any():
|
|
51
|
+
return None
|
|
52
|
+
values = feature[mask]
|
|
53
|
+
groups = target[mask]
|
|
54
|
+
if groups.nunique() < 2:
|
|
55
|
+
return None
|
|
56
|
+
overall_mean = values.mean()
|
|
57
|
+
ss_total = ((values - overall_mean) ** 2).sum()
|
|
58
|
+
if ss_total == 0:
|
|
59
|
+
return None
|
|
60
|
+
ss_between = 0.0
|
|
61
|
+
for _, group_values in values.groupby(groups):
|
|
62
|
+
count = group_values.size
|
|
63
|
+
if count == 0:
|
|
64
|
+
continue
|
|
65
|
+
mean = group_values.mean()
|
|
66
|
+
ss_between += count * (mean - overall_mean) ** 2
|
|
67
|
+
eta_sq = ss_between / ss_total
|
|
68
|
+
if pd.isna(eta_sq):
|
|
69
|
+
return None
|
|
70
|
+
return float(eta_sq) ** 0.5
|
|
71
|
+
|
|
72
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
73
|
+
"""Compute correlations or eta with the target for numeric columns."""
|
|
74
|
+
if dataset.type_of_target == 'survival':
|
|
75
|
+
return pd.DataFrame()
|
|
76
|
+
|
|
77
|
+
numeric_columns = dataset.get_columns_names_by_type(DataType.NUMERIC)
|
|
78
|
+
if not numeric_columns:
|
|
79
|
+
return pd.DataFrame()
|
|
80
|
+
|
|
81
|
+
data = []
|
|
82
|
+
columns = []
|
|
83
|
+
|
|
84
|
+
if dataset.type_of_target == 'continuous':
|
|
85
|
+
for col in numeric_columns:
|
|
86
|
+
columns.append(col)
|
|
87
|
+
data.append(self._pearson_corr(dataset.X[col], dataset.y))
|
|
88
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
89
|
+
|
|
90
|
+
class_labels = list(pd.unique(dataset.y))
|
|
91
|
+
for col in numeric_columns:
|
|
92
|
+
overall_eta = self._eta(dataset.X[col], dataset.y)
|
|
93
|
+
for label in ['all'] + class_labels:
|
|
94
|
+
columns.append(f"{col}_{label}")
|
|
95
|
+
if label == 'all':
|
|
96
|
+
data.append(overall_eta)
|
|
97
|
+
else:
|
|
98
|
+
binary_target = pd.Series(dataset.y == label, index=dataset.X.index)
|
|
99
|
+
data.append(self._eta(dataset.X[col], binary_target))
|
|
100
|
+
|
|
101
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
102
|
+
|
|
103
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
104
|
+
"""Does this statistic apply to the dataset?"""
|
|
105
|
+
return dataset.type_of_target != 'survival'
|