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/statistics/count.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""[STATISTIC] Count."""
|
|
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 CountStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Count."""
|
|
14
|
+
|
|
15
|
+
name: str = "Count"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Count measures the number of non-null values for each column.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Count measures the number of non-null values (or null values when requested)
|
|
21
|
+
for each column.
|
|
22
|
+
""")
|
|
23
|
+
refs: list[dict] = []
|
|
24
|
+
|
|
25
|
+
count_to_compute = [True, False]
|
|
26
|
+
|
|
27
|
+
def __init__(self, null_count: bool = False) -> None:
|
|
28
|
+
self.null_count = null_count
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
return 'null_count' if self.null_count else 'count'
|
|
32
|
+
|
|
33
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
34
|
+
"""Compute counts for each column."""
|
|
35
|
+
if dataset.type_of_target == 'survival':
|
|
36
|
+
return pd.DataFrame()
|
|
37
|
+
|
|
38
|
+
df = pd.DataFrame()
|
|
39
|
+
if dataset.type_of_target == 'continuous':
|
|
40
|
+
for cnt in CountStatistic.count_to_compute:
|
|
41
|
+
self.null_count = cnt
|
|
42
|
+
data = []
|
|
43
|
+
columns = []
|
|
44
|
+
for col in dataset.X.columns:
|
|
45
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
46
|
+
columns.append(col)
|
|
47
|
+
value = dataset.X[col].isna().sum() if self.null_count else dataset.X[col].count()
|
|
48
|
+
data.append(value)
|
|
49
|
+
df = pd.concat([df, pd.DataFrame([data], index=[str(self)], columns=columns)])
|
|
50
|
+
return df
|
|
51
|
+
|
|
52
|
+
class_labels = list(pd.unique(dataset.y))
|
|
53
|
+
for cnt in CountStatistic.count_to_compute:
|
|
54
|
+
self.null_count = cnt
|
|
55
|
+
data = []
|
|
56
|
+
columns = []
|
|
57
|
+
for col in dataset.X.columns:
|
|
58
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
59
|
+
for label in ['all'] + class_labels:
|
|
60
|
+
columns.append(f"{col}_{label}")
|
|
61
|
+
if label == 'all':
|
|
62
|
+
value = dataset.X[col].isna().sum() if self.null_count else dataset.X[col].count()
|
|
63
|
+
else:
|
|
64
|
+
values = dataset.X.loc[dataset.y == label][col]
|
|
65
|
+
value = values.isna().sum() if self.null_count else values.count()
|
|
66
|
+
data.append(value)
|
|
67
|
+
df = pd.concat([df, pd.DataFrame([data], index=[str(self)], columns=columns)])
|
|
68
|
+
return df
|
|
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,74 @@
|
|
|
1
|
+
"""[STATISTIC] Data Type Summary."""
|
|
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 DataTypeSummaryStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Data Type Summary."""
|
|
14
|
+
|
|
15
|
+
name: str = "Data Type Summary"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Data type summary counts column types and ratios.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Data type summary reports the number of numeric, categorical, text, and date
|
|
21
|
+
columns, along with ratios relative to the total number of columns.
|
|
22
|
+
""")
|
|
23
|
+
refs: list[dict] = []
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return 'data_type_summary'
|
|
27
|
+
|
|
28
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
29
|
+
"""Compute data type counts and ratios for the dataset."""
|
|
30
|
+
if dataset.type_of_target == 'survival':
|
|
31
|
+
return pd.DataFrame()
|
|
32
|
+
|
|
33
|
+
total_columns = len(dataset.X.columns)
|
|
34
|
+
n_numeric = len(dataset.get_columns_names_by_type(DataType.NUMERIC))
|
|
35
|
+
n_categorical = len(dataset.get_columns_names_by_type(DataType.CATEGORICAL))
|
|
36
|
+
n_text = len(dataset.get_columns_names_by_type([DataType.TEXT, DataType.SHORT_TEXT]))
|
|
37
|
+
n_date = len(dataset.get_columns_names_by_type(DataType.DATE))
|
|
38
|
+
|
|
39
|
+
if total_columns:
|
|
40
|
+
ratio_numeric = n_numeric / total_columns
|
|
41
|
+
ratio_categorical = n_categorical / total_columns
|
|
42
|
+
ratio_text = n_text / total_columns
|
|
43
|
+
ratio_date = n_date / total_columns
|
|
44
|
+
else:
|
|
45
|
+
ratio_numeric = 0.0
|
|
46
|
+
ratio_categorical = 0.0
|
|
47
|
+
ratio_text = 0.0
|
|
48
|
+
ratio_date = 0.0
|
|
49
|
+
|
|
50
|
+
data = [[
|
|
51
|
+
n_numeric,
|
|
52
|
+
n_categorical,
|
|
53
|
+
n_text,
|
|
54
|
+
n_date,
|
|
55
|
+
ratio_numeric,
|
|
56
|
+
ratio_categorical,
|
|
57
|
+
ratio_text,
|
|
58
|
+
ratio_date,
|
|
59
|
+
]]
|
|
60
|
+
columns = [
|
|
61
|
+
'n_numeric',
|
|
62
|
+
'n_categorical',
|
|
63
|
+
'n_text',
|
|
64
|
+
'n_date',
|
|
65
|
+
'ratio_numeric',
|
|
66
|
+
'ratio_categorical',
|
|
67
|
+
'ratio_text',
|
|
68
|
+
'ratio_date',
|
|
69
|
+
]
|
|
70
|
+
return pd.DataFrame(data, index=[str(self)], columns=columns)
|
|
71
|
+
|
|
72
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
73
|
+
"""Does this statistic apply to the dataset?"""
|
|
74
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""[STATISTIC] Duplicate Rows."""
|
|
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 DuplicateRowStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Duplicate Rows."""
|
|
14
|
+
|
|
15
|
+
name: str = "Duplicate Rows"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Duplicate rows reports the number and ratio of duplicated rows.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Duplicate rows counts duplicated rows based on usable columns and
|
|
21
|
+
provides the ratio relative to the total number of rows.
|
|
22
|
+
""")
|
|
23
|
+
refs: list[dict] = []
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return 'duplicate_rows'
|
|
27
|
+
|
|
28
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
29
|
+
"""Compute number and ratio of duplicated rows."""
|
|
30
|
+
if dataset.type_of_target == 'survival':
|
|
31
|
+
return pd.DataFrame()
|
|
32
|
+
|
|
33
|
+
columns = [
|
|
34
|
+
col for col in dataset.X.columns
|
|
35
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT)
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
n_rows = int(dataset.X.shape[0])
|
|
39
|
+
if columns:
|
|
40
|
+
data_frame = dataset.X[columns]
|
|
41
|
+
n_duplicate = int(data_frame.duplicated().sum())
|
|
42
|
+
else:
|
|
43
|
+
n_duplicate = 0
|
|
44
|
+
|
|
45
|
+
ratio = n_duplicate / n_rows if n_rows else 0.0
|
|
46
|
+
|
|
47
|
+
data = [[n_duplicate, ratio]]
|
|
48
|
+
return pd.DataFrame(
|
|
49
|
+
data,
|
|
50
|
+
index=[str(self)],
|
|
51
|
+
columns=["n_duplicate_rows", "ratio_duplicate_rows"],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
55
|
+
"""Does this statistic apply to the dataset?"""
|
|
56
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""[STATISTIC] Effect size (Cohen d)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import numpy as np
|
|
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 EffectSizeStatistic(Statistic):
|
|
14
|
+
"""[STATISTIC] Effect size (Cohen d)."""
|
|
15
|
+
|
|
16
|
+
name: str = "Effect Size"
|
|
17
|
+
_description: str = textwrap.dedent("""\
|
|
18
|
+
Effect size measures standardized differences (Cohen d) between classes.
|
|
19
|
+
""")
|
|
20
|
+
_description_long: str = textwrap.dedent("""\
|
|
21
|
+
Effect size computes Cohen's d for each numerical column per class
|
|
22
|
+
compared against the rest of the data. The `_all` suffix reports
|
|
23
|
+
the mean absolute effect size across classes for each feature.
|
|
24
|
+
""")
|
|
25
|
+
refs: list[dict] = []
|
|
26
|
+
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
return 'effect_size'
|
|
29
|
+
|
|
30
|
+
def _safe_variance(self, n: float, total: float, total_sq: float) -> float | None:
|
|
31
|
+
if n is None or n <= 1:
|
|
32
|
+
return None
|
|
33
|
+
denom = n - 1.0
|
|
34
|
+
var = (total_sq - (total * total) / n) / denom
|
|
35
|
+
if pd.isna(var) or var < 0:
|
|
36
|
+
return None
|
|
37
|
+
return float(var)
|
|
38
|
+
|
|
39
|
+
def _cohen_d(self, mean_a: float, mean_b: float, var_a: float | None,
|
|
40
|
+
var_b: float | None, n_a: float, n_b: float) -> float | None:
|
|
41
|
+
if var_a is None or var_b is None:
|
|
42
|
+
return None
|
|
43
|
+
if n_a <= 1 or n_b <= 1:
|
|
44
|
+
return None
|
|
45
|
+
pooled_denom = (n_a + n_b - 2.0)
|
|
46
|
+
if pooled_denom <= 0:
|
|
47
|
+
return None
|
|
48
|
+
pooled_var = ((n_a - 1.0) * var_a + (n_b - 1.0) * var_b) / pooled_denom
|
|
49
|
+
if pooled_var <= 0 or pd.isna(pooled_var):
|
|
50
|
+
return None
|
|
51
|
+
pooled_std = np.sqrt(pooled_var)
|
|
52
|
+
if pooled_std == 0 or pd.isna(pooled_std):
|
|
53
|
+
return None
|
|
54
|
+
return (mean_a - mean_b) / pooled_std
|
|
55
|
+
|
|
56
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
57
|
+
"""Compute Cohen's d for numerical columns."""
|
|
58
|
+
if dataset.type_of_target in ['survival', 'continuous']:
|
|
59
|
+
return pd.DataFrame()
|
|
60
|
+
|
|
61
|
+
numeric_columns = dataset.get_columns_names_by_type(DataType.NUMERIC)
|
|
62
|
+
if not numeric_columns:
|
|
63
|
+
return pd.DataFrame()
|
|
64
|
+
|
|
65
|
+
class_labels = list(pd.unique(dataset.y))
|
|
66
|
+
|
|
67
|
+
values = dataset.X[numeric_columns]
|
|
68
|
+
total_n = values.count()
|
|
69
|
+
total_sum = values.sum()
|
|
70
|
+
total_sumsq = (values ** 2).sum()
|
|
71
|
+
|
|
72
|
+
grouped = values.groupby(dataset.y)
|
|
73
|
+
class_n = grouped.count()
|
|
74
|
+
class_sum = grouped.sum()
|
|
75
|
+
class_sumsq = (values ** 2).groupby(dataset.y).sum()
|
|
76
|
+
|
|
77
|
+
data = []
|
|
78
|
+
columns = []
|
|
79
|
+
|
|
80
|
+
for col in numeric_columns:
|
|
81
|
+
col_total_n = float(total_n.get(col, 0))
|
|
82
|
+
col_total_sum = float(total_sum.get(col, 0))
|
|
83
|
+
col_total_sumsq = float(total_sumsq.get(col, 0))
|
|
84
|
+
abs_effects = []
|
|
85
|
+
|
|
86
|
+
for label in ['all'] + class_labels:
|
|
87
|
+
columns.append(f"{col}_{label}")
|
|
88
|
+
if label == 'all':
|
|
89
|
+
data.append(None)
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
n_a = float(class_n.at[label, col]) if label in class_n.index else 0.0
|
|
93
|
+
if n_a <= 0:
|
|
94
|
+
data.append(None)
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
sum_a = float(class_sum.at[label, col]) if label in class_sum.index else 0.0
|
|
98
|
+
sumsq_a = float(class_sumsq.at[label, col]) if label in class_sumsq.index else 0.0
|
|
99
|
+
|
|
100
|
+
n_b = col_total_n - n_a
|
|
101
|
+
if n_b <= 0:
|
|
102
|
+
data.append(None)
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
sum_b = col_total_sum - sum_a
|
|
106
|
+
sumsq_b = col_total_sumsq - sumsq_a
|
|
107
|
+
|
|
108
|
+
mean_a = sum_a / n_a if n_a > 0 else None
|
|
109
|
+
mean_b = sum_b / n_b if n_b > 0 else None
|
|
110
|
+
if mean_a is None or mean_b is None or pd.isna(mean_a) or pd.isna(mean_b):
|
|
111
|
+
data.append(None)
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
var_a = self._safe_variance(n_a, sum_a, sumsq_a)
|
|
115
|
+
var_b = self._safe_variance(n_b, sum_b, sumsq_b)
|
|
116
|
+
d_value = self._cohen_d(mean_a, mean_b, var_a, var_b, n_a, n_b)
|
|
117
|
+
data.append(d_value)
|
|
118
|
+
if d_value is not None and not pd.isna(d_value):
|
|
119
|
+
abs_effects.append(abs(d_value))
|
|
120
|
+
|
|
121
|
+
if abs_effects:
|
|
122
|
+
data_index = columns.index(f"{col}_all")
|
|
123
|
+
data[data_index] = float(np.mean(abs_effects))
|
|
124
|
+
|
|
125
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
126
|
+
|
|
127
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
128
|
+
"""Does this statistic apply to the dataset?"""
|
|
129
|
+
return dataset.type_of_target not in ['survival', 'continuous']
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""[STATISTIC] Entropy."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import numpy as np
|
|
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 EntropyStatistic(Statistic):
|
|
14
|
+
"""[STATISTIC] Entropy."""
|
|
15
|
+
|
|
16
|
+
name: str = "Entropy"
|
|
17
|
+
_description: str = textwrap.dedent("""\
|
|
18
|
+
Entropy measures the distribution uncertainty for categorical columns.
|
|
19
|
+
""")
|
|
20
|
+
_description_long: str = textwrap.dedent("""\
|
|
21
|
+
Entropy measures the distribution uncertainty for categorical columns,
|
|
22
|
+
optionally per class for classification.
|
|
23
|
+
""")
|
|
24
|
+
refs: list[dict] = []
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
return 'entropy'
|
|
28
|
+
|
|
29
|
+
def _entropy(self, series: pd.Series) -> float:
|
|
30
|
+
values = series.dropna()
|
|
31
|
+
if values.empty:
|
|
32
|
+
return 0.0
|
|
33
|
+
counts = values.value_counts()
|
|
34
|
+
total = counts.sum()
|
|
35
|
+
if total == 0:
|
|
36
|
+
return 0.0
|
|
37
|
+
probs = counts / total
|
|
38
|
+
return float(-(probs * np.log2(probs)).sum())
|
|
39
|
+
|
|
40
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
41
|
+
"""Compute entropy for categorical columns."""
|
|
42
|
+
if dataset.type_of_target == 'survival':
|
|
43
|
+
return pd.DataFrame()
|
|
44
|
+
|
|
45
|
+
data = []
|
|
46
|
+
columns = []
|
|
47
|
+
|
|
48
|
+
if dataset.type_of_target == 'continuous':
|
|
49
|
+
for col in dataset.X.columns:
|
|
50
|
+
if dataset.columns_types[col][1] == DataType.CATEGORICAL:
|
|
51
|
+
columns.append(col)
|
|
52
|
+
data.append(self._entropy(dataset.X[col]))
|
|
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] == DataType.CATEGORICAL:
|
|
58
|
+
for label in ['all'] + class_labels:
|
|
59
|
+
columns.append(f"{col}_{label}")
|
|
60
|
+
if label == 'all':
|
|
61
|
+
values = dataset.X[col]
|
|
62
|
+
else:
|
|
63
|
+
values = dataset.X.loc[dataset.y == label][col]
|
|
64
|
+
data.append(self._entropy(values))
|
|
65
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
66
|
+
|
|
67
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
68
|
+
"""Does this statistic apply to the dataset?"""
|
|
69
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""[STATISTIC] Event Rate."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import textwrap
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from ..dataset import Dataset
|
|
8
|
+
from ..statistic import Statistic
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EventRateStatistic(Statistic):
|
|
12
|
+
"""[STATISTIC] Event Rate."""
|
|
13
|
+
|
|
14
|
+
name: str = "Event Rate"
|
|
15
|
+
_description: str = textwrap.dedent("""\
|
|
16
|
+
Event rate measures the proportion of events versus censoring in survival data.
|
|
17
|
+
""")
|
|
18
|
+
_description_long: str = textwrap.dedent("""\
|
|
19
|
+
Event rate measures the proportion of events versus censoring in survival targets.
|
|
20
|
+
Rates are reported as fractions in [0, 1].
|
|
21
|
+
""")
|
|
22
|
+
refs: list[dict] = []
|
|
23
|
+
|
|
24
|
+
def __str__(self) -> str:
|
|
25
|
+
return 'event_rate'
|
|
26
|
+
|
|
27
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
28
|
+
"""Compute event and censoring rates for survival targets."""
|
|
29
|
+
if dataset.type_of_target != 'survival':
|
|
30
|
+
return pd.DataFrame()
|
|
31
|
+
|
|
32
|
+
samples = Dataset.normalize_survival_target(dataset.y)
|
|
33
|
+
n_samples = len(samples)
|
|
34
|
+
|
|
35
|
+
if n_samples:
|
|
36
|
+
event_count = int(sum(event for event, _ in samples))
|
|
37
|
+
censor_count = n_samples - event_count
|
|
38
|
+
event_rate = event_count / n_samples
|
|
39
|
+
censor_rate = censor_count / n_samples
|
|
40
|
+
else:
|
|
41
|
+
event_count = 0
|
|
42
|
+
censor_count = 0
|
|
43
|
+
event_rate = 0.0
|
|
44
|
+
censor_rate = 0.0
|
|
45
|
+
|
|
46
|
+
data = [[event_count, censor_count, event_rate, censor_rate]]
|
|
47
|
+
columns = ['event_count', 'censor_count', 'event_rate', 'censor_rate']
|
|
48
|
+
return pd.DataFrame(data, index=[str(self)], columns=columns)
|
|
49
|
+
|
|
50
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
51
|
+
"""Does this statistic apply to the dataset?"""
|
|
52
|
+
return dataset.type_of_target == 'survival'
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""[STATISTIC] Grouped mean."""
|
|
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 GroupedMeanStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Grouped mean."""
|
|
14
|
+
|
|
15
|
+
name: str = "Grouped Mean"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Grouped mean measures per-class mean offsets from the overall mean.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Grouped mean measures numerical column means per class as offsets from the overall mean.
|
|
21
|
+
The overall mean is reported under the `_all` column suffix for each feature.
|
|
22
|
+
""")
|
|
23
|
+
refs: list[dict] = []
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return 'grouped_mean'
|
|
27
|
+
|
|
28
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
29
|
+
"""Compute grouped mean offsets for numerical columns."""
|
|
30
|
+
if dataset.type_of_target in ['survival', 'continuous']:
|
|
31
|
+
return pd.DataFrame()
|
|
32
|
+
|
|
33
|
+
numeric_columns = dataset.get_columns_names_by_type(DataType.NUMERIC)
|
|
34
|
+
if not numeric_columns:
|
|
35
|
+
return pd.DataFrame()
|
|
36
|
+
|
|
37
|
+
overall_means = dataset.X[numeric_columns].mean()
|
|
38
|
+
grouped_means = dataset.X[numeric_columns].groupby(dataset.y).mean()
|
|
39
|
+
class_labels = list(pd.unique(dataset.y))
|
|
40
|
+
|
|
41
|
+
data = []
|
|
42
|
+
columns = []
|
|
43
|
+
for col in numeric_columns:
|
|
44
|
+
overall = overall_means.get(col)
|
|
45
|
+
for label in ['all'] + class_labels:
|
|
46
|
+
columns.append(f"{col}_{label}")
|
|
47
|
+
if label == 'all':
|
|
48
|
+
data.append(overall)
|
|
49
|
+
else:
|
|
50
|
+
class_mean = grouped_means.at[label, col] if label in grouped_means.index else pd.NA
|
|
51
|
+
if pd.isna(class_mean) or pd.isna(overall):
|
|
52
|
+
data.append(None)
|
|
53
|
+
else:
|
|
54
|
+
data.append(class_mean - overall)
|
|
55
|
+
|
|
56
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
57
|
+
|
|
58
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
59
|
+
"""Does this statistic apply to the dataset?"""
|
|
60
|
+
return dataset.type_of_target not in ['survival', 'continuous']
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""[STATISTIC] IQR."""
|
|
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 IQRStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] IQR."""
|
|
14
|
+
|
|
15
|
+
name: str = "IQR"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
IQR measures dispersion as Q3 minus Q1 for numerical columns.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
IQR measures dispersion as Q3 minus Q1 for numerical columns, per class for classification.
|
|
21
|
+
""")
|
|
22
|
+
refs: list[dict] = []
|
|
23
|
+
|
|
24
|
+
def __str__(self) -> str:
|
|
25
|
+
return 'iqr'
|
|
26
|
+
|
|
27
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
28
|
+
"""Compute IQR for each numerical column."""
|
|
29
|
+
if dataset.type_of_target == 'survival':
|
|
30
|
+
return pd.DataFrame()
|
|
31
|
+
|
|
32
|
+
data = []
|
|
33
|
+
columns = []
|
|
34
|
+
|
|
35
|
+
if dataset.type_of_target == 'continuous':
|
|
36
|
+
for col in dataset.X.columns:
|
|
37
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
38
|
+
columns.append(col)
|
|
39
|
+
if dataset.columns_types[col][1] == DataType.NUMERIC:
|
|
40
|
+
q3 = dataset.X[col].quantile(0.75)
|
|
41
|
+
q1 = dataset.X[col].quantile(0.25)
|
|
42
|
+
data.append(q3 - q1)
|
|
43
|
+
else:
|
|
44
|
+
data.append(None)
|
|
45
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
46
|
+
|
|
47
|
+
class_labels = list(pd.unique(dataset.y))
|
|
48
|
+
for col in dataset.X.columns:
|
|
49
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
50
|
+
for label in ['all'] + class_labels:
|
|
51
|
+
columns.append(f"{col}_{label}")
|
|
52
|
+
if dataset.columns_types[col][1] == DataType.NUMERIC:
|
|
53
|
+
if label == 'all':
|
|
54
|
+
series = dataset.X[col]
|
|
55
|
+
else:
|
|
56
|
+
series = dataset.X.loc[dataset.y == label][col]
|
|
57
|
+
q3 = series.quantile(0.75)
|
|
58
|
+
q1 = series.quantile(0.25)
|
|
59
|
+
data.append(q3 - q1)
|
|
60
|
+
else:
|
|
61
|
+
data.append(None)
|
|
62
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
63
|
+
|
|
64
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
65
|
+
"""Does this statistic apply to the dataset?"""
|
|
66
|
+
return dataset.type_of_target != 'survival'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""[STATISTIC] Kurtosis."""
|
|
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 KurtosisStatistic(Statistic):
|
|
13
|
+
"""[STATISTIC] Kurtosis."""
|
|
14
|
+
|
|
15
|
+
name: str = "Kurtosis"
|
|
16
|
+
_description: str = textwrap.dedent("""\
|
|
17
|
+
Kurtosis measures the tailedness of numerical distributions.
|
|
18
|
+
""")
|
|
19
|
+
_description_long: str = textwrap.dedent("""\
|
|
20
|
+
Kurtosis measures the tailedness of numerical distributions, per class for classification.
|
|
21
|
+
""")
|
|
22
|
+
refs: list[dict] = []
|
|
23
|
+
|
|
24
|
+
def __str__(self) -> str:
|
|
25
|
+
return 'kurtosis'
|
|
26
|
+
|
|
27
|
+
def compute(self, dataset: Dataset, **kwargs) -> pd.DataFrame:
|
|
28
|
+
"""Compute kurtosis for numerical columns."""
|
|
29
|
+
if dataset.type_of_target in ['survival', 'continuous']:
|
|
30
|
+
return pd.DataFrame()
|
|
31
|
+
|
|
32
|
+
class_labels = list(pd.unique(dataset.y))
|
|
33
|
+
data = []
|
|
34
|
+
columns = []
|
|
35
|
+
for col in dataset.X.columns:
|
|
36
|
+
if dataset.columns_types[col][1] not in (DataType.TEXT, DataType.SHORT_TEXT):
|
|
37
|
+
for label in ['all'] + class_labels:
|
|
38
|
+
columns.append(f"{col}_{label}")
|
|
39
|
+
if dataset.columns_types[col][1] == DataType.NUMERIC:
|
|
40
|
+
if label == 'all':
|
|
41
|
+
data.append(dataset.X[col].kurtosis())
|
|
42
|
+
else:
|
|
43
|
+
data.append(dataset.X.loc[dataset.y == label][col].kurtosis())
|
|
44
|
+
else:
|
|
45
|
+
data.append(None)
|
|
46
|
+
return pd.DataFrame([data], index=[str(self)], columns=columns)
|
|
47
|
+
|
|
48
|
+
def suitable(self, dataset: Dataset) -> bool: # pylint: disable=unused-argument
|
|
49
|
+
"""Does this statistic apply to the dataset?"""
|
|
50
|
+
return dataset.type_of_target not in ['survival', 'continuous']
|