upgini 1.2.167a4220.dev7__tar.gz → 1.2.167a4220.dev9__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.dev7 → upgini-1.2.167a4220.dev9}/PKG-INFO +1 -1
- upgini-1.2.167a4220.dev9/src/upgini/__about__.py +1 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/features_enricher.py +84 -65
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/metadata.py +2 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/report/assemble.py +179 -40
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/report/data.py +6 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/report/html.py +33 -5
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/report/template.html +7 -4
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/search_task.py +25 -0
- upgini-1.2.167a4220.dev7/src/upgini/__about__.py +0 -1
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/.gitignore +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/LICENSE +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/README.md +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/pyproject.toml +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/ads.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/ads_management/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/ads_management/ads_manager.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/all_operators.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/binary.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/date.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/feature.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/groupby.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/operand.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/operator.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/base.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/cross.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/delta.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/lag.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/numpy_kernels.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/roll.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/trend.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/volatility.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/unary.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/vector.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/data_source/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/data_source/data_source_publisher.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/dataset.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/errors.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/http.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/mdc/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/mdc/context.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/metrics.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/normalizer/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/normalizer/normalize_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/report/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/report/stats.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/exceptions.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/strings.properties +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/strings_widget.properties +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/sampler/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/sampler/base.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/sampler/random_under_sampler.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/sampler/utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/spinner.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/Roboto-Regular.ttf +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/__init__.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/base_search_key_detector.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/blocked_time_series.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/config.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/country_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/cpu_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/custom_loss_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/cv_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/datetime_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/deduplicate_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/display_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/email_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/fallback_progress_bar.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/feature_info.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/features_validator.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/format.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/hash_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/ip_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/mstats.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/one_hot_encoder.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/phone_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/postal_code_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/progress_bar.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/psi.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/pyarrow_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/sample_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/search_key_derivations.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/sklearn_ext.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/sort.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/target_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/track_info.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/ts_utils.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/warning_counter.py +0 -0
- {upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/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.dev9
|
|
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.dev9"
|
|
@@ -64,6 +64,7 @@ from upgini.metadata import (
|
|
|
64
64
|
FeaturesMetadataV2,
|
|
65
65
|
FileColumnMeaningType,
|
|
66
66
|
FileColumnMetadata,
|
|
67
|
+
GeneratedFeatureMetadata,
|
|
67
68
|
ModelTaskType,
|
|
68
69
|
RuntimeParameters,
|
|
69
70
|
SearchKey,
|
|
@@ -98,7 +99,13 @@ from upgini.utils.deduplicate_utils import (
|
|
|
98
99
|
clean_full_duplicates,
|
|
99
100
|
remove_fintech_duplicates,
|
|
100
101
|
)
|
|
101
|
-
from upgini.report.assemble import
|
|
102
|
+
from upgini.report.assemble import (
|
|
103
|
+
_generated_feature_names,
|
|
104
|
+
_parse_generated_feature,
|
|
105
|
+
assemble_report_data,
|
|
106
|
+
autofe_rows_from_description,
|
|
107
|
+
build_search_results,
|
|
108
|
+
)
|
|
102
109
|
from upgini.report.html import generate_html_report
|
|
103
110
|
from upgini.report.stats import make_report_frame
|
|
104
111
|
from upgini.utils.display_utils import (
|
|
@@ -2288,29 +2295,31 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2288
2295
|
return self._get_single_ensemble_score_name() is not None
|
|
2289
2296
|
|
|
2290
2297
|
def _get_single_ensemble_score_name(self) -> str | None:
|
|
2298
|
+
if len(self._ensemble_generated_metadata()) != 1:
|
|
2299
|
+
return None
|
|
2291
2300
|
selected = list(self.feature_names_ or [])
|
|
2292
2301
|
ensemble = [name for name in selected if self._is_ensemble_feature(name)]
|
|
2293
2302
|
if len(ensemble) != 1:
|
|
2294
2303
|
return None
|
|
2295
|
-
|
|
2296
|
-
allowed = self._column_name_aliases([ensemble[0]], renaming) | self._etalon_feature_aliases()
|
|
2297
|
-
if any(name not in allowed for name in selected):
|
|
2304
|
+
if any(self._is_extra_enriched_feature(name) for name in selected):
|
|
2298
2305
|
return None
|
|
2299
2306
|
return ensemble[0]
|
|
2300
2307
|
|
|
2301
|
-
def
|
|
2308
|
+
def _is_extra_enriched_feature(self, name: str) -> bool:
|
|
2309
|
+
if self._is_ensemble_feature(name):
|
|
2310
|
+
return False
|
|
2302
2311
|
renaming = self.fit_columns_renaming or {}
|
|
2303
|
-
|
|
2304
|
-
if self.
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
)
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
return
|
|
2312
|
+
aliases = self._column_name_aliases([name], renaming)
|
|
2313
|
+
if aliases & set(self.external_source_feature_names or []):
|
|
2314
|
+
return True
|
|
2315
|
+
if self._search_task is None:
|
|
2316
|
+
return False
|
|
2317
|
+
for meta in self._search_task.get_all_features_metadata_v2() or []:
|
|
2318
|
+
if meta.source == "etalon" or self._is_ensemble_feature(meta.name):
|
|
2319
|
+
continue
|
|
2320
|
+
if aliases & self._column_name_aliases([meta.name], renaming):
|
|
2321
|
+
return True
|
|
2322
|
+
return False
|
|
2314
2323
|
|
|
2315
2324
|
def _reference_rows(self) -> int | None:
|
|
2316
2325
|
if self.X is None:
|
|
@@ -2472,12 +2481,21 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2472
2481
|
if self._search_task is not None:
|
|
2473
2482
|
features_meta = list(self._search_task.get_all_features_metadata_v2() or [])
|
|
2474
2483
|
features, sources, model_shap, summary = build_search_results(
|
|
2475
|
-
model_names=self._ensemble_model_feature_names(),
|
|
2476
2484
|
features_meta=features_meta,
|
|
2477
|
-
base_columns=self._ensemble_base_columns(),
|
|
2478
2485
|
is_ensemble=self._is_ensemble_feature,
|
|
2486
|
+
generated_names=self._column_name_aliases(
|
|
2487
|
+
self.fit_generated_features or [], self.fit_columns_renaming or {}
|
|
2488
|
+
),
|
|
2489
|
+
generated_features=self._search_task.get_all_generated_features() if self._search_task else [],
|
|
2490
|
+
joined_ads_features_count=(
|
|
2491
|
+
self._search_task.get_joined_ads_features_count() if self._search_task else None
|
|
2492
|
+
),
|
|
2493
|
+
joined_ads_count=self._search_task.get_joined_ads_count() if self._search_task else None,
|
|
2494
|
+
)
|
|
2495
|
+
autofe = autofe_rows_from_description(
|
|
2496
|
+
self.get_autofe_features_description(features_meta=features_meta),
|
|
2497
|
+
self.bundle,
|
|
2479
2498
|
)
|
|
2480
|
-
autofe = autofe_rows_from_description(self.get_autofe_features_description(), self.bundle)
|
|
2481
2499
|
return features, sources, model_shap, summary, autofe
|
|
2482
2500
|
|
|
2483
2501
|
def _score_report_html(self) -> str | None:
|
|
@@ -2495,15 +2513,29 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2495
2513
|
return False
|
|
2496
2514
|
return bool(aliases & self._column_name_aliases(list(ensemble_names), renaming))
|
|
2497
2515
|
|
|
2516
|
+
def _match_autofe_feature_meta(
|
|
2517
|
+
self,
|
|
2518
|
+
generated_meta: GeneratedFeatureMetadata,
|
|
2519
|
+
features_meta: list[FeaturesMetadataV2],
|
|
2520
|
+
used_names: set[str],
|
|
2521
|
+
) -> FeaturesMetadataV2 | None:
|
|
2522
|
+
available = [meta for meta in features_meta or [] if meta.name not in used_names]
|
|
2523
|
+
by_name = {meta.name: meta for meta in available}
|
|
2524
|
+
for name in _generated_feature_names(generated_meta):
|
|
2525
|
+
matched = by_name.get(name)
|
|
2526
|
+
if matched is not None:
|
|
2527
|
+
return matched
|
|
2528
|
+
return None
|
|
2529
|
+
|
|
2498
2530
|
def _ensemble_generated_metadata(self):
|
|
2499
2531
|
autofe_meta = self._search_task.get_autofe_metadata() if self._search_task else None
|
|
2500
2532
|
if not autofe_meta:
|
|
2501
2533
|
return []
|
|
2502
2534
|
matched = []
|
|
2503
2535
|
for meta in autofe_meta:
|
|
2504
|
-
|
|
2505
|
-
if
|
|
2506
|
-
matched.append((meta, op))
|
|
2536
|
+
feature = _parse_generated_feature(meta)
|
|
2537
|
+
if feature is not None and isinstance(feature.op, EnsembleModel):
|
|
2538
|
+
matched.append((meta, feature.op))
|
|
2507
2539
|
return matched
|
|
2508
2540
|
|
|
2509
2541
|
def _ensemble_feature_names_from_metadata(self) -> set[str]:
|
|
@@ -2520,25 +2552,6 @@ class FeaturesEnricher(TransformerMixin):
|
|
|
2520
2552
|
names.add(meta.alias)
|
|
2521
2553
|
return names
|
|
2522
2554
|
|
|
2523
|
-
def _ensemble_base_columns(self) -> list[tuple[str, str]]:
|
|
2524
|
-
return [
|
|
2525
|
-
(column.original_name, column.hashed_name)
|
|
2526
|
-
for meta, _ in self._ensemble_generated_metadata()
|
|
2527
|
-
for column in meta.base_columns
|
|
2528
|
-
]
|
|
2529
|
-
|
|
2530
|
-
def _ensemble_model_feature_names(self) -> list[str]:
|
|
2531
|
-
return sorted(
|
|
2532
|
-
{
|
|
2533
|
-
original
|
|
2534
|
-
for original, hashed in self._ensemble_base_columns()
|
|
2535
|
-
if not self._is_ensemble_feature(original) and not self._is_ensemble_feature(hashed)
|
|
2536
|
-
}
|
|
2537
|
-
)
|
|
2538
|
-
|
|
2539
|
-
def _ensemble_model_feature_count(self) -> int | None:
|
|
2540
|
-
return len(self._ensemble_model_feature_names()) or None
|
|
2541
|
-
|
|
2542
2555
|
def _psi_exceeds_threshold(self, psi: float | None, threshold: float) -> bool:
|
|
2543
2556
|
return psi is not None and not pd.isna(psi) and psi > threshold
|
|
2544
2557
|
|
|
@@ -5875,6 +5888,7 @@ if response.status_code == 200:
|
|
|
5875
5888
|
|
|
5876
5889
|
selected_features_meta.sort(key=lambda m: (-m.shap_value, m.name))
|
|
5877
5890
|
psi_keepers = self._psi_stability_keepers() if self.psi_values is not None else set()
|
|
5891
|
+
single_ensemble = len(self._ensemble_generated_metadata()) == 1
|
|
5878
5892
|
|
|
5879
5893
|
for feature_meta in selected_features_meta:
|
|
5880
5894
|
original_name = original_names_dict.get(feature_meta.name, feature_meta.name)
|
|
@@ -5884,6 +5898,10 @@ if response.status_code == 200:
|
|
|
5884
5898
|
) or original_name in generated_aliases
|
|
5885
5899
|
is_client_feature = original_name in clients_features_df.columns and not is_generated_feature
|
|
5886
5900
|
|
|
5901
|
+
# Model-input ads/AutoFE rows are only for the HTML report; the selected output is the score.
|
|
5902
|
+
if single_ensemble and not is_client_feature and not self._is_ensemble_feature(feature_meta.name):
|
|
5903
|
+
continue
|
|
5904
|
+
|
|
5887
5905
|
if not is_client_feature and not is_generated_feature:
|
|
5888
5906
|
self.external_source_feature_names.append(original_name)
|
|
5889
5907
|
|
|
@@ -5898,8 +5916,6 @@ if response.status_code == 200:
|
|
|
5898
5916
|
if original_name in self.unstable_features or feature_meta.name in self.unstable_features:
|
|
5899
5917
|
continue
|
|
5900
5918
|
|
|
5901
|
-
# TODO make a decision about selected features based on special flag from mlb
|
|
5902
|
-
|
|
5903
5919
|
if original_shaps.get(feature_meta.name, 0.0) == 0.0 and not self._should_show_client_feature_in_report(
|
|
5904
5920
|
is_client_feature, feature_meta.name
|
|
5905
5921
|
):
|
|
@@ -5950,12 +5966,12 @@ if response.status_code == 200:
|
|
|
5950
5966
|
else:
|
|
5951
5967
|
self.logger.warning("Empty features info")
|
|
5952
5968
|
|
|
5953
|
-
def get_autofe_features_description(self):
|
|
5969
|
+
def get_autofe_features_description(self, features_meta: list[FeaturesMetadataV2] | None = None):
|
|
5954
5970
|
try:
|
|
5955
5971
|
autofe_meta = self._search_task.get_autofe_metadata()
|
|
5956
5972
|
if autofe_meta is None:
|
|
5957
5973
|
return None
|
|
5958
|
-
if len(self._internal_features_info) != 0:
|
|
5974
|
+
if features_meta is None and len(self._internal_features_info) != 0:
|
|
5959
5975
|
|
|
5960
5976
|
def to_feature_meta(row):
|
|
5961
5977
|
fm = FeaturesMetadataV2(
|
|
@@ -5969,38 +5985,40 @@ if response.status_code == 200:
|
|
|
5969
5985
|
return fm
|
|
5970
5986
|
|
|
5971
5987
|
features_meta = self._internal_features_info.apply(to_feature_meta, axis=1).to_list()
|
|
5972
|
-
|
|
5988
|
+
elif features_meta is None:
|
|
5973
5989
|
features_meta = self._search_task.get_all_features_metadata_v2()
|
|
5974
5990
|
|
|
5975
|
-
def get_feature_by_name(name: str):
|
|
5976
|
-
for m in features_meta:
|
|
5977
|
-
if m.name == name:
|
|
5978
|
-
return m
|
|
5979
|
-
|
|
5980
5991
|
descriptions = []
|
|
5992
|
+
used_names: set[str] = set()
|
|
5981
5993
|
for m in autofe_meta:
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5994
|
+
try:
|
|
5995
|
+
orig_to_hashed = {
|
|
5996
|
+
base_column.original_name: base_column.hashed_name for base_column in m.base_columns
|
|
5997
|
+
}
|
|
5998
|
+
autofe_feature = (
|
|
5999
|
+
Feature.from_formula(m.formula)
|
|
6000
|
+
.set_display_index(m.display_index or None)
|
|
6001
|
+
.set_alias(m.alias)
|
|
6002
|
+
.set_op_params(m.operator_params or {})
|
|
6003
|
+
.rename_columns(orig_to_hashed)
|
|
6004
|
+
)
|
|
6005
|
+
except Exception:
|
|
6006
|
+
self.logger.exception(f"Failed to parse AutoFE formula: {m.formula}")
|
|
6007
|
+
continue
|
|
5991
6008
|
|
|
5992
6009
|
is_ts = isinstance(autofe_feature.op, TimeSeriesBase)
|
|
5993
6010
|
|
|
5994
6011
|
if autofe_feature.op.is_vector and not is_ts:
|
|
5995
6012
|
continue
|
|
5996
6013
|
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
feature_meta = get_feature_by_name(autofe_feature.get_display_name(shorten=True, unhash=True))
|
|
6000
|
-
if feature_meta is None:
|
|
6014
|
+
feature_meta = self._match_autofe_feature_meta(m, features_meta, used_names)
|
|
6015
|
+
if feature_meta is None or not feature_meta.shap_value:
|
|
6001
6016
|
continue
|
|
6017
|
+
|
|
6018
|
+
description = {}
|
|
6002
6019
|
description["shap"] = feature_meta.shap_value
|
|
6003
|
-
|
|
6020
|
+
source = feature_meta.data_source or ""
|
|
6021
|
+
description[self.bundle.get("autofe_descriptions_sources")] = source.replace(
|
|
6004
6022
|
"AutoFE: features from ", ""
|
|
6005
6023
|
).replace("AutoFE: feature from ", "")
|
|
6006
6024
|
description[self.bundle.get("autofe_descriptions_feature_name")] = feature_meta.name
|
|
@@ -6019,6 +6037,7 @@ if response.status_code == 200:
|
|
|
6019
6037
|
sorted(autofe_feature.get_all_operand_names())
|
|
6020
6038
|
)
|
|
6021
6039
|
|
|
6040
|
+
used_names.add(feature_meta.name)
|
|
6022
6041
|
descriptions.append(description)
|
|
6023
6042
|
|
|
6024
6043
|
if len(descriptions) == 0:
|
|
@@ -346,6 +346,8 @@ class ProviderTaskMetadataV2(BaseModel):
|
|
|
346
346
|
zero_hit_rate_search_keys: Optional[List[str]] = None
|
|
347
347
|
features_used_for_embeddings: Optional[List[str]] = None
|
|
348
348
|
shuffle_kfold: Optional[bool] = None
|
|
349
|
+
joined_ads_features_count: Optional[int] = None
|
|
350
|
+
joined_ads_count: Optional[int] = None
|
|
349
351
|
generated_features: Optional[List[GeneratedFeatureMetadata]] = None
|
|
350
352
|
|
|
351
353
|
|
|
@@ -7,7 +7,9 @@ from typing import Callable, Optional
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
|
|
9
9
|
from upgini.__about__ import __version__
|
|
10
|
-
from upgini.
|
|
10
|
+
from upgini.autofe.feature import Feature
|
|
11
|
+
from upgini.autofe.vector import CatboostModel, EnsembleModel, OnnxModel
|
|
12
|
+
from upgini.metadata import BaseColumnMetadata, FeaturesMetadataV2, GeneratedFeatureMetadata
|
|
11
13
|
from upgini.report.data import (
|
|
12
14
|
FeatureRow,
|
|
13
15
|
ModelFeatureShap,
|
|
@@ -98,13 +100,23 @@ def assemble_report_data(
|
|
|
98
100
|
|
|
99
101
|
def build_search_results(
|
|
100
102
|
*,
|
|
101
|
-
model_names: list[str],
|
|
102
103
|
features_meta: list[FeaturesMetadataV2],
|
|
103
|
-
base_columns: list[tuple[str, str]],
|
|
104
104
|
is_ensemble: Callable[[str], bool],
|
|
105
|
+
generated_names: Optional[set[str]] = None,
|
|
106
|
+
generated_features: Optional[list[GeneratedFeatureMetadata]] = None,
|
|
107
|
+
joined_ads_features_count: Optional[int] = None,
|
|
108
|
+
joined_ads_count: Optional[int] = None,
|
|
105
109
|
) -> tuple[list[FeatureRow], list[SourceRow], list[ModelFeatureShap], SearchResultsSummary]:
|
|
106
|
-
|
|
107
|
-
model_rows = [
|
|
110
|
+
generated_names = generated_names or set()
|
|
111
|
+
model_rows: list[FeatureRow] = []
|
|
112
|
+
found_rows: list[FeatureRow] = []
|
|
113
|
+
for meta in features_meta:
|
|
114
|
+
if is_ensemble(meta.name) or not _has_nonzero_shap_value(meta.shap_value):
|
|
115
|
+
continue
|
|
116
|
+
row = _feature_row(meta.name, meta, generated_names)
|
|
117
|
+
model_rows.append(row)
|
|
118
|
+
if meta.source != "etalon":
|
|
119
|
+
found_rows.append(row)
|
|
108
120
|
model_rows.sort(key=lambda row: (row.shap is None, -abs(row.shap or 0.0), row.name))
|
|
109
121
|
model_shap = [
|
|
110
122
|
ModelFeatureShap(
|
|
@@ -115,23 +127,16 @@ def build_search_results(
|
|
|
115
127
|
)
|
|
116
128
|
for index, row in enumerate(model_rows, start=1)
|
|
117
129
|
]
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
summary = SearchResultsSummary(
|
|
129
|
-
relevant_features=relevant,
|
|
130
|
-
model_features=model_count,
|
|
131
|
-
data_sources=len(sources),
|
|
132
|
-
stable_features_share=(stable / len(model_rows)) if model_rows else None,
|
|
133
|
-
contributed_sources=contributed,
|
|
134
|
-
stable_features=stable if model_rows else None,
|
|
130
|
+
sources = _source_rows(found_rows)
|
|
131
|
+
summary = _overview_summary(
|
|
132
|
+
features_meta=features_meta,
|
|
133
|
+
model_rows=model_rows,
|
|
134
|
+
found_rows=found_rows,
|
|
135
|
+
sources=sources,
|
|
136
|
+
is_ensemble=is_ensemble,
|
|
137
|
+
generated_features=generated_features or [],
|
|
138
|
+
joined_ads_features_count=joined_ads_features_count,
|
|
139
|
+
joined_ads_count=joined_ads_count,
|
|
135
140
|
)
|
|
136
141
|
return model_rows, sources, model_shap, summary
|
|
137
142
|
|
|
@@ -234,23 +239,15 @@ def _as_int(value) -> Optional[int]:
|
|
|
234
239
|
return None if pd.isna(value) else int(value)
|
|
235
240
|
|
|
236
241
|
|
|
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:
|
|
242
|
+
def _feature_row(
|
|
243
|
+
name: str, meta: Optional[FeaturesMetadataV2], generated_names: Optional[set[str]] = None
|
|
244
|
+
) -> FeatureRow:
|
|
251
245
|
if meta is None:
|
|
252
246
|
return FeatureRow(name=name)
|
|
253
|
-
|
|
247
|
+
generated_names = generated_names or set()
|
|
248
|
+
is_generated = name in generated_names or meta.source == "generated"
|
|
249
|
+
is_client = meta.source == "etalon" and not is_generated
|
|
250
|
+
info = FeatureInfo.from_metadata(meta, None, is_client, is_generated)
|
|
254
251
|
return FeatureRow(
|
|
255
252
|
name=name,
|
|
256
253
|
shap=meta.shap_value,
|
|
@@ -258,11 +255,20 @@ def _feature_row(name: str, meta: Optional[FeaturesMetadataV2]) -> FeatureRow:
|
|
|
258
255
|
drift=meta.drift_score,
|
|
259
256
|
coverage=meta.hit_rate,
|
|
260
257
|
provider=info.internal_provider,
|
|
261
|
-
source=info.internal_source,
|
|
258
|
+
source=_feature_source(meta, is_generated, info.internal_source),
|
|
262
259
|
stability_status=_psi_status(meta.psi_value),
|
|
263
260
|
)
|
|
264
261
|
|
|
265
262
|
|
|
263
|
+
def _feature_source(meta: FeaturesMetadataV2, is_generated: bool, fallback: str) -> str:
|
|
264
|
+
if is_generated:
|
|
265
|
+
if meta.data_source:
|
|
266
|
+
return meta.data_source
|
|
267
|
+
if meta.data_sources:
|
|
268
|
+
return ", ".join(meta.data_sources)
|
|
269
|
+
return fallback
|
|
270
|
+
|
|
271
|
+
|
|
266
272
|
def _source_rows(rows: list[FeatureRow]) -> list[SourceRow]:
|
|
267
273
|
grouped: dict[tuple[str, str], list[FeatureRow]] = {}
|
|
268
274
|
for row in rows:
|
|
@@ -292,5 +298,138 @@ def _psi_status(psi: Optional[float]) -> Optional[str]:
|
|
|
292
298
|
return "good"
|
|
293
299
|
|
|
294
300
|
|
|
295
|
-
def
|
|
296
|
-
return
|
|
301
|
+
def _has_nonzero_shap_value(shap: Optional[float]) -> bool:
|
|
302
|
+
return shap is not None and not pd.isna(shap) and shap != 0
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _overview_summary(
|
|
306
|
+
*,
|
|
307
|
+
features_meta: list[FeaturesMetadataV2],
|
|
308
|
+
model_rows: list[FeatureRow],
|
|
309
|
+
found_rows: list[FeatureRow],
|
|
310
|
+
sources: list[SourceRow],
|
|
311
|
+
is_ensemble: Callable[[str], bool],
|
|
312
|
+
generated_features: list[GeneratedFeatureMetadata],
|
|
313
|
+
joined_ads_features_count: Optional[int],
|
|
314
|
+
joined_ads_count: Optional[int],
|
|
315
|
+
) -> SearchResultsSummary:
|
|
316
|
+
top_level = _unique_fold_model_columns(generated_features)
|
|
317
|
+
features_by_name = {meta.name: meta for meta in features_meta}
|
|
318
|
+
autofe_by_name = _autofe_generated_by_name(generated_features, is_ensemble)
|
|
319
|
+
if top_level:
|
|
320
|
+
counts = {"external": 0, "original": 0, "autofe": 0}
|
|
321
|
+
ads_ids: set[str] = set()
|
|
322
|
+
psis: list[Optional[float]] = []
|
|
323
|
+
for column in top_level:
|
|
324
|
+
kind = _top_level_kind(column, features_by_name, autofe_by_name, is_ensemble)
|
|
325
|
+
counts[kind] += 1
|
|
326
|
+
ads_ids.update(_leaf_ads_ids(column, autofe_by_name))
|
|
327
|
+
meta = features_by_name.get(column.hashed_name) or features_by_name.get(column.original_name)
|
|
328
|
+
psis.append(None if meta is None else meta.psi_value)
|
|
329
|
+
model_features = len(top_level)
|
|
330
|
+
external, original, autofe = counts["external"], counts["original"], counts["autofe"]
|
|
331
|
+
contributed = len(ads_ids)
|
|
332
|
+
else:
|
|
333
|
+
model_features = len(model_rows) or None
|
|
334
|
+
external = original = autofe = None
|
|
335
|
+
contributed = len({(row.provider, row.source) for row in found_rows if row.provider})
|
|
336
|
+
psis = [row.psi for row in model_rows]
|
|
337
|
+
share, stable, psi_count = _stability_from_psi(psis)
|
|
338
|
+
return SearchResultsSummary(
|
|
339
|
+
relevant_features=len(found_rows),
|
|
340
|
+
features_found=joined_ads_features_count if joined_ads_features_count is not None else len(found_rows),
|
|
341
|
+
model_features=model_features,
|
|
342
|
+
external_features=external,
|
|
343
|
+
original_features=original,
|
|
344
|
+
autofe_features=autofe,
|
|
345
|
+
data_sources=len(sources),
|
|
346
|
+
joined_sources=joined_ads_count if joined_ads_count is not None else len(sources),
|
|
347
|
+
stable_features_share=share,
|
|
348
|
+
contributed_sources=contributed,
|
|
349
|
+
stable_features=stable,
|
|
350
|
+
psi_features=psi_count,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _unique_fold_model_columns(generated_features: list[GeneratedFeatureMetadata]) -> list[BaseColumnMetadata]:
|
|
355
|
+
unique: dict[str, BaseColumnMetadata] = {}
|
|
356
|
+
for meta in generated_features:
|
|
357
|
+
feature = _parse_generated_feature(meta)
|
|
358
|
+
if feature is None or not isinstance(feature.op, (CatboostModel, OnnxModel)):
|
|
359
|
+
continue
|
|
360
|
+
for column in meta.base_columns:
|
|
361
|
+
unique.setdefault(column.hashed_name or column.original_name, column)
|
|
362
|
+
return list(unique.values())
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _autofe_generated_by_name(
|
|
366
|
+
generated_features: list[GeneratedFeatureMetadata], is_ensemble: Callable[[str], bool]
|
|
367
|
+
) -> dict[str, GeneratedFeatureMetadata]:
|
|
368
|
+
by_name: dict[str, GeneratedFeatureMetadata] = {}
|
|
369
|
+
for meta in generated_features:
|
|
370
|
+
feature = _parse_generated_feature(meta)
|
|
371
|
+
if feature is None or isinstance(feature.op, (CatboostModel, EnsembleModel, OnnxModel)):
|
|
372
|
+
continue
|
|
373
|
+
name = feature.get_display_name(shorten=True, unhash=True, cache=False)
|
|
374
|
+
if name and not is_ensemble(name):
|
|
375
|
+
by_name.setdefault(name, meta)
|
|
376
|
+
return by_name
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _parse_generated_feature(meta: GeneratedFeatureMetadata) -> Feature | None:
|
|
380
|
+
try:
|
|
381
|
+
parsed = Feature.from_formula(meta.formula)
|
|
382
|
+
if not isinstance(parsed, Feature):
|
|
383
|
+
return None
|
|
384
|
+
orig_to_hashed = {column.original_name: column.hashed_name for column in meta.base_columns}
|
|
385
|
+
return (
|
|
386
|
+
parsed.set_display_index(meta.display_index or None)
|
|
387
|
+
.set_alias(meta.alias)
|
|
388
|
+
.set_op_params(meta.operator_params or {})
|
|
389
|
+
.rename_columns(orig_to_hashed)
|
|
390
|
+
)
|
|
391
|
+
except Exception:
|
|
392
|
+
return None
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _generated_feature_names(meta: GeneratedFeatureMetadata) -> set[str]:
|
|
396
|
+
feature = _parse_generated_feature(meta)
|
|
397
|
+
if feature is None:
|
|
398
|
+
return set()
|
|
399
|
+
return {feature.get_display_name(shorten=True, unhash=True, cache=False)}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _top_level_kind(
|
|
403
|
+
column: BaseColumnMetadata,
|
|
404
|
+
features_by_name: dict[str, FeaturesMetadataV2],
|
|
405
|
+
autofe_by_name: dict[str, GeneratedFeatureMetadata],
|
|
406
|
+
is_ensemble: Callable[[str], bool],
|
|
407
|
+
) -> str:
|
|
408
|
+
names = [column.hashed_name, column.original_name]
|
|
409
|
+
if any(name in autofe_by_name for name in names if name):
|
|
410
|
+
return "autofe"
|
|
411
|
+
meta = next((features_by_name[name] for name in names if name in features_by_name), None)
|
|
412
|
+
if meta is not None and not is_ensemble(meta.name):
|
|
413
|
+
if meta.source == "generated":
|
|
414
|
+
return "autofe"
|
|
415
|
+
if meta.source == "ads":
|
|
416
|
+
return "external"
|
|
417
|
+
if meta.source == "etalon":
|
|
418
|
+
return "original"
|
|
419
|
+
if column.ads_definition_id:
|
|
420
|
+
return "external"
|
|
421
|
+
return "original"
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _leaf_ads_ids(column: BaseColumnMetadata, autofe_by_name: dict[str, GeneratedFeatureMetadata]) -> set[str]:
|
|
425
|
+
autofe = autofe_by_name.get(column.hashed_name) or autofe_by_name.get(column.original_name)
|
|
426
|
+
leaves = autofe.base_columns if autofe is not None else [column]
|
|
427
|
+
return {leaf.ads_definition_id for leaf in leaves if leaf.ads_definition_id}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _stability_from_psi(psis: list[Optional[float]]) -> tuple[Optional[float], Optional[int], Optional[int]]:
|
|
431
|
+
values = [psi for psi in psis if psi is not None and not pd.isna(psi)]
|
|
432
|
+
if not values:
|
|
433
|
+
return None, None, None
|
|
434
|
+
stable = sum(1 for psi in values if psi < _STABILITY_PSI)
|
|
435
|
+
return stable / len(values), stable, len(values)
|
|
@@ -66,11 +66,17 @@ class SourceRow:
|
|
|
66
66
|
@dataclass
|
|
67
67
|
class SearchResultsSummary:
|
|
68
68
|
relevant_features: Optional[int] = None
|
|
69
|
+
features_found: Optional[int] = None
|
|
69
70
|
model_features: Optional[int] = None
|
|
71
|
+
external_features: Optional[int] = None
|
|
72
|
+
original_features: Optional[int] = None
|
|
73
|
+
autofe_features: Optional[int] = None
|
|
70
74
|
data_sources: Optional[int] = None
|
|
75
|
+
joined_sources: Optional[int] = None
|
|
71
76
|
stable_features_share: Optional[float] = None
|
|
72
77
|
contributed_sources: Optional[int] = None
|
|
73
78
|
stable_features: Optional[int] = None
|
|
79
|
+
psi_features: Optional[int] = None
|
|
74
80
|
|
|
75
81
|
|
|
76
82
|
@dataclass
|
|
@@ -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")
|
|
@@ -144,22 +145,37 @@ def _summary_cards(summary: SearchResultsSummary) -> list[dict]:
|
|
|
144
145
|
if summary.contributed_sources is not None:
|
|
145
146
|
sources_caption = f"{summary.contributed_sources} contributed"
|
|
146
147
|
stability_caption = ""
|
|
147
|
-
if summary.stable_features is not None and summary.
|
|
148
|
-
stability_caption = f"{summary.stable_features} of {summary.
|
|
148
|
+
if summary.stable_features is not None and summary.psi_features is not None:
|
|
149
|
+
stability_caption = f"{summary.stable_features} of {summary.psi_features} are stable"
|
|
150
|
+
features_found = summary.features_found if summary.features_found is not None else summary.relevant_features
|
|
151
|
+
data_sources = summary.joined_sources if summary.joined_sources is not None else summary.data_sources
|
|
149
152
|
return [
|
|
150
|
-
{"label": "Features found", "value": _dash(
|
|
151
|
-
{"label": "Used in model", "value": _dash(summary.model_features), "caption":
|
|
152
|
-
{"label": "Data sources", "value": _dash(
|
|
153
|
+
{"label": "Features found", "value": _dash(features_found), "caption": ""},
|
|
154
|
+
{"label": "Used in model", "value": _dash(summary.model_features), "caption": _used_in_model_caption(summary)},
|
|
155
|
+
{"label": "Data sources", "value": _dash(data_sources), "caption": sources_caption},
|
|
153
156
|
{"label": "Stability", "value": _dash(stability), "caption": stability_caption},
|
|
154
157
|
]
|
|
155
158
|
|
|
156
159
|
|
|
160
|
+
def _used_in_model_caption(summary: SearchResultsSummary) -> str:
|
|
161
|
+
if summary.external_features is None and summary.original_features is None and summary.autofe_features is None:
|
|
162
|
+
return ""
|
|
163
|
+
return " · ".join(
|
|
164
|
+
[
|
|
165
|
+
f"{summary.external_features or 0} external",
|
|
166
|
+
f"{summary.original_features or 0} original",
|
|
167
|
+
f"{summary.autofe_features or 0} AutoFE",
|
|
168
|
+
]
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
157
172
|
def _features_payload(features: list[FeatureRow]) -> list[dict]:
|
|
158
173
|
return [
|
|
159
174
|
{
|
|
160
175
|
"name": row.name,
|
|
161
176
|
"provider": row.provider,
|
|
162
177
|
"source": row.source,
|
|
178
|
+
"shapClass": _shap_fill_class(row),
|
|
163
179
|
"importance": abs(row.shap) if row.shap is not None else 0,
|
|
164
180
|
"shap": row.shap,
|
|
165
181
|
"coverage": row.coverage,
|
|
@@ -171,6 +187,18 @@ def _features_payload(features: list[FeatureRow]) -> list[dict]:
|
|
|
171
187
|
]
|
|
172
188
|
|
|
173
189
|
|
|
190
|
+
def _shap_fill_class(row: FeatureRow) -> str:
|
|
191
|
+
source = (row.source or "").lower()
|
|
192
|
+
provider = (row.provider or "").lower()
|
|
193
|
+
if source.startswith("autofe") or provider == "autofe" or source == GENERATED_SOURCE.lower():
|
|
194
|
+
return "autofe"
|
|
195
|
+
if source == CLIENT_SOURCE.lower() or provider == "user":
|
|
196
|
+
return "user"
|
|
197
|
+
if provider in {"", "upgini"}:
|
|
198
|
+
return "upgini"
|
|
199
|
+
return "external"
|
|
200
|
+
|
|
201
|
+
|
|
174
202
|
def _sources_payload(sources: list[SourceRow]) -> list[dict]:
|
|
175
203
|
return [
|
|
176
204
|
{
|
|
@@ -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
|
|
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{display:inline-block;font-size:9px;margin-left:6px;padding:3px 7px;border-radius:8px;border:1px solid var(--line);color:var(--muted);line-height:1.3;text-align:center;vertical-align:middle;white-space:normal}.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}}
|
|
@@ -127,6 +127,9 @@ const REPORT_DATA = __REPORT_DATA__;
|
|
|
127
127
|
const pct1 = v => (v == null ? '—' : Number(v).toFixed(1) + '%');
|
|
128
128
|
const signed3 = v => (v >= 0 ? '+' : '') + Number(v).toFixed(3);
|
|
129
129
|
const setText = (id, text) => { const el = document.getElementById(id); if (el) el.textContent = text; };
|
|
130
|
+
const providerBadge = provider => provider
|
|
131
|
+
? `<span class="provider">${String(provider).replace(/,\s*/g, '<br>')}</span>`
|
|
132
|
+
: '';
|
|
130
133
|
function rgba(hex, alpha) {
|
|
131
134
|
const h = hex.replace('#', '');
|
|
132
135
|
const n = parseInt(h.length === 3 ? h.split('').map(x => x + x).join('') : h, 16);
|
|
@@ -507,8 +510,8 @@ const REPORT_DATA = __REPORT_DATA__;
|
|
|
507
510
|
const maxImportance = Math.max(...features.map(f => f.importance)) || 1;
|
|
508
511
|
const list = shapAll ? features : features.slice(0, (D.shap && D.shap.topN) || features.length);
|
|
509
512
|
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.
|
|
513
|
+
`<div class="shap-row"><div><b>#${i + 1} ${f.name}</b>${providerBadge(f.provider)}</div>` +
|
|
514
|
+
`<div class="shap-track"><i class="shap-fill ${f.shapClass || 'upgini'}" style="width:${(f.importance / maxImportance * 100).toFixed(2)}%"></i></div>` +
|
|
512
515
|
`<b>${fmt3(Math.abs(f.shap))}</b></div>`).join('');
|
|
513
516
|
document.getElementById('showAllShap').textContent = shapAll ? `Show top ${(D.shap && D.shap.topN) || features.length}` : `Show all ${features.length}`;
|
|
514
517
|
}
|
|
@@ -534,7 +537,7 @@ const REPORT_DATA = __REPORT_DATA__;
|
|
|
534
537
|
const body = document.getElementById('featureBody');
|
|
535
538
|
body.innerHTML = rows.map(f =>
|
|
536
539
|
`<tr data-name="${f.name}"><td><span class="feature-name" title="${f.name}">${f.name}</span></td>` +
|
|
537
|
-
`<td
|
|
540
|
+
`<td>${providerBadge(f.provider)}</td><td class="source-name">${f.source}</td>` +
|
|
538
541
|
`<td>${shapLabel(f.shap)}</td><td><span class="tag ${f.status}">${fmt2(f.psi)}</span></td>` +
|
|
539
542
|
`<td>${f.drift == null ? '—' : fmt2(f.drift)}</td><td>${pct1(f.coverage)}</td></tr>`).join('');
|
|
540
543
|
document.getElementById('emptyState').style.display = rows.length ? 'none' : 'block';
|
|
@@ -210,6 +210,31 @@ class SearchTask:
|
|
|
210
210
|
if meta.generated_features is not None:
|
|
211
211
|
return meta.generated_features
|
|
212
212
|
|
|
213
|
+
def get_all_generated_features(self) -> List[GeneratedFeatureMetadata]:
|
|
214
|
+
if self.provider_metadata_v2 is None:
|
|
215
|
+
return []
|
|
216
|
+
generated: List[GeneratedFeatureMetadata] = []
|
|
217
|
+
for meta in self.provider_metadata_v2:
|
|
218
|
+
if meta.generated_features:
|
|
219
|
+
generated.extend(meta.generated_features)
|
|
220
|
+
return generated
|
|
221
|
+
|
|
222
|
+
def get_joined_ads_features_count(self) -> Optional[int]:
|
|
223
|
+
if self.provider_metadata_v2 is None:
|
|
224
|
+
return None
|
|
225
|
+
for meta in self.provider_metadata_v2:
|
|
226
|
+
if meta.joined_ads_features_count is not None:
|
|
227
|
+
return meta.joined_ads_features_count
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
def get_joined_ads_count(self) -> Optional[int]:
|
|
231
|
+
if self.provider_metadata_v2 is None:
|
|
232
|
+
return None
|
|
233
|
+
for meta in self.provider_metadata_v2:
|
|
234
|
+
if meta.joined_ads_count is not None:
|
|
235
|
+
return meta.joined_ads_count
|
|
236
|
+
return None
|
|
237
|
+
|
|
213
238
|
@staticmethod
|
|
214
239
|
def _get_provider_summaries(summary: SearchTaskSummary) -> List[ProviderTaskSummary]:
|
|
215
240
|
if summary.status in {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.167.a4220.dev7"
|
|
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.dev7 → upgini-1.2.167a4220.dev9}/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.dev7 → upgini-1.2.167a4220.dev9}/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.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/autofe/timeseries/numpy_kernels.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/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
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/normalizer/normalize_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/__init__.py
RENAMED
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/exceptions.py
RENAMED
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/resource_bundle/strings.properties
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/sampler/random_under_sampler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/base_search_key_detector.py
RENAMED
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/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.dev7 → upgini-1.2.167a4220.dev9}/src/upgini/utils/fallback_progress_bar.py
RENAMED
|
File without changes
|
|
File without changes
|
{upgini-1.2.167a4220.dev7 → upgini-1.2.167a4220.dev9}/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.dev7 → upgini-1.2.167a4220.dev9}/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
|