upgini 1.2.167a4220.dev1__tar.gz → 1.2.167a4220.dev2__tar.gz
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.
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/PKG-INFO +1 -1
- upgini-1.2.167a4220.dev2/src/upgini/__about__.py +1 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/features_enricher.py +96 -48
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/metrics.py +40 -6
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/report/assemble.py +53 -4
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/report/data.py +17 -2
- upgini-1.2.167a4220.dev2/src/upgini/report/html.py +385 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/resource_bundle/strings.properties +2 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/display_utils.py +41 -35
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/track_info.py +4 -0
- upgini-1.2.167a4220.dev1/src/upgini/__about__.py +0 -1
- upgini-1.2.167a4220.dev1/src/upgini/report/html.py +0 -281
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/.gitignore +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/LICENSE +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/README.md +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/pyproject.toml +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/ads.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/ads_management/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/ads_management/ads_manager.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/all_operators.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/binary.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/date.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/feature.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/groupby.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/operand.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/operator.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/base.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/cross.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/delta.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/lag.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/numpy_kernels.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/roll.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/trend.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/timeseries/volatility.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/unary.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/autofe/vector.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/data_source/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/data_source/data_source_publisher.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/dataset.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/errors.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/http.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/mdc/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/mdc/context.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/metadata.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/normalizer/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/normalizer/normalize_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/report/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/resource_bundle/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/resource_bundle/exceptions.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/resource_bundle/strings_widget.properties +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/sampler/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/sampler/base.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/sampler/random_under_sampler.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/sampler/utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/search_task.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/spinner.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/Roboto-Regular.ttf +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/__init__.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/base_search_key_detector.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/blocked_time_series.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/config.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/country_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/cpu_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/custom_loss_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/cv_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/datetime_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/deduplicate_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/email_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/fallback_progress_bar.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/feature_info.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/features_validator.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/format.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/hash_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/ip_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/mstats.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/one_hot_encoder.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/phone_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/postal_code_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/progress_bar.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/psi.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/pyarrow_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/sample_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/search_key_derivations.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/sklearn_ext.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/sort.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/target_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/ts_utils.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/utils/warning_counter.py +0 -0
- {upgini-1.2.167a4220.dev1 → upgini-1.2.167a4220.dev2}/src/upgini/version_validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: upgini
|
|
3
|
-
Version: 1.2.167a4220.
|
|
3
|
+
Version: 1.2.167a4220.dev2
|
|
4
4
|
Summary: Intelligent data search & enrichment for Machine Learning
|
|
5
5
|
Project-URL: Bug Reports, https://github.com/upgini/upgini/issues
|
|
6
6
|
Project-URL: Homepage, https://upgini.com/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.167.a4220.dev2"
|
|
@@ -68,7 +68,13 @@ from upgini.metadata import (
|
|
|
68
68
|
RuntimeParameters,
|
|
69
69
|
SearchKey,
|
|
70
70
|
)
|
|
71
|
-
from upgini.metrics import
|
|
71
|
+
from upgini.metrics import (
|
|
72
|
+
EstimatorWrapper,
|
|
73
|
+
define_scorer,
|
|
74
|
+
format_metrics_for_display,
|
|
75
|
+
put_cv_metric,
|
|
76
|
+
validate_scoring_argument,
|
|
77
|
+
)
|
|
72
78
|
from upgini.normalizer.normalize_utils import Normalizer
|
|
73
79
|
from upgini.resource_bundle import ResourceBundle, bundle, get_custom_bundle
|
|
74
80
|
from upgini.search_task import SearchTask
|
|
@@ -318,6 +324,7 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
318
324
|
self.relevant_data_sources: pd.DataFrame = self.EMPTY_DATA_SOURCES
|
|
319
325
|
self._relevant_data_sources_wo_links: pd.DataFrame = self.EMPTY_DATA_SOURCES
|
|
320
326
|
self.metrics: pd.DataFrame | None = None
|
|
327
|
+
self.metrics_raw: pd.DataFrame | None = None
|
|
321
328
|
self.metrics_metric_name: str | None = None
|
|
322
329
|
self.feature_names_ = []
|
|
323
330
|
self.external_source_feature_names = []
|
|
@@ -1108,8 +1115,8 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1108
1115
|
|
|
1109
1116
|
# 1 If client features are presented - fit and predict with KFold estimator
|
|
1110
1117
|
# on etalon features and calculate baseline metric
|
|
1111
|
-
baseline_metric = None
|
|
1112
1118
|
baseline_estimator = None
|
|
1119
|
+
baseline_cv_result = None
|
|
1113
1120
|
updating_shaps = None
|
|
1114
1121
|
custom_loss_add_params = get_additional_params_custom_loss(
|
|
1115
1122
|
self.loss, model_task_type, logger=self.logger
|
|
@@ -1133,22 +1140,23 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1133
1140
|
baseline_cv_result = baseline_estimator.cross_val_predict(
|
|
1134
1141
|
fitting_X, y_sorted, self.baseline_score_column
|
|
1135
1142
|
)
|
|
1136
|
-
|
|
1137
|
-
if baseline_metric is None:
|
|
1143
|
+
if baseline_cv_result.metric is None:
|
|
1138
1144
|
self.logger.info(
|
|
1139
1145
|
f"Baseline {metric} on train client features is None (maybe all features was removed)"
|
|
1140
1146
|
)
|
|
1141
1147
|
baseline_estimator = None
|
|
1142
1148
|
else:
|
|
1143
|
-
self.logger.info(
|
|
1149
|
+
self.logger.info(
|
|
1150
|
+
f"Baseline {metric} on train client features: {baseline_cv_result.get_display_metric()}"
|
|
1151
|
+
)
|
|
1144
1152
|
updating_shaps = baseline_cv_result.shap_values
|
|
1145
1153
|
|
|
1146
1154
|
# 2 Fit and predict with KFold estimator on enriched tds
|
|
1147
1155
|
# and calculate final metric (and uplift)
|
|
1148
|
-
enriched_metric = None
|
|
1149
1156
|
uplift = None
|
|
1150
1157
|
uplift_perc = None
|
|
1151
1158
|
enriched_estimator = None
|
|
1159
|
+
enriched_cv_result = None
|
|
1152
1160
|
ensemble_score_column = self._get_ensemble_score_column(fitting_X, fitting_enriched_X)
|
|
1153
1161
|
|
|
1154
1162
|
def evaluate_enriched(score_column, description: str):
|
|
@@ -1167,17 +1175,16 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1167
1175
|
cv_result = wrapper.cross_val_predict(
|
|
1168
1176
|
fitting_enriched_X, enriched_y_sorted, score_column
|
|
1169
1177
|
)
|
|
1170
|
-
|
|
1171
|
-
if display_metric is None:
|
|
1178
|
+
if cv_result.metric is None:
|
|
1172
1179
|
self.logger.warning(f"Enriched {metric} {description} is None")
|
|
1173
|
-
return None,
|
|
1174
|
-
self.logger.info(f"Enriched {metric} {description}: {
|
|
1180
|
+
return None, cv_result, None, None
|
|
1181
|
+
self.logger.info(f"Enriched {metric} {description}: {cv_result.get_display_metric()}")
|
|
1175
1182
|
sample_uplift = None
|
|
1176
1183
|
sample_uplift_perc = None
|
|
1177
|
-
if
|
|
1184
|
+
if baseline_cv_result is not None and baseline_cv_result.metric is not None:
|
|
1178
1185
|
sample_uplift = (cv_result.metric - baseline_cv_result.metric) * multiplier
|
|
1179
1186
|
sample_uplift_perc = sample_uplift / abs(baseline_cv_result.metric) * 100
|
|
1180
|
-
return wrapper,
|
|
1187
|
+
return wrapper, cv_result, sample_uplift, sample_uplift_perc
|
|
1181
1188
|
|
|
1182
1189
|
if ensemble_score_column is not None:
|
|
1183
1190
|
self.logger.info(
|
|
@@ -1186,14 +1193,14 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1186
1193
|
)
|
|
1187
1194
|
(
|
|
1188
1195
|
enriched_estimator,
|
|
1189
|
-
|
|
1190
|
-
updating_shaps,
|
|
1196
|
+
enriched_cv_result,
|
|
1191
1197
|
uplift,
|
|
1192
1198
|
uplift_perc,
|
|
1193
1199
|
) = evaluate_enriched(
|
|
1194
1200
|
ensemble_score_column,
|
|
1195
1201
|
f"on ensemble score column {ensemble_score_column}",
|
|
1196
1202
|
)
|
|
1203
|
+
updating_shaps = enriched_cv_result.shap_values
|
|
1197
1204
|
elif set(fitting_X.columns) != set(fitting_enriched_X.columns):
|
|
1198
1205
|
self.logger.info(
|
|
1199
1206
|
f"Calculate enriched {metric} on train combined "
|
|
@@ -1201,11 +1208,11 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1201
1208
|
)
|
|
1202
1209
|
(
|
|
1203
1210
|
enriched_estimator,
|
|
1204
|
-
|
|
1205
|
-
updating_shaps,
|
|
1211
|
+
enriched_cv_result,
|
|
1206
1212
|
uplift,
|
|
1207
1213
|
uplift_perc,
|
|
1208
1214
|
) = evaluate_enriched(None, "on train combined features")
|
|
1215
|
+
updating_shaps = enriched_cv_result.shap_values
|
|
1209
1216
|
|
|
1210
1217
|
train_metrics = {
|
|
1211
1218
|
self.bundle.get("quality_metrics_segment_header"): self.bundle.get(
|
|
@@ -1223,14 +1230,22 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1223
1230
|
np.mean(y_sorted),
|
|
1224
1231
|
4,
|
|
1225
1232
|
)
|
|
1226
|
-
|
|
1227
|
-
train_metrics
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1233
|
+
put_cv_metric(
|
|
1234
|
+
train_metrics,
|
|
1235
|
+
"quality_metrics_baseline_header",
|
|
1236
|
+
"quality_metrics_baseline_std_header",
|
|
1237
|
+
baseline_cv_result,
|
|
1238
|
+
metric,
|
|
1239
|
+
self.bundle,
|
|
1240
|
+
)
|
|
1241
|
+
put_cv_metric(
|
|
1242
|
+
train_metrics,
|
|
1243
|
+
"quality_metrics_enriched_header",
|
|
1244
|
+
"quality_metrics_enriched_std_header",
|
|
1245
|
+
enriched_cv_result,
|
|
1246
|
+
metric,
|
|
1247
|
+
self.bundle,
|
|
1248
|
+
)
|
|
1234
1249
|
if uplift is not None:
|
|
1235
1250
|
train_metrics[self.bundle.get("quality_metrics_uplift_header")] = round(uplift, 3)
|
|
1236
1251
|
train_metrics[self.bundle.get("quality_metrics_uplift_perc_header")] = (
|
|
@@ -1262,12 +1277,13 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1262
1277
|
etalon_eval_results = baseline_estimator.calculate_metric(
|
|
1263
1278
|
eval_X_sorted, eval_y_sorted, self.baseline_score_column
|
|
1264
1279
|
)
|
|
1265
|
-
etalon_eval_metric = etalon_eval_results.get_display_metric()
|
|
1266
1280
|
self.logger.info(
|
|
1267
|
-
|
|
1281
|
+
"Baseline {} on eval set {} client features: {}".format(
|
|
1282
|
+
metric, idx + 1, etalon_eval_results.get_display_metric()
|
|
1283
|
+
)
|
|
1268
1284
|
)
|
|
1269
1285
|
else:
|
|
1270
|
-
|
|
1286
|
+
etalon_eval_results = None
|
|
1271
1287
|
|
|
1272
1288
|
if enriched_estimator is not None:
|
|
1273
1289
|
self.logger.info(
|
|
@@ -1277,14 +1293,20 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1277
1293
|
enriched_eval_results = enriched_estimator.calculate_metric(
|
|
1278
1294
|
enriched_eval_X_sorted, enriched_eval_y_sorted, ensemble_score_column
|
|
1279
1295
|
)
|
|
1280
|
-
enriched_eval_metric = enriched_eval_results.get_display_metric()
|
|
1281
1296
|
self.logger.info(
|
|
1282
|
-
|
|
1297
|
+
"Enriched {} on eval set {} combined features: {}".format(
|
|
1298
|
+
metric, idx + 1, enriched_eval_results.get_display_metric()
|
|
1299
|
+
)
|
|
1283
1300
|
)
|
|
1284
1301
|
else:
|
|
1285
|
-
|
|
1302
|
+
enriched_eval_results = None
|
|
1286
1303
|
|
|
1287
|
-
if
|
|
1304
|
+
if (
|
|
1305
|
+
etalon_eval_results is not None
|
|
1306
|
+
and etalon_eval_results.metric is not None
|
|
1307
|
+
and enriched_eval_results is not None
|
|
1308
|
+
and enriched_eval_results.metric is not None
|
|
1309
|
+
):
|
|
1288
1310
|
eval_uplift = (enriched_eval_results.metric - etalon_eval_results.metric) * multiplier
|
|
1289
1311
|
eval_uplift_perc = eval_uplift / abs(etalon_eval_results.metric) * 100
|
|
1290
1312
|
else:
|
|
@@ -1308,14 +1330,22 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1308
1330
|
np.mean(eval_y_sorted),
|
|
1309
1331
|
4,
|
|
1310
1332
|
)
|
|
1311
|
-
|
|
1312
|
-
eval_metrics
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1333
|
+
put_cv_metric(
|
|
1334
|
+
eval_metrics,
|
|
1335
|
+
"quality_metrics_baseline_header",
|
|
1336
|
+
"quality_metrics_baseline_std_header",
|
|
1337
|
+
etalon_eval_results,
|
|
1338
|
+
metric,
|
|
1339
|
+
self.bundle,
|
|
1340
|
+
)
|
|
1341
|
+
put_cv_metric(
|
|
1342
|
+
eval_metrics,
|
|
1343
|
+
"quality_metrics_enriched_header",
|
|
1344
|
+
"quality_metrics_enriched_std_header",
|
|
1345
|
+
enriched_eval_results,
|
|
1346
|
+
metric,
|
|
1347
|
+
self.bundle,
|
|
1348
|
+
)
|
|
1319
1349
|
if eval_uplift is not None:
|
|
1320
1350
|
eval_metrics[self.bundle.get("quality_metrics_uplift_header")] = round(eval_uplift, 3)
|
|
1321
1351
|
eval_metrics[self.bundle.get("quality_metrics_uplift_perc_header")] = (
|
|
@@ -1352,8 +1382,9 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
1352
1382
|
elif uplift_col in metrics_df.columns and (metrics_df[uplift_col] < 0).any():
|
|
1353
1383
|
self.logger.warning("Uplift is negative")
|
|
1354
1384
|
|
|
1385
|
+
self.metrics_raw = metrics_df
|
|
1355
1386
|
self.metrics_metric_name = metric
|
|
1356
|
-
return metrics_df
|
|
1387
|
+
return format_metrics_for_display(metrics_df, metric, self.bundle)
|
|
1357
1388
|
except Exception as e:
|
|
1358
1389
|
error_message = "Failed to calculate metrics" + (
|
|
1359
1390
|
" with validation error" if isinstance(e, ValidationError) else ""
|
|
@@ -2299,8 +2330,10 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2299
2330
|
search_duration_seconds=self.search_duration_seconds,
|
|
2300
2331
|
reference_rows=self._reference_rows(),
|
|
2301
2332
|
samples=self._report_sample_names(),
|
|
2302
|
-
metrics_df=self.metrics,
|
|
2333
|
+
metrics_df=self.metrics_raw if self.metrics_raw is not None else self.metrics,
|
|
2303
2334
|
metric_name=self.metrics_metric_name,
|
|
2335
|
+
model_features=self._ensemble_model_feature_count(),
|
|
2336
|
+
is_binary=self.model_task_type == ModelTaskType.BINARY,
|
|
2304
2337
|
bundle=self.bundle,
|
|
2305
2338
|
)
|
|
2306
2339
|
|
|
@@ -2325,15 +2358,20 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2325
2358
|
return False
|
|
2326
2359
|
return bool(aliases & self._column_name_aliases(list(ensemble_names), renaming))
|
|
2327
2360
|
|
|
2328
|
-
def
|
|
2361
|
+
def _ensemble_generated_metadata(self):
|
|
2329
2362
|
autofe_meta = self._search_task.get_autofe_metadata() if self._search_task else None
|
|
2330
2363
|
if not autofe_meta:
|
|
2331
|
-
return
|
|
2332
|
-
|
|
2364
|
+
return []
|
|
2365
|
+
matched = []
|
|
2333
2366
|
for meta in autofe_meta:
|
|
2334
2367
|
op = EnsembleModel.from_formula(meta.formula.split("(")[0])
|
|
2335
|
-
if op is None:
|
|
2336
|
-
|
|
2368
|
+
if op is not None:
|
|
2369
|
+
matched.append((meta, op))
|
|
2370
|
+
return matched
|
|
2371
|
+
|
|
2372
|
+
def _ensemble_feature_names_from_metadata(self) -> set[str]:
|
|
2373
|
+
names: set[str] = set()
|
|
2374
|
+
for meta, op in self._ensemble_generated_metadata():
|
|
2337
2375
|
feature = (
|
|
2338
2376
|
Feature(op, [])
|
|
2339
2377
|
.set_display_index(meta.display_index)
|
|
@@ -2345,6 +2383,14 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2345
2383
|
names.add(meta.alias)
|
|
2346
2384
|
return names
|
|
2347
2385
|
|
|
2386
|
+
def _ensemble_model_feature_count(self) -> int | None:
|
|
2387
|
+
names = {
|
|
2388
|
+
column.original_name
|
|
2389
|
+
for meta, _ in self._ensemble_generated_metadata()
|
|
2390
|
+
for column in meta.base_columns
|
|
2391
|
+
}
|
|
2392
|
+
return len(names) or None
|
|
2393
|
+
|
|
2348
2394
|
def _psi_exceeds_threshold(self, psi: float | None, threshold: float) -> bool:
|
|
2349
2395
|
return psi is not None and not pd.isna(psi) and psi > threshold
|
|
2350
2396
|
|
|
@@ -4219,7 +4265,9 @@ if response.status_code == 200:
|
|
|
4219
4265
|
self._fit_match_search_keys = None
|
|
4220
4266
|
self.__cached_sampled_datasets = dict()
|
|
4221
4267
|
self.metrics = None
|
|
4268
|
+
self.metrics_raw = None
|
|
4222
4269
|
self.metrics_metric_name = None
|
|
4270
|
+
self.report_file_path = None
|
|
4223
4271
|
self.fit_columns_renaming = None
|
|
4224
4272
|
self.fit_dropped_features = set()
|
|
4225
4273
|
self.fit_generated_features = []
|
|
@@ -6037,8 +6085,8 @@ if response.status_code == 200:
|
|
|
6037
6085
|
|
|
6038
6086
|
def __show_report_button(self, display_id: str | None = None, display_handle=None):
|
|
6039
6087
|
try:
|
|
6040
|
-
report_path = self._write_score_report()
|
|
6041
|
-
if report_path
|
|
6088
|
+
report_path = self._write_score_report() or self.report_file_path
|
|
6089
|
+
if report_path:
|
|
6042
6090
|
if not ipython_available():
|
|
6043
6091
|
print(f"Full report saved to {report_path}")
|
|
6044
6092
|
return
|
|
@@ -253,12 +253,46 @@ class _CrossValResults:
|
|
|
253
253
|
shap_values: Optional[Dict[str, float]]
|
|
254
254
|
|
|
255
255
|
def get_display_metric(self) -> Optional[str]:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
256
|
+
return format_display_metric(self.metric, self.metric_std)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def format_display_metric(metric: Optional[float], metric_std: Optional[float] = None) -> Optional[str]:
|
|
260
|
+
if metric is None or pd.isna(metric):
|
|
261
|
+
return None
|
|
262
|
+
if metric_std is None or pd.isna(metric_std):
|
|
263
|
+
return f"{float(metric):.3f}"
|
|
264
|
+
return f"{float(metric):.3f} ± {float(metric_std):.3f}"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def format_metrics_for_display(metrics_df: pd.DataFrame, metric_name: str, bundle) -> pd.DataFrame:
|
|
268
|
+
display = metrics_df.copy()
|
|
269
|
+
drop: list[str] = []
|
|
270
|
+
for value_key, std_key in (
|
|
271
|
+
("quality_metrics_baseline_header", "quality_metrics_baseline_std_header"),
|
|
272
|
+
("quality_metrics_enriched_header", "quality_metrics_enriched_std_header"),
|
|
273
|
+
):
|
|
274
|
+
col = bundle.get(value_key).format(metric_name)
|
|
275
|
+
std_col = bundle.get(std_key).format(metric_name)
|
|
276
|
+
if col in display.columns:
|
|
277
|
+
stds = display[std_col] if std_col in display.columns else [None] * len(display)
|
|
278
|
+
display[col] = [format_display_metric(value, std) for value, std in zip(display[col], stds)]
|
|
279
|
+
if std_col in display.columns:
|
|
280
|
+
drop.append(std_col)
|
|
281
|
+
return display.drop(columns=drop, errors="ignore")
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def put_cv_metric(
|
|
285
|
+
row: dict,
|
|
286
|
+
value_key: str,
|
|
287
|
+
std_key: str,
|
|
288
|
+
result: Optional[_CrossValResults],
|
|
289
|
+
metric_name: str,
|
|
290
|
+
bundle,
|
|
291
|
+
) -> None:
|
|
292
|
+
if result is None or result.metric is None:
|
|
293
|
+
return
|
|
294
|
+
row[bundle.get(value_key).format(metric_name)] = result.metric
|
|
295
|
+
row[bundle.get(std_key).format(metric_name)] = result.metric_std
|
|
262
296
|
|
|
263
297
|
|
|
264
298
|
def is_numeric_object(x: pd.Series) -> bool:
|
|
@@ -6,7 +6,7 @@ from typing import Optional
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
8
8
|
from upgini.__about__ import __version__
|
|
9
|
-
from upgini.report.data import QualitySample, ReportData, ReportMetadata
|
|
9
|
+
from upgini.report.data import QualitySample, ReportData, ReportMetadata, SampleStats, SearchResultsSummary
|
|
10
10
|
from upgini.resource_bundle import ResourceBundle
|
|
11
11
|
|
|
12
12
|
|
|
@@ -34,9 +34,13 @@ def assemble_report_data(
|
|
|
34
34
|
samples: Optional[list[str]] = None,
|
|
35
35
|
metrics_df: Optional[pd.DataFrame] = None,
|
|
36
36
|
metric_name: Optional[str] = None,
|
|
37
|
+
model_features: Optional[int] = None,
|
|
38
|
+
is_binary: bool = False,
|
|
39
|
+
logo_url: Optional[str] = None,
|
|
37
40
|
bundle: ResourceBundle,
|
|
38
41
|
) -> ReportData:
|
|
39
42
|
generated_at = generated_at or datetime.now(timezone.utc)
|
|
43
|
+
sample_names = list(samples or [])
|
|
40
44
|
return ReportData(
|
|
41
45
|
metadata=ReportMetadata(
|
|
42
46
|
search_id=search_id,
|
|
@@ -45,9 +49,13 @@ def assemble_report_data(
|
|
|
45
49
|
search_keys=list(search_keys or []),
|
|
46
50
|
search_duration=format_search_duration(search_duration_seconds),
|
|
47
51
|
reference_rows=reference_rows,
|
|
48
|
-
samples=
|
|
52
|
+
samples=sample_names,
|
|
53
|
+
logo_url=logo_url or None,
|
|
49
54
|
),
|
|
50
55
|
quality_by_sample=_quality_samples(metrics_df, metric_name, bundle),
|
|
56
|
+
summary=SearchResultsSummary(model_features=model_features),
|
|
57
|
+
sample_stats=_sample_stats(sample_names, metrics_df, bundle),
|
|
58
|
+
is_binary=is_binary,
|
|
51
59
|
)
|
|
52
60
|
|
|
53
61
|
|
|
@@ -61,6 +69,8 @@ def _quality_samples(
|
|
|
61
69
|
uplift_pct_col = bundle.get("quality_metrics_uplift_perc_header")
|
|
62
70
|
baseline_col = bundle.get("quality_metrics_baseline_header").format(metric_name) if metric_name else None
|
|
63
71
|
enriched_col = bundle.get("quality_metrics_enriched_header").format(metric_name) if metric_name else None
|
|
72
|
+
baseline_std_col = bundle.get("quality_metrics_baseline_std_header").format(metric_name) if metric_name else None
|
|
73
|
+
enriched_std_col = bundle.get("quality_metrics_enriched_std_header").format(metric_name) if metric_name else None
|
|
64
74
|
|
|
65
75
|
samples: list[QualitySample] = []
|
|
66
76
|
for _, row in metrics_df.iterrows():
|
|
@@ -68,8 +78,9 @@ def _quality_samples(
|
|
|
68
78
|
QualitySample(
|
|
69
79
|
evaluation_scope=(_as_str(row[segment_col]) or "") if segment_col in metrics_df.columns else "",
|
|
70
80
|
metric=metric_name or "",
|
|
71
|
-
baseline=
|
|
72
|
-
enriched=
|
|
81
|
+
baseline=_as_float(row[baseline_col]) if baseline_col in metrics_df.columns else None,
|
|
82
|
+
enriched=_as_float(row[enriched_col]) if enriched_col in metrics_df.columns else None,
|
|
83
|
+
std=_metric_std(row, metrics_df.columns, enriched_std_col, baseline_std_col),
|
|
73
84
|
uplift=_as_float(row[uplift_col]) if uplift_col in metrics_df.columns else None,
|
|
74
85
|
relative_uplift=_as_str(row[uplift_pct_col]) if uplift_pct_col in metrics_df.columns else None,
|
|
75
86
|
)
|
|
@@ -77,9 +88,47 @@ def _quality_samples(
|
|
|
77
88
|
return samples
|
|
78
89
|
|
|
79
90
|
|
|
91
|
+
def _sample_stats(
|
|
92
|
+
sample_names: list[str], metrics_df: Optional[pd.DataFrame], bundle: ResourceBundle
|
|
93
|
+
) -> list[SampleStats]:
|
|
94
|
+
segment_col = bundle.get("quality_metrics_segment_header")
|
|
95
|
+
rows_col = bundle.get("quality_metrics_rows_header")
|
|
96
|
+
mean_col = bundle.get("quality_metrics_mean_target_header")
|
|
97
|
+
by_name: dict[str, SampleStats] = {}
|
|
98
|
+
if metrics_df is not None and not metrics_df.empty and segment_col in metrics_df.columns:
|
|
99
|
+
for _, row in metrics_df.iterrows():
|
|
100
|
+
name = _as_str(row[segment_col])
|
|
101
|
+
if not name:
|
|
102
|
+
continue
|
|
103
|
+
by_name[name] = SampleStats(
|
|
104
|
+
sample=name,
|
|
105
|
+
rows=_as_int(row[rows_col]) if rows_col in metrics_df.columns else None,
|
|
106
|
+
mean_target=_as_float(row[mean_col]) if mean_col in metrics_df.columns else None,
|
|
107
|
+
)
|
|
108
|
+
names = sample_names or list(by_name)
|
|
109
|
+
stats = [by_name.get(name) or SampleStats(sample=name) for name in names]
|
|
110
|
+
seen = set(names)
|
|
111
|
+
stats.extend(sample for name, sample in by_name.items() if name not in seen)
|
|
112
|
+
return stats
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _metric_std(row, columns, enriched_std_col: Optional[str], baseline_std_col: Optional[str]) -> Optional[float]:
|
|
116
|
+
if enriched_std_col in columns:
|
|
117
|
+
std = _as_float(row[enriched_std_col])
|
|
118
|
+
if std is not None:
|
|
119
|
+
return std
|
|
120
|
+
if baseline_std_col in columns:
|
|
121
|
+
return _as_float(row[baseline_std_col])
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
|
|
80
125
|
def _as_str(value) -> Optional[str]:
|
|
81
126
|
return None if pd.isna(value) else str(value)
|
|
82
127
|
|
|
83
128
|
|
|
84
129
|
def _as_float(value) -> Optional[float]:
|
|
85
130
|
return None if pd.isna(value) else float(value)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _as_int(value) -> Optional[int]:
|
|
134
|
+
return None if pd.isna(value) else int(value)
|
|
@@ -13,18 +13,31 @@ class ReportMetadata:
|
|
|
13
13
|
search_duration: Optional[str] = None
|
|
14
14
|
reference_rows: Optional[int] = None
|
|
15
15
|
samples: list[str] = field(default_factory=list)
|
|
16
|
+
logo_url: Optional[str] = None
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
@dataclass
|
|
19
20
|
class QualitySample:
|
|
20
21
|
evaluation_scope: str
|
|
21
22
|
metric: str
|
|
22
|
-
baseline: Optional[
|
|
23
|
-
enriched: Optional[
|
|
23
|
+
baseline: Optional[float] = None
|
|
24
|
+
enriched: Optional[float] = None
|
|
25
|
+
std: Optional[float] = None
|
|
24
26
|
uplift: Optional[float] = None
|
|
25
27
|
relative_uplift: Optional[str] = None
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
@dataclass
|
|
31
|
+
class SampleStats:
|
|
32
|
+
sample: str
|
|
33
|
+
rows: Optional[int] = None
|
|
34
|
+
mean_target: Optional[float] = None
|
|
35
|
+
date_range: Optional[str] = None
|
|
36
|
+
labeled: Optional[int] = None
|
|
37
|
+
unlabeled: Optional[int] = None
|
|
38
|
+
positive: Optional[int] = None
|
|
39
|
+
|
|
40
|
+
|
|
28
41
|
@dataclass
|
|
29
42
|
class FeatureRow:
|
|
30
43
|
name: str
|
|
@@ -66,6 +79,8 @@ class ReportData:
|
|
|
66
79
|
metadata: ReportMetadata
|
|
67
80
|
quality_by_sample: list[QualitySample] = field(default_factory=list)
|
|
68
81
|
summary: SearchResultsSummary = field(default_factory=SearchResultsSummary)
|
|
82
|
+
sample_stats: list[SampleStats] = field(default_factory=list)
|
|
83
|
+
is_binary: bool = False
|
|
69
84
|
features: list[FeatureRow] = field(default_factory=list)
|
|
70
85
|
sources: list[SourceRow] = field(default_factory=list)
|
|
71
86
|
autofe: list[dict[str, str]] = field(default_factory=list)
|