upgini 1.2.167a4220.dev6__tar.gz → 1.2.167a4220.dev8__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.dev6 → upgini-1.2.167a4220.dev8}/PKG-INFO +1 -1
- upgini-1.2.167a4220.dev8/src/upgini/__about__.py +1 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/features_enricher.py +75 -53
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/report/assemble.py +49 -32
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/report/html.py +14 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/report/template.html +4 -4
- upgini-1.2.167a4220.dev6/src/upgini/__about__.py +0 -1
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/.gitignore +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/LICENSE +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/README.md +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/pyproject.toml +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/ads.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/ads_management/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/ads_management/ads_manager.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/all_operators.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/binary.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/date.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/feature.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/groupby.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/operand.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/operator.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/base.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/cross.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/delta.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/lag.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/numpy_kernels.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/roll.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/trend.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/volatility.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/unary.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/vector.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/data_source/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/data_source/data_source_publisher.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/dataset.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/errors.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/http.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/mdc/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/mdc/context.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/metadata.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/metrics.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/normalizer/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/normalizer/normalize_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/report/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/report/data.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/report/stats.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/exceptions.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/strings.properties +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/strings_widget.properties +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/sampler/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/sampler/base.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/sampler/random_under_sampler.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/sampler/utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/search_task.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/spinner.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/Roboto-Regular.ttf +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/__init__.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/base_search_key_detector.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/blocked_time_series.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/config.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/country_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/cpu_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/custom_loss_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/cv_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/datetime_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/deduplicate_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/display_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/email_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/fallback_progress_bar.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/feature_info.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/features_validator.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/format.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/hash_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/ip_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/mstats.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/one_hot_encoder.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/phone_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/postal_code_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/progress_bar.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/psi.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/pyarrow_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/sample_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/search_key_derivations.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/sklearn_ext.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/sort.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/target_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/track_info.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/ts_utils.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/warning_counter.py +0 -0
- {upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/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.dev8
|
|
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.dev8"
|
|
@@ -98,7 +98,7 @@ from upgini.utils.deduplicate_utils import (
|
|
|
98
98
|
clean_full_duplicates,
|
|
99
99
|
remove_fintech_duplicates,
|
|
100
100
|
)
|
|
101
|
-
from upgini.report.assemble import assemble_report_data,
|
|
101
|
+
from upgini.report.assemble import assemble_report_data, autofe_rows_for_report, build_search_results
|
|
102
102
|
from upgini.report.html import generate_html_report
|
|
103
103
|
from upgini.report.stats import make_report_frame
|
|
104
104
|
from upgini.utils.display_utils import (
|
|
@@ -2288,22 +2288,32 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2288
2288
|
return self._get_single_ensemble_score_name() is not None
|
|
2289
2289
|
|
|
2290
2290
|
def _get_single_ensemble_score_name(self) -> str | None:
|
|
2291
|
+
if len(self._ensemble_generated_metadata()) != 1:
|
|
2292
|
+
return None
|
|
2291
2293
|
selected = list(self.feature_names_ or [])
|
|
2292
2294
|
ensemble = [name for name in selected if self._is_ensemble_feature(name)]
|
|
2293
2295
|
if len(ensemble) != 1:
|
|
2294
2296
|
return None
|
|
2295
|
-
|
|
2296
|
-
if self._search_task is not None:
|
|
2297
|
-
etalon_columns = {
|
|
2298
|
-
meta.name
|
|
2299
|
-
for meta in (self._search_task.get_all_features_metadata_v2() or [])
|
|
2300
|
-
if meta.source == "etalon"
|
|
2301
|
-
}
|
|
2302
|
-
extras = [name for name in selected if name != ensemble[0] and name not in etalon_columns]
|
|
2303
|
-
if extras:
|
|
2297
|
+
if any(self._is_extra_enriched_feature(name) for name in selected):
|
|
2304
2298
|
return None
|
|
2305
2299
|
return ensemble[0]
|
|
2306
2300
|
|
|
2301
|
+
def _is_extra_enriched_feature(self, name: str) -> bool:
|
|
2302
|
+
if self._is_ensemble_feature(name):
|
|
2303
|
+
return False
|
|
2304
|
+
renaming = self.fit_columns_renaming or {}
|
|
2305
|
+
aliases = self._column_name_aliases([name], renaming)
|
|
2306
|
+
if aliases & set(self.external_source_feature_names or []):
|
|
2307
|
+
return True
|
|
2308
|
+
if self._search_task is None:
|
|
2309
|
+
return False
|
|
2310
|
+
for meta in self._search_task.get_all_features_metadata_v2() or []:
|
|
2311
|
+
if meta.source == "etalon" or self._is_ensemble_feature(meta.name):
|
|
2312
|
+
continue
|
|
2313
|
+
if aliases & self._column_name_aliases([meta.name], renaming):
|
|
2314
|
+
return True
|
|
2315
|
+
return False
|
|
2316
|
+
|
|
2307
2317
|
def _reference_rows(self) -> int | None:
|
|
2308
2318
|
if self.X is None:
|
|
2309
2319
|
return None
|
|
@@ -2464,12 +2474,18 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2464
2474
|
if self._search_task is not None:
|
|
2465
2475
|
features_meta = list(self._search_task.get_all_features_metadata_v2() or [])
|
|
2466
2476
|
features, sources, model_shap, summary = build_search_results(
|
|
2467
|
-
model_names=self._ensemble_model_feature_names(),
|
|
2468
2477
|
features_meta=features_meta,
|
|
2469
|
-
base_columns=self._ensemble_base_columns(),
|
|
2470
2478
|
is_ensemble=self._is_ensemble_feature,
|
|
2479
|
+
generated_names=self._column_name_aliases(
|
|
2480
|
+
self.fit_generated_features or [], self.fit_columns_renaming or {}
|
|
2481
|
+
),
|
|
2482
|
+
)
|
|
2483
|
+
autofe = autofe_rows_for_report(
|
|
2484
|
+
self.get_autofe_features_description(features_meta=features_meta),
|
|
2485
|
+
features_meta,
|
|
2486
|
+
self._is_ensemble_feature,
|
|
2487
|
+
self.bundle,
|
|
2471
2488
|
)
|
|
2472
|
-
autofe = autofe_rows_from_description(self.get_autofe_features_description(), self.bundle)
|
|
2473
2489
|
return features, sources, model_shap, summary, autofe
|
|
2474
2490
|
|
|
2475
2491
|
def _score_report_html(self) -> str | None:
|
|
@@ -2512,25 +2528,6 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2512
2528
|
names.add(meta.alias)
|
|
2513
2529
|
return names
|
|
2514
2530
|
|
|
2515
|
-
def _ensemble_base_columns(self) -> list[tuple[str, str]]:
|
|
2516
|
-
return [
|
|
2517
|
-
(column.original_name, column.hashed_name)
|
|
2518
|
-
for meta, _ in self._ensemble_generated_metadata()
|
|
2519
|
-
for column in meta.base_columns
|
|
2520
|
-
]
|
|
2521
|
-
|
|
2522
|
-
def _ensemble_model_feature_names(self) -> list[str]:
|
|
2523
|
-
return sorted(
|
|
2524
|
-
{
|
|
2525
|
-
original
|
|
2526
|
-
for original, hashed in self._ensemble_base_columns()
|
|
2527
|
-
if not self._is_ensemble_feature(original) and not self._is_ensemble_feature(hashed)
|
|
2528
|
-
}
|
|
2529
|
-
)
|
|
2530
|
-
|
|
2531
|
-
def _ensemble_model_feature_count(self) -> int | None:
|
|
2532
|
-
return len(self._ensemble_model_feature_names()) or None
|
|
2533
|
-
|
|
2534
2531
|
def _psi_exceeds_threshold(self, psi: float | None, threshold: float) -> bool:
|
|
2535
2532
|
return psi is not None and not pd.isna(psi) and psi > threshold
|
|
2536
2533
|
|
|
@@ -5942,12 +5939,12 @@ if response.status_code == 200:
|
|
|
5942
5939
|
else:
|
|
5943
5940
|
self.logger.warning("Empty features info")
|
|
5944
5941
|
|
|
5945
|
-
def get_autofe_features_description(self):
|
|
5942
|
+
def get_autofe_features_description(self, features_meta: list[FeaturesMetadataV2] | None = None):
|
|
5946
5943
|
try:
|
|
5947
5944
|
autofe_meta = self._search_task.get_autofe_metadata()
|
|
5948
5945
|
if autofe_meta is None:
|
|
5949
5946
|
return None
|
|
5950
|
-
if len(self._internal_features_info) != 0:
|
|
5947
|
+
if features_meta is None and len(self._internal_features_info) != 0:
|
|
5951
5948
|
|
|
5952
5949
|
def to_feature_meta(row):
|
|
5953
5950
|
fm = FeaturesMetadataV2(
|
|
@@ -5961,13 +5958,16 @@ if response.status_code == 200:
|
|
|
5961
5958
|
return fm
|
|
5962
5959
|
|
|
5963
5960
|
features_meta = self._internal_features_info.apply(to_feature_meta, axis=1).to_list()
|
|
5964
|
-
|
|
5961
|
+
elif features_meta is None:
|
|
5965
5962
|
features_meta = self._search_task.get_all_features_metadata_v2()
|
|
5966
5963
|
|
|
5967
5964
|
def get_feature_by_name(name: str):
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5965
|
+
if not name or not features_meta:
|
|
5966
|
+
return None
|
|
5967
|
+
for candidate in features_meta:
|
|
5968
|
+
if candidate.name == name:
|
|
5969
|
+
return candidate
|
|
5970
|
+
return None
|
|
5971
5971
|
|
|
5972
5972
|
descriptions = []
|
|
5973
5973
|
for m in autofe_meta:
|
|
@@ -5988,11 +5988,28 @@ if response.status_code == 200:
|
|
|
5988
5988
|
|
|
5989
5989
|
description = {}
|
|
5990
5990
|
|
|
5991
|
-
|
|
5992
|
-
if
|
|
5991
|
+
lookup_names = [autofe_feature.get_display_name(shorten=True, unhash=True)]
|
|
5992
|
+
if m.alias:
|
|
5993
|
+
lookup_names.extend([m.alias, f"f_autofe_{m.alias}"])
|
|
5994
|
+
feature_meta = None
|
|
5995
|
+
for lookup_name in lookup_names:
|
|
5996
|
+
feature_meta = get_feature_by_name(lookup_name)
|
|
5997
|
+
if feature_meta is not None:
|
|
5998
|
+
break
|
|
5999
|
+
if feature_meta is None and m.alias and features_meta:
|
|
6000
|
+
prefix = f"f_autofe_{m.alias}"
|
|
6001
|
+
matches = [
|
|
6002
|
+
candidate
|
|
6003
|
+
for candidate in features_meta
|
|
6004
|
+
if candidate.name == prefix or candidate.name.startswith(prefix + "_")
|
|
6005
|
+
]
|
|
6006
|
+
if len(matches) == 1:
|
|
6007
|
+
feature_meta = matches[0]
|
|
6008
|
+
if feature_meta is None or not feature_meta.shap_value:
|
|
5993
6009
|
continue
|
|
5994
6010
|
description["shap"] = feature_meta.shap_value
|
|
5995
|
-
|
|
6011
|
+
source = feature_meta.data_source or ""
|
|
6012
|
+
description[self.bundle.get("autofe_descriptions_sources")] = source.replace(
|
|
5996
6013
|
"AutoFE: features from ", ""
|
|
5997
6014
|
).replace("AutoFE: feature from ", "")
|
|
5998
6015
|
description[self.bundle.get("autofe_descriptions_feature_name")] = feature_meta.name
|
|
@@ -6224,19 +6241,24 @@ if response.status_code == 200:
|
|
|
6224
6241
|
print(self._internal_features_info)
|
|
6225
6242
|
|
|
6226
6243
|
def __show_report_button(self, display_id: str | None = None, display_handle=None):
|
|
6244
|
+
if self._has_single_ensemble_score():
|
|
6245
|
+
try:
|
|
6246
|
+
report_html = self._score_report_html()
|
|
6247
|
+
if report_html:
|
|
6248
|
+
if not ipython_available():
|
|
6249
|
+
return
|
|
6250
|
+
search_id = (
|
|
6251
|
+
self._search_task.search_task_id if self._search_task is not None else (self.search_id or "")
|
|
6252
|
+
)
|
|
6253
|
+
return show_button_open_report(
|
|
6254
|
+
report_html,
|
|
6255
|
+
download_name=f"upgini-report-{search_id}.html",
|
|
6256
|
+
display_id=display_id,
|
|
6257
|
+
display_handle=display_handle,
|
|
6258
|
+
)
|
|
6259
|
+
except Exception:
|
|
6260
|
+
self.logger.exception("Failed to generate HTML report, falling back to PDF")
|
|
6227
6261
|
try:
|
|
6228
|
-
report_html = self._score_report_html() or self.report_html
|
|
6229
|
-
if report_html:
|
|
6230
|
-
if not ipython_available():
|
|
6231
|
-
return
|
|
6232
|
-
search_id = self._search_task.search_task_id if self._search_task is not None else (self.search_id or "")
|
|
6233
|
-
return show_button_open_report(
|
|
6234
|
-
report_html,
|
|
6235
|
-
download_name=f"upgini-report-{search_id}.html",
|
|
6236
|
-
display_id=display_id,
|
|
6237
|
-
display_handle=display_handle,
|
|
6238
|
-
)
|
|
6239
|
-
|
|
6240
6262
|
eval_sets_drift_df = self._get_eval_sets_drift_summary()
|
|
6241
6263
|
|
|
6242
6264
|
return prepare_and_show_report(
|
|
@@ -98,13 +98,20 @@ def assemble_report_data(
|
|
|
98
98
|
|
|
99
99
|
def build_search_results(
|
|
100
100
|
*,
|
|
101
|
-
model_names: list[str],
|
|
102
101
|
features_meta: list[FeaturesMetadataV2],
|
|
103
|
-
base_columns: list[tuple[str, str]],
|
|
104
102
|
is_ensemble: Callable[[str], bool],
|
|
103
|
+
generated_names: Optional[set[str]] = None,
|
|
105
104
|
) -> tuple[list[FeatureRow], list[SourceRow], list[ModelFeatureShap], SearchResultsSummary]:
|
|
106
|
-
|
|
107
|
-
model_rows = [
|
|
105
|
+
generated_names = generated_names or set()
|
|
106
|
+
model_rows: list[FeatureRow] = []
|
|
107
|
+
found_rows: list[FeatureRow] = []
|
|
108
|
+
for meta in features_meta:
|
|
109
|
+
if is_ensemble(meta.name) or not _has_nonzero_shap_value(meta.shap_value):
|
|
110
|
+
continue
|
|
111
|
+
row = _feature_row(meta.name, meta, generated_names)
|
|
112
|
+
model_rows.append(row)
|
|
113
|
+
if meta.source != "etalon":
|
|
114
|
+
found_rows.append(row)
|
|
108
115
|
model_rows.sort(key=lambda row: (row.shap is None, -abs(row.shap or 0.0), row.name))
|
|
109
116
|
model_shap = [
|
|
110
117
|
ModelFeatureShap(
|
|
@@ -115,19 +122,12 @@ def build_search_results(
|
|
|
115
122
|
)
|
|
116
123
|
for index, row in enumerate(model_rows, start=1)
|
|
117
124
|
]
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for meta in features_meta
|
|
121
|
-
if meta.source != "etalon" and not is_ensemble(meta.name)
|
|
122
|
-
]
|
|
123
|
-
sources = _source_rows(join_rows)
|
|
124
|
-
relevant = sum(1 for row in join_rows if _has_nonzero_shap(row))
|
|
125
|
-
contributed = len({(row.provider, row.source) for row in join_rows if row.provider and _has_nonzero_shap(row)})
|
|
126
|
-
model_count = len(model_names) or None
|
|
125
|
+
sources = _source_rows(found_rows)
|
|
126
|
+
contributed = len({(row.provider, row.source) for row in found_rows if row.provider})
|
|
127
127
|
stable = sum(1 for row in model_rows if row.psi is not None and row.psi < _STABILITY_PSI)
|
|
128
128
|
summary = SearchResultsSummary(
|
|
129
|
-
relevant_features=
|
|
130
|
-
model_features=
|
|
129
|
+
relevant_features=len(found_rows),
|
|
130
|
+
model_features=len(model_rows) or None,
|
|
131
131
|
data_sources=len(sources),
|
|
132
132
|
stable_features_share=(stable / len(model_rows)) if model_rows else None,
|
|
133
133
|
contributed_sources=contributed,
|
|
@@ -136,6 +136,31 @@ def build_search_results(
|
|
|
136
136
|
return model_rows, sources, model_shap, summary
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
def autofe_rows_for_report(
|
|
140
|
+
df: Optional[pd.DataFrame],
|
|
141
|
+
features_meta: list[FeaturesMetadataV2],
|
|
142
|
+
is_ensemble: Callable[[str], bool],
|
|
143
|
+
bundle: ResourceBundle,
|
|
144
|
+
) -> list[dict[str, str]]:
|
|
145
|
+
rows = autofe_rows_from_description(df, bundle)
|
|
146
|
+
listed = {row["generatedFeature"] for row in rows if row.get("generatedFeature")}
|
|
147
|
+
for meta in features_meta:
|
|
148
|
+
if is_ensemble(meta.name) or meta.source != "generated":
|
|
149
|
+
continue
|
|
150
|
+
if not _has_nonzero_shap_value(meta.shap_value) or meta.name in listed:
|
|
151
|
+
continue
|
|
152
|
+
row = _feature_row(meta.name, meta)
|
|
153
|
+
rows.append(
|
|
154
|
+
{
|
|
155
|
+
"sources": row.source,
|
|
156
|
+
"generatedFeature": row.name,
|
|
157
|
+
"sourceFeatures": "",
|
|
158
|
+
"functions": "",
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
return rows
|
|
162
|
+
|
|
163
|
+
|
|
139
164
|
def autofe_rows_from_description(df: Optional[pd.DataFrame], bundle: ResourceBundle) -> list[dict[str, str]]:
|
|
140
165
|
if df is None or df.empty:
|
|
141
166
|
return []
|
|
@@ -234,23 +259,15 @@ def _as_int(value) -> Optional[int]:
|
|
|
234
259
|
return None if pd.isna(value) else int(value)
|
|
235
260
|
|
|
236
261
|
|
|
237
|
-
def
|
|
238
|
-
|
|
239
|
-
) ->
|
|
240
|
-
index = {meta.name: meta for meta in features_meta}
|
|
241
|
-
for original, hashed in base_columns:
|
|
242
|
-
matched = index.get(hashed) or index.get(original)
|
|
243
|
-
if matched is None:
|
|
244
|
-
continue
|
|
245
|
-
index[original] = matched
|
|
246
|
-
index[hashed] = matched
|
|
247
|
-
return index
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
def _feature_row(name: str, meta: Optional[FeaturesMetadataV2]) -> FeatureRow:
|
|
262
|
+
def _feature_row(
|
|
263
|
+
name: str, meta: Optional[FeaturesMetadataV2], generated_names: Optional[set[str]] = None
|
|
264
|
+
) -> FeatureRow:
|
|
251
265
|
if meta is None:
|
|
252
266
|
return FeatureRow(name=name)
|
|
253
|
-
|
|
267
|
+
generated_names = generated_names or set()
|
|
268
|
+
is_generated = name in generated_names or meta.source == "generated"
|
|
269
|
+
is_client = meta.source == "etalon" and not is_generated
|
|
270
|
+
info = FeatureInfo.from_metadata(meta, None, is_client, is_generated)
|
|
254
271
|
return FeatureRow(
|
|
255
272
|
name=name,
|
|
256
273
|
shap=meta.shap_value,
|
|
@@ -292,5 +309,5 @@ def _psi_status(psi: Optional[float]) -> Optional[str]:
|
|
|
292
309
|
return "good"
|
|
293
310
|
|
|
294
311
|
|
|
295
|
-
def
|
|
296
|
-
return
|
|
312
|
+
def _has_nonzero_shap_value(shap: Optional[float]) -> bool:
|
|
313
|
+
return shap is not None and not pd.isna(shap) and shap != 0
|
|
@@ -6,6 +6,7 @@ from typing import Optional
|
|
|
6
6
|
|
|
7
7
|
from upgini.report.data import FeatureRow, ReportCharts, ReportData, SampleStats, SearchResultsSummary, SourceRow
|
|
8
8
|
from upgini.report.stats import MEAN_AXIS_PADDING
|
|
9
|
+
from upgini.utils.feature_info import CLIENT_SOURCE, GENERATED_SOURCE
|
|
9
10
|
|
|
10
11
|
_TEMPLATE_PATH = Path(__file__).with_name("template.html")
|
|
11
12
|
_SAMPLE_COLORS = ("#1645ee", "#20aab4", "#53cc61", "#e19132", "#d45b5b")
|
|
@@ -160,6 +161,7 @@ def _features_payload(features: list[FeatureRow]) -> list[dict]:
|
|
|
160
161
|
"name": row.name,
|
|
161
162
|
"provider": row.provider,
|
|
162
163
|
"source": row.source,
|
|
164
|
+
"shapClass": _shap_fill_class(row),
|
|
163
165
|
"importance": abs(row.shap) if row.shap is not None else 0,
|
|
164
166
|
"shap": row.shap,
|
|
165
167
|
"coverage": row.coverage,
|
|
@@ -171,6 +173,18 @@ def _features_payload(features: list[FeatureRow]) -> list[dict]:
|
|
|
171
173
|
]
|
|
172
174
|
|
|
173
175
|
|
|
176
|
+
def _shap_fill_class(row: FeatureRow) -> str:
|
|
177
|
+
source = (row.source or "").lower()
|
|
178
|
+
provider = (row.provider or "").lower()
|
|
179
|
+
if source.startswith("autofe") or provider == "autofe" or source == GENERATED_SOURCE.lower():
|
|
180
|
+
return "autofe"
|
|
181
|
+
if source == CLIENT_SOURCE.lower() or provider == "user":
|
|
182
|
+
return "user"
|
|
183
|
+
if provider in {"", "upgini"}:
|
|
184
|
+
return "upgini"
|
|
185
|
+
return "external"
|
|
186
|
+
|
|
187
|
+
|
|
174
188
|
def _sources_payload(sources: list[SourceRow]) -> list[dict]:
|
|
175
189
|
return [
|
|
176
190
|
{
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<style>
|
|
8
8
|
:root{--ink:#15211f;--muted:#6f7976;--line:#dce4e1;--paper:#fff;--bg:#f3f6f5;--blue:#1645ee;--green:#53cc61;--cyan:#20aab4;--warn:#e19132;--danger:#d45b5b;--shadow:0 18px 60px rgba(18,45,39,.08)}
|
|
9
9
|
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--bg);color:var(--ink);font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:14px;line-height:1.45}.shell{max-width:1440px;margin:auto;min-height:100vh}.topbar{height:72px;padding:0 42px;display:flex;align-items:center;gap:25px;background:rgba(255,255,255,.92);backdrop-filter:blur(16px);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20}.brand{display:flex;align-items:center;gap:14px}.wordmark{font-size:25px;font-weight:800;letter-spacing:-.07em;line-height:1;background:linear-gradient(135deg,#0138f8,#5bd357);-webkit-background-clip:text;background-clip:text;color:transparent}.wordmark b{font-weight:850}.wordmark span{font-weight:650}.wordmark.small{font-size:20px;min-width:66px}.brand-divider{width:1px;height:25px;background:var(--line)}.brand-label{font-weight:700;color:#45514f}.top-meta{display:flex;gap:8px;margin-left:auto;align-items:center}.chip{background:#eef2f1;border:1px solid #e0e7e5;border-radius:999px;padding:7px 11px;color:#53605d;font-size:12px}.button{border:0;border-radius:10px;padding:10px 15px;font:inherit;font-weight:700;cursor:pointer;transition:.18s ease}.button:hover{transform:translateY(-1px)}.button.primary{background:var(--ink);color:white}.button.ghost{background:white;border:1px solid var(--line);color:var(--ink)}.layout{display:grid;grid-template-columns:210px minmax(0,1fr);gap:0}.sidenav{position:sticky;top:72px;height:calc(100vh - 72px);padding:30px 22px;border-right:1px solid var(--line);background:#f7f9f8}.nav-title{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:#98a29f;margin:0 10px 12px}.nav-link{display:flex;align-items:center;gap:10px;color:#66716f;text-decoration:none;padding:10px;border-radius:9px;margin:2px 0;font-weight:600}.nav-link:hover,.nav-link.active{background:white;color:var(--ink);box-shadow:0 4px 18px rgba(25,49,43,.06)}.nav-dot{width:7px;height:7px;border-radius:50%;background:#c4cecb}.nav-link.active .nav-dot{background:linear-gradient(135deg,var(--blue),var(--green))}.run-note{position:absolute;bottom:24px;left:22px;right:22px;padding:14px;background:white;border:1px solid var(--line);border-radius:12px;font-size:11px;color:var(--muted)}.run-note strong{display:block;color:var(--ink);font-size:12px;margin-bottom:4px}.main{min-width:0;padding:38px 44px 80px}.eyebrow{display:flex;align-items:center;gap:8px;text-transform:uppercase;letter-spacing:.12em;color:#74807d;font-size:11px;font-weight:800}.status-dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 0 4px #e7f8e9}.hero-title{display:flex;justify-content:space-between;align-items:flex-end;gap:30px;margin:12px 0 28px}.hero-title h1{font-size:36px;line-height:1.08;letter-spacing:-.04em;margin:0;max-width:720px}.hero-title p{color:var(--muted);margin:10px 0 0;max-width:690px}.run-stamp{text-align:right;min-width:190px;color:var(--muted);font-size:12px}.run-stamp strong{display:block;color:var(--ink);font-size:14px;margin-bottom:3px}.metric-hero{position:relative;overflow:hidden;background:#172421;color:white;border-radius:22px;padding:28px 30px;box-shadow:var(--shadow);margin-bottom:18px}.metric-hero:after{content:"";position:absolute;width:400px;height:400px;right:-150px;top:-250px;border-radius:50%;background:radial-gradient(circle,rgba(71,215,117,.24),transparent 68%)}.metric-head{position:relative;z-index:1;display:flex;justify-content:space-between;gap:20px}.metric-tabs{display:flex;gap:5px;background:rgba(255,255,255,.08);border-radius:11px;padding:4px}.metric-tab{border:0;background:transparent;color:#aebbb7;padding:8px 12px;border-radius:8px;font-weight:700;cursor:pointer}.metric-tab.active{background:white;color:#13201e}.scores{position:relative;z-index:1;display:grid;grid-template-columns:1fr 1fr 1.2fr;gap:25px;margin-top:32px;align-items:end}.score-label{text-transform:uppercase;letter-spacing:.11em;color:#8fa09b;font-size:10px;font-weight:800}.score-value{font-size:39px;line-height:1;margin-top:8px;font-variant-numeric:tabular-nums;letter-spacing:-.04em}.uplift .score-value{color:#73e57b}.uplift p{color:#aebcb8;margin:8px 0 0;font-size:12px}.summary-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:18px 0 34px}.summary{background:white;border:1px solid var(--line);border-radius:15px;padding:17px 18px}.summary .k{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.09em;font-weight:800}.summary .v{font-size:23px;font-weight:750;margin-top:5px;letter-spacing:-.02em}.summary .d{font-size:11px;color:#88928f;margin-top:4px}.section{scroll-margin-top:94px;margin-top:36px}.section-head{display:flex;align-items:end;justify-content:space-between;gap:20px;margin-bottom:15px}.section-head h2{font-size:22px;letter-spacing:-.025em;margin:0}.section-head p{color:var(--muted);margin:5px 0 0;font-size:12px}.panel{background:white;border:1px solid var(--line);border-radius:17px;box-shadow:0 4px 20px rgba(18,45,39,.035)}.chart-panel{padding:20px}.chart-head{display:flex;justify-content:space-between;align-items:center;gap:16px;margin-bottom:10px}.legend{display:flex;gap:17px;flex-wrap:wrap;font-size:11px;color:var(--muted)}.legend i{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px}.control{border:1px solid var(--line);background:#fafcfb;color:var(--ink);border-radius:9px;padding:8px 10px;font:inherit;font-size:12px}.canvas-wrap{height:280px;position:relative}canvas{width:100%;height:100%}.callout{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;background:#fff8ed;border:1px solid #f1dcc0;border-radius:13px;color:#6b4b20}.callout strong{display:block;color:#4c361a;margin-bottom:2px}.callout-icon{width:27px;height:27px;display:grid;place-items:center;background:#f4c77f;border-radius:8px;font-weight:900;color:#65410c;flex:none}.stability-grid{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(260px,.5fr);gap:14px}.heatmap{overflow:auto;padding:16px}.heat-table{border-collapse:separate;border-spacing:5px;width:100%;font-size:11px}.heat-table th{color:var(--muted);font-weight:600;padding:2px 6px;white-space:nowrap}.heat-table th:first-child{text-align:left}.heat-table td:first-child{font-weight:700;padding-right:12px;white-space:nowrap}.heat{height:30px;min-width:47px;text-align:center;border-radius:6px;font-weight:750}.s1{background:#dbf4de;color:#276b2f}.s2{background:#e9f4cf;color:#526826}.s3{background:#fff0c7;color:#7f641b}.s4{background:#f8d5c8;color:#914634}.stability-side{padding:20px}.donut{width:140px;height:140px;border-radius:50%;margin:8px auto 17px;background:conic-gradient(var(--green) 0 72%,#e6b556 72% 91%,#df7474 91%);position:relative}.donut:after{content:"";position:absolute;inset:25px;background:white;border-radius:50%}.donut-label{position:absolute;inset:0;z-index:1;display:grid;place-items:center;text-align:center;font-weight:800;font-size:21px}.donut-label small{display:block;color:var(--muted);font-size:10px;font-weight:600}.stat-list{display:grid;gap:8px}.stat-row{display:flex;justify-content:space-between;padding-top:8px;border-top:1px solid #edf1ef;color:var(--muted);font-size:12px}.stat-row b{color:var(--ink)}.toolbar{display:flex;gap:9px;flex-wrap:wrap}.search{min-width:230px;border:1px solid var(--line);background:white;border-radius:9px;padding:9px 11px;font:inherit}.table-wrap{overflow:auto}.data-table{width:100%;border-collapse:collapse;min-width:860px}.data-table th{text-align:left;padding:12px 14px;background:#f8faf9;color:#77827f;text-transform:uppercase;letter-spacing:.07em;font-size:10px;border-bottom:1px solid var(--line)}.data-table td{padding:13px 14px;border-bottom:1px solid #edf1ef;font-size:12px}.data-table tr:last-child td{border-bottom:0}.data-table tbody tr:hover{background:#f8fbfa}.feature-name{font-weight:750;color:#25312f}.source-name{color:#74807d}.bar{height:6px;width:90px;background:#edf1ef;border-radius:99px;overflow:hidden}.bar i{height:100%;display:block;background:linear-gradient(90deg,var(--blue),#43c878);border-radius:99px}.tag{display:inline-flex;align-items:center;border-radius:999px;padding:4px 8px;font-size:10px;font-weight:750}.tag.good{background:#e8f7ea;color:#337139}.tag.watch{background:#fff1d9;color:#855d18}.tag.risk{background:#fde7e4;color:#984a40}.source-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.source-card{padding:19px}.source-top{display:flex;justify-content:space-between;gap:12px}.source-icon{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;background:linear-gradient(135deg,#e8eeff,#e8f9e8);font-weight:850;color:var(--blue)}.source-card h3{font-size:14px;margin:12px 0 4px}.source-card p{font-size:11px;color:var(--muted);margin:0}.source-metrics{display:flex;gap:18px;margin-top:17px;padding-top:13px;border-top:1px solid #edf1ef}.source-metrics b{display:block;font-size:16px}.source-metrics span{color:var(--muted);font-size:10px}.notebook{padding:0;overflow:hidden}.cell-head{background:#f8faf9;border-bottom:1px solid var(--line);padding:10px 14px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:#68736f;font-size:11px}.cell-output{padding:18px;display:flex;align-items:center;gap:25px;flex-wrap:wrap}.cell-quality{display:flex;gap:24px}.cell-quality span{font-size:11px;color:var(--muted)}.cell-quality b{font-size:17px;color:var(--ink);display:block}.footer{text-align:center;color:#8a9592;font-size:11px;padding:40px 0 10px}.empty{display:none;padding:24px;text-align:center;color:var(--muted)}
|
|
10
|
-
.sample-table{min-width:700px}.toggle-legend{margin-bottom:10px}.series-toggle{border:0;background:transparent;color:var(--muted);cursor:pointer;font:inherit;font-size:11px;padding:5px 7px;border-radius:6px}.series-toggle:not(.active){opacity:.35;text-decoration:line-through}.threshold-legend{display:flex;gap:8px;justify-content:flex-end;margin-bottom:10px}.shap-panel{padding:16px}.shap-row{display:grid;grid-template-columns:minmax(170px,1fr) 2fr 60px;gap:12px;align-items:center;padding:9px 4px;border-bottom:1px solid #edf1ef}.shap-row:last-child{border:0}.provider{font-size:9px;margin-left:6px;padding:3px 6px;border-radius:99px;border:1px solid var(--line);color:var(--muted)}.shap-track{height:10px;background:#edf1ef;border-radius:99px;overflow:hidden}.shap-fill{display:block;height:100%;border-radius:99px}.shap-fill.upgini{background:#1645ee}.shap-fill.user{background:#20aab4}.shap-fill.autofe{background:#53cc61}.result-tabs{display:flex;gap:4px;margin-bottom:12px}.result-tab{border:1px solid var(--line);background:#eef2f1;padding:9px 13px;border-radius:9px;font:inherit;cursor:pointer;color:var(--muted)}.result-tab.active{background:var(--ink);color:#fff}.result-tab b{font-size:10px;margin-left:5px}.result-pane{display:none}.result-pane.active{display:block}.table-actions{display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap;margin-bottom:10px}.chart-tooltip{position:absolute;display:none;pointer-events:none;background:#172421;color:white;padding:9px 11px;border-radius:8px;font-size:10px;box-shadow:var(--shadow);white-space:nowrap}.sidenav{overflow:auto}.run-note{position:static;margin-top:22px}.nav-link{font-size:12px;padding:8px 10px}
|
|
10
|
+
.sample-table{min-width:700px}.toggle-legend{margin-bottom:10px}.series-toggle{border:0;background:transparent;color:var(--muted);cursor:pointer;font:inherit;font-size:11px;padding:5px 7px;border-radius:6px}.series-toggle:not(.active){opacity:.35;text-decoration:line-through}.threshold-legend{display:flex;gap:8px;justify-content:flex-end;margin-bottom:10px}.shap-panel{padding:16px}.shap-row{display:grid;grid-template-columns:minmax(170px,1fr) 2fr 60px;gap:12px;align-items:center;padding:9px 4px;border-bottom:1px solid #edf1ef}.shap-row:last-child{border:0}.provider{font-size:9px;margin-left:6px;padding:3px 6px;border-radius:99px;border:1px solid var(--line);color:var(--muted)}.shap-track{height:10px;background:#edf1ef;border-radius:99px;overflow:hidden}.shap-fill{display:block;height:100%;border-radius:99px}.shap-fill.upgini{background:#1645ee}.shap-fill.user{background:#20aab4}.shap-fill.autofe{background:#53cc61}.shap-fill.external{background:#7b5cff}.result-tabs{display:flex;gap:4px;margin-bottom:12px}.result-tab{border:1px solid var(--line);background:#eef2f1;padding:9px 13px;border-radius:9px;font:inherit;cursor:pointer;color:var(--muted)}.result-tab.active{background:var(--ink);color:#fff}.result-tab b{font-size:10px;margin-left:5px}.result-pane{display:none}.result-pane.active{display:block}.table-actions{display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap;margin-bottom:10px}.chart-tooltip{position:absolute;display:none;pointer-events:none;background:#172421;color:white;padding:9px 11px;border-radius:8px;font-size:10px;box-shadow:var(--shadow);white-space:nowrap}.sidenav{overflow:auto}.run-note{position:static;margin-top:22px}.nav-link{font-size:12px;padding:8px 10px}
|
|
11
11
|
.metric-sample-table{position:relative;z-index:1;margin-top:26px}.metric-sample-head,.metric-sample-row{display:grid;grid-template-columns:minmax(140px,.9fr) 1fr 1fr .8fr .8fr;gap:18px;align-items:center}.metric-sample-ci{display:block;margin-top:6px;color:#8fa09b;font-size:11px;font-weight:600;font-variant-numeric:tabular-nums}.metric-note{color:#8fa09b;font-size:11px;margin-top:6px}.partner-logo{height:30px;width:auto;display:block}.metric-sample-head{padding:0 14px 8px;color:#8fa09b;text-transform:uppercase;letter-spacing:.11em;font-size:10px;font-weight:800}.metric-sample-row{padding:15px 14px;border-top:1px solid rgba(255,255,255,.12)}.metric-sample-name{display:flex;align-items:center;gap:10px;font-size:16px;font-weight:760}.metric-sample-name i{width:9px;height:9px;border-radius:50%;background:var(--blue);box-shadow:0 0 0 4px rgba(22,69,238,.18)}.metric-sample-row[data-sample="eval_1"] .metric-sample-name i{background:var(--cyan);box-shadow:0 0 0 4px rgba(32,170,180,.18)}.metric-sample-row[data-sample="eval_2"] .metric-sample-name i{background:var(--green);box-shadow:0 0 0 4px rgba(83,204,97,.18)}.metric-sample-name small{display:block;color:#8fa09b;font-size:10px;font-weight:650;margin-top:2px}.metric-sample-value{font-size:27px;line-height:1;font-variant-numeric:tabular-nums;letter-spacing:-.035em}.metric-sample-uplift .metric-sample-value{color:#73e57b}.metric-sample-uplift p{margin:5px 0 0;color:#aebcb8;font-size:11px}.tall-chart{height:340px}.chart-tooltip b{font-size:11px}.chart-tooltip .tip-label{color:#aebbb7}.canvas-wrap canvas{cursor:crosshair}.search-id-chip{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
12
12
|
@media(max-width:980px){.layout{grid-template-columns:1fr}.sidenav{display:none}.main{padding:28px 22px 70px}.topbar{padding:0 22px}.scores{grid-template-columns:1fr 1fr}.uplift{grid-column:1/-1}.summary-grid{grid-template-columns:1fr 1fr}.stability-grid,.sample-grid{grid-template-columns:1fr}.source-cards{grid-template-columns:1fr 1fr}}
|
|
13
13
|
@media(max-width:650px){.top-meta .chip,.brand-divider,.brand-label{display:none}.topbar{gap:10px}.button{padding:9px 10px}.hero-title{display:block}.run-stamp{text-align:left;margin-top:16px}.hero-title h1{font-size:30px}.metric-head{display:block}.metric-tabs{margin-top:15px;width:max-content}.summary-grid,.source-cards{grid-template-columns:1fr}.scores{gap:14px}.score-value{font-size:32px}.metric-sample-head{display:none}.metric-sample-row{grid-template-columns:1fr 1fr;gap:14px;padding:16px 2px}.metric-sample-name{grid-column:1/-1}.metric-sample-value{font-size:24px}.metric-sample-row>div:not(.metric-sample-name):before{display:block;color:#8fa09b;text-transform:uppercase;letter-spacing:.08em;font-size:8px;font-weight:800;margin-bottom:6px}.metric-sample-row>div:nth-child(2):before{content:"Baseline"}.metric-sample-row>div:nth-child(3):before{content:"Enriched"}.metric-sample-row>div:nth-child(4):before{content:"Uplift, abs"}.metric-sample-row>div:nth-child(5):before{content:"Uplift, %"}.main{padding-left:15px;padding-right:15px}.cell-output{gap:14px}}
|
|
@@ -507,8 +507,8 @@ const REPORT_DATA = __REPORT_DATA__;
|
|
|
507
507
|
const maxImportance = Math.max(...features.map(f => f.importance)) || 1;
|
|
508
508
|
const list = shapAll ? features : features.slice(0, (D.shap && D.shap.topN) || features.length);
|
|
509
509
|
document.getElementById('shapList').innerHTML = list.map((f, i) =>
|
|
510
|
-
`<div class="shap-row"><div><b>#${i + 1} ${f.name}</b
|
|
511
|
-
`<div class="shap-track"><i class="shap-fill ${f.
|
|
510
|
+
`<div class="shap-row"><div><b>#${i + 1} ${f.name}</b>${f.provider ? `<span class="provider">${f.provider}</span>` : ''}</div>` +
|
|
511
|
+
`<div class="shap-track"><i class="shap-fill ${f.shapClass || 'upgini'}" style="width:${(f.importance / maxImportance * 100).toFixed(2)}%"></i></div>` +
|
|
512
512
|
`<b>${fmt3(Math.abs(f.shap))}</b></div>`).join('');
|
|
513
513
|
document.getElementById('showAllShap').textContent = shapAll ? `Show top ${(D.shap && D.shap.topN) || features.length}` : `Show all ${features.length}`;
|
|
514
514
|
}
|
|
@@ -534,7 +534,7 @@ const REPORT_DATA = __REPORT_DATA__;
|
|
|
534
534
|
const body = document.getElementById('featureBody');
|
|
535
535
|
body.innerHTML = rows.map(f =>
|
|
536
536
|
`<tr data-name="${f.name}"><td><span class="feature-name" title="${f.name}">${f.name}</span></td>` +
|
|
537
|
-
`<td
|
|
537
|
+
`<td>${f.provider ? `<span class="provider">${f.provider}</span>` : ''}</td><td class="source-name">${f.source}</td>` +
|
|
538
538
|
`<td>${shapLabel(f.shap)}</td><td><span class="tag ${f.status}">${fmt2(f.psi)}</span></td>` +
|
|
539
539
|
`<td>${f.drift == null ? '—' : fmt2(f.drift)}</td><td>${pct1(f.coverage)}</td></tr>`).join('');
|
|
540
540
|
document.getElementById('emptyState').style.display = rows.length ? 'none' : 'block';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.167.a4220.dev6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/ads_management/ads_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/numpy_kernels.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/autofe/timeseries/volatility.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/normalizer/normalize_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/__init__.py
RENAMED
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/exceptions.py
RENAMED
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/resource_bundle/strings.properties
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/sampler/random_under_sampler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/base_search_key_detector.py
RENAMED
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/blocked_time_series.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/fallback_progress_bar.py
RENAMED
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/features_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev6 → upgini-1.2.167a4220.dev8}/src/upgini/utils/search_key_derivations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|