churnkit 0.75.1a1__tar.gz → 0.76.0a1__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.
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/PKG-INFO +5 -2
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/README.md +4 -1
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/pyproject.toml +1 -1
- churnkit-0.76.0a1/scripts/release.sh +47 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/__init__.py +11 -1
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/notebook_progress.py +4 -2
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/chart_builder.py +6 -7
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/__init__.py +53 -0
- churnkit-0.76.0a1/src/customer_retention/core/config/__init__.py +74 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/experiments.py +20 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s05_feature_engineering.py +2 -1
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/renderer.py +7 -5
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/mlflow_pipeline_generator.py +223 -149
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/factory.py +8 -5
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/base.py +1 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/databricks.py +58 -10
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/base.py +8 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/databricks.py +15 -2
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/local.py +7 -0
- churnkit-0.76.0a1/src/customer_retention/integrations/databricks_init.py +141 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/temporal_features.py +12 -12
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/column_profiler.py +2 -2
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/pattern_analysis_config.py +4 -3
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_feature_analyzer.py +5 -5
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_feature_engineer.py +8 -8
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_pattern_analyzer.py +28 -10
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_quality_checks.py +9 -4
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/time_series_profiler.py +11 -10
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/time_window_aggregator.py +7 -4
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/datetime_transformer.py +10 -2
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/timeseries_detector.py +4 -1
- churnkit-0.75.1a1/src/customer_retention/core/config/__init__.py +0 -39
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/.gitignore +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/LICENSE +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/constraints/.gitkeep +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/00_start_here.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01_data_discovery.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01a_a_temporal_text_deep_dive.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01a_temporal_deep_dive.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01b_temporal_quality.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01c_temporal_patterns.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01d_event_aggregation.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/02_column_deep_dive.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/02a_text_columns_deep_dive.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/03_quality_assessment.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/04_relationship_analysis.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/05_multi_dataset.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/06_feature_opportunities.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/07_modeling_readiness.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/08_baseline_experiments.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/09_business_alignment.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/10_spec_generation.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/11_scoring_validation.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/12_view_documentation.ipynb +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/README.md +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/create_snapshot.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/generate_retail_dataset.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/generate_test_data.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/migrate_parquet_to_delta.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/migrate_to_temporal.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/build_wheel.sh +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/capture_runtime.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/dbr_init.sh +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/generate_constraints.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/notebook_setup.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/clean_notebook_outputs.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/export_tutorial_html.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/init_project.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/plotly_image_preprocessor.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/run_exploration.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/test_notebooks.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/templates/tutorial_html/conf.json +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/templates/tutorial_html/index.html.j2 +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/update_notebook_paths.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/exploration_manager.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/explorer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/findings.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/layered_recommendations.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/recommendation_builder.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/recommendations.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/ab_test_designer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/fairness_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/intervention_matcher.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/report_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/risk_profile.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/roi_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/calibration_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/cv_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/error_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/leakage_detector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/noise_tester.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/overfitting_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/segment_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/config_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/discovery_flow.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/type_inferencer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/cohort_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/counterfactual.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/individual_explainer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/pdp_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/shap_explainer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/jupyter_save_hook.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/notebook_html_exporter.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/plotly_preprocessor.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/base.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/consistency.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/deduplicate.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/impute.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/outlier.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/datetime/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/datetime/extract.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/encoding/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/encoding/categorical.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/pipeline.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/selection/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/selection/drop_column.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/transform/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/transform/power.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/transform/scale.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/console.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/display.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/number_formatter.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/artifacts/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/artifacts/fit_artifact_registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/cli.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/detection.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/ops.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/pandas_backend.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/spark_backend.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/base.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/deployer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/explainer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/feature_eng.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/ingester.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/profiler.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/trainer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/transformer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/validator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/enums.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/orchestrator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/column_config.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/pipeline_config.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/source_config.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/leakage.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/severity.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/statistics.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/base.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/cell_builder.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/config.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/databricks_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/local_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/project_init.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/runner.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/script_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/base_stage.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s01_ingestion.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s02_profiling.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s03_cleaning.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s04_transformation.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s06_feature_selection.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s07_model_training.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s08_deployment.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s09_monitoring.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s10_batch_inference.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s11_feature_store.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/code_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/context.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/data_materializer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/databricks_exporter.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/doc_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/findings_parser.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/models.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/databricks_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/generic_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/pipeline_spec.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/base.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/feast_adapter.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/local.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/experiment_tracker.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/base.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/databricks.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/local.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/definitions.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/manager.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/context.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/feedback_collector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/orchestrator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/recommendation_tracker.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/signals.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/llm_context/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/llm_context/context_builder.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/llm_context/prompts.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/batch_integration.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/early_warning_model.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/event_schema.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/online_store_writer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/realtime_scorer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/trigger_engine.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/window_aggregator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/base.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/missing_handler.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/outlier_handler.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/batch_scorer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/champion_challenger.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/model_registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/retraining_trigger.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/behavioral_features.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/customer_segmentation.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_definitions.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_engineer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_manifest.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_selector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/interaction_features.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/load_result.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/loaders.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/source_registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/baseline_trainer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/cross_validator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/data_splitter.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/feature_scaler.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/hyperparameter_tuner.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/imbalance_handler.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/mlflow_logger.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/model_comparator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/model_evaluator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/threshold_optimizer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/alert_manager.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/drift_detector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/performance_monitor.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/preprocessing/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/preprocessing/transformer_manager.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/categorical_distribution.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/categorical_target_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/distribution_analysis.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/drift_detector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/feature_capacity.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/profile_result.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/quality_checks.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/relationship_detector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/relationship_recommender.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/report_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/scd_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/segment_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/segment_aware_outlier.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/target_level_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_coverage.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_target_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/text_embedder.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/text_processor.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/text_reducer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/type_detector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/window_recommendation.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/access_guard.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/cutoff_analyzer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/data_preparer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/point_in_time_join.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/point_in_time_registry.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/scenario_detector.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/snapshot_manager.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/synthetic_coordinator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/timestamp_discovery.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/timestamp_manager.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/binary_handler.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/categorical_encoder.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/numeric_transformer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/pipeline.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/adversarial_scoring_validator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/business_sense_gate.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/data_quality_gate.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/data_validators.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/feature_quality_gate.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/gates.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/leakage_gate.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/model_validity_gate.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/pipeline_validation_runner.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/quality_scorer.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/rule_generator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/scoring_pipeline_validator.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/__init__.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/artifact_store.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/executor.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/fitted.py +0 -0
- {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/ops.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: churnkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.76.0a1
|
|
4
4
|
Summary: Structured ML framework for customer churn prediction -- from exploration notebooks to production pipelines, locally or on Databricks.
|
|
5
5
|
Project-URL: Homepage, https://github.com/aladjov/CR
|
|
6
6
|
Project-URL: Documentation, https://github.com/aladjov/CR/wiki
|
|
@@ -164,12 +164,14 @@ It serves two audiences:
|
|
|
164
164
|
|
|
165
165
|
## Quick Start
|
|
166
166
|
|
|
167
|
-
### 1. Install
|
|
167
|
+
### 1. Install (local)
|
|
168
168
|
|
|
169
169
|
```bash
|
|
170
170
|
pip install "churnkit[ml]"
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
+
For **Databricks**, see the [Databricks Installation](https://github.com/aladjov/CR/wiki/Databricks-Installation) guide.
|
|
174
|
+
|
|
173
175
|
### 2. Bootstrap notebooks into your project
|
|
174
176
|
|
|
175
177
|
```bash
|
|
@@ -200,6 +202,7 @@ Detailed documentation lives in the [Wiki](https://github.com/aladjov/CR/wiki):
|
|
|
200
202
|
| Topic | Wiki Page |
|
|
201
203
|
|-------|-----------|
|
|
202
204
|
| Installation options & environment setup | [Getting Started](https://github.com/aladjov/CR/wiki/Getting-Started) |
|
|
205
|
+
| Databricks install & `databricks_init()` setup | [Databricks Installation](https://github.com/aladjov/CR/wiki/Databricks-Installation) |
|
|
203
206
|
| Medallion architecture & system design | [Architecture](https://github.com/aladjov/CR/wiki/Architecture) |
|
|
204
207
|
| Notebook workflow & iteration tracking | [Exploration Loop](https://github.com/aladjov/CR/wiki/Exploration-Loop) |
|
|
205
208
|
| Leakage-safe temporal data preparation | [Temporal Framework](https://github.com/aladjov/CR/wiki/Temporal-Framework) |
|
|
@@ -34,12 +34,14 @@ It serves two audiences:
|
|
|
34
34
|
|
|
35
35
|
## Quick Start
|
|
36
36
|
|
|
37
|
-
### 1. Install
|
|
37
|
+
### 1. Install (local)
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
pip install "churnkit[ml]"
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
For **Databricks**, see the [Databricks Installation](https://github.com/aladjov/CR/wiki/Databricks-Installation) guide.
|
|
44
|
+
|
|
43
45
|
### 2. Bootstrap notebooks into your project
|
|
44
46
|
|
|
45
47
|
```bash
|
|
@@ -70,6 +72,7 @@ Detailed documentation lives in the [Wiki](https://github.com/aladjov/CR/wiki):
|
|
|
70
72
|
| Topic | Wiki Page |
|
|
71
73
|
|-------|-----------|
|
|
72
74
|
| Installation options & environment setup | [Getting Started](https://github.com/aladjov/CR/wiki/Getting-Started) |
|
|
75
|
+
| Databricks install & `databricks_init()` setup | [Databricks Installation](https://github.com/aladjov/CR/wiki/Databricks-Installation) |
|
|
73
76
|
| Medallion architecture & system design | [Architecture](https://github.com/aladjov/CR/wiki/Architecture) |
|
|
74
77
|
| Notebook workflow & iteration tracking | [Exploration Loop](https://github.com/aladjov/CR/wiki/Exploration-Loop) |
|
|
75
78
|
| Leakage-safe temporal data preparation | [Temporal Framework](https://github.com/aladjov/CR/wiki/Temporal-Framework) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "churnkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.76.0a1"
|
|
4
4
|
description = "Structured ML framework for customer churn prediction -- from exploration notebooks to production pipelines, locally or on Databricks."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "Apache-2.0"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
PYPROJECT="$REPO_ROOT/pyproject.toml"
|
|
6
|
+
INIT_PY="$REPO_ROOT/src/customer_retention/__init__.py"
|
|
7
|
+
|
|
8
|
+
usage() {
|
|
9
|
+
echo "Usage: $0 <version>"
|
|
10
|
+
echo " e.g. $0 0.75.1a2"
|
|
11
|
+
echo " $0 v0.75.1a2 (leading 'v' is stripped for file versions)"
|
|
12
|
+
exit 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
[[ $# -ne 1 ]] && usage
|
|
16
|
+
|
|
17
|
+
# Strip leading 'v' for the version string used in files
|
|
18
|
+
VERSION="${1#v}"
|
|
19
|
+
TAG="v${VERSION}"
|
|
20
|
+
|
|
21
|
+
echo "==> Releasing ${TAG} (file version: ${VERSION})"
|
|
22
|
+
|
|
23
|
+
# --- 1. Update version in pyproject.toml and __init__.py ---------------
|
|
24
|
+
sed -i '' "s/^version = \".*\"/version = \"${VERSION}\"/" "$PYPROJECT"
|
|
25
|
+
sed -i '' "s/^__version__ = \".*\"/__version__ = \"${VERSION}\"/" "$INIT_PY"
|
|
26
|
+
|
|
27
|
+
echo " pyproject.toml -> $(grep '^version' "$PYPROJECT")"
|
|
28
|
+
echo " __init__.py -> $(grep '^__version__' "$INIT_PY")"
|
|
29
|
+
|
|
30
|
+
# --- 2. Commit the version bump ----------------------------------------
|
|
31
|
+
git -C "$REPO_ROOT" add "$PYPROJECT" "$INIT_PY"
|
|
32
|
+
git -C "$REPO_ROOT" commit -m "Bump version to ${VERSION}"
|
|
33
|
+
|
|
34
|
+
# --- 3. Tag the commit --------------------------------------------------
|
|
35
|
+
git -C "$REPO_ROOT" tag -a "$TAG" -m "Release ${TAG}"
|
|
36
|
+
echo " Tagged: ${TAG}"
|
|
37
|
+
|
|
38
|
+
# --- 4. Build -----------------------------------------------------------
|
|
39
|
+
echo "==> Building sdist + wheel"
|
|
40
|
+
rm -rf "$REPO_ROOT/dist"
|
|
41
|
+
uvx --from build pyproject-build "$REPO_ROOT" --outdir "$REPO_ROOT/dist"
|
|
42
|
+
|
|
43
|
+
# --- 5. Publish to PyPI -------------------------------------------------
|
|
44
|
+
echo "==> Uploading to PyPI"
|
|
45
|
+
uvx twine upload "$REPO_ROOT/dist/"*
|
|
46
|
+
|
|
47
|
+
echo "==> Done. ${TAG} published to PyPI."
|
|
@@ -17,7 +17,7 @@ Main module categories:
|
|
|
17
17
|
llm_context, iteration)
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
__version__ = "0.
|
|
20
|
+
__version__ = "0.76.0a1"
|
|
21
21
|
|
|
22
22
|
# Environment utilities (always available)
|
|
23
23
|
from .core.compat import (
|
|
@@ -34,4 +34,14 @@ __all__ = [
|
|
|
34
34
|
"is_spark_available",
|
|
35
35
|
"is_databricks",
|
|
36
36
|
"is_notebook",
|
|
37
|
+
# Databricks initialization
|
|
38
|
+
"databricks_init",
|
|
37
39
|
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def __getattr__(name: str):
|
|
43
|
+
if name == "databricks_init":
|
|
44
|
+
from .integrations.databricks_init import databricks_init
|
|
45
|
+
|
|
46
|
+
return databricks_init
|
|
47
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
{churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/notebook_progress.py
RENAMED
|
@@ -4,7 +4,7 @@ import threading
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
|
-
from customer_retention.
|
|
7
|
+
from customer_retention.core.compat import is_databricks
|
|
8
8
|
from customer_retention.core.config.experiments import get_notebook_experiments_dir
|
|
9
9
|
|
|
10
10
|
|
|
@@ -25,7 +25,7 @@ def track_and_export_previous(current_notebook: str) -> None:
|
|
|
25
25
|
previous = _read_last_notebook(progress_file)
|
|
26
26
|
_write_current_notebook(progress_file, current_notebook)
|
|
27
27
|
|
|
28
|
-
if previous:
|
|
28
|
+
if previous and not is_databricks():
|
|
29
29
|
_export_in_background(previous, docs_dir)
|
|
30
30
|
|
|
31
31
|
|
|
@@ -40,6 +40,8 @@ def _read_last_notebook(progress_file: Path) -> Optional[str]:
|
|
|
40
40
|
|
|
41
41
|
def _export_notebook(notebook_name: str, docs_dir: Path) -> Optional[Path]:
|
|
42
42
|
"""Export *notebook_name* to HTML in *docs_dir*."""
|
|
43
|
+
from customer_retention.analysis.notebook_html_exporter import export_notebook_html
|
|
44
|
+
|
|
43
45
|
return export_notebook_html(Path(notebook_name), docs_dir)
|
|
44
46
|
|
|
45
47
|
|
|
@@ -5,7 +5,7 @@ import numpy as np
|
|
|
5
5
|
import plotly.express as px
|
|
6
6
|
import plotly.graph_objects as go
|
|
7
7
|
|
|
8
|
-
from customer_retention.core.compat import DataFrame, Series, ensure_pandas_series, to_pandas
|
|
8
|
+
from customer_retention.core.compat import DataFrame, Series, ensure_pandas_series, safe_to_datetime, to_pandas
|
|
9
9
|
|
|
10
10
|
from .number_formatter import NumberFormatter
|
|
11
11
|
|
|
@@ -532,9 +532,8 @@ class ChartBuilder:
|
|
|
532
532
|
dates: Series,
|
|
533
533
|
title: Optional[str] = None,
|
|
534
534
|
) -> go.Figure:
|
|
535
|
-
import pandas as pd
|
|
536
535
|
dates = ensure_pandas_series(dates)
|
|
537
|
-
parsed =
|
|
536
|
+
parsed = safe_to_datetime(dates, errors="coerce").dropna()
|
|
538
537
|
|
|
539
538
|
if len(parsed) == 0:
|
|
540
539
|
fig = go.Figure()
|
|
@@ -1029,7 +1028,7 @@ class ChartBuilder:
|
|
|
1029
1028
|
"""
|
|
1030
1029
|
import pandas as pd
|
|
1031
1030
|
dates = ensure_pandas_series(dates)
|
|
1032
|
-
parsed =
|
|
1031
|
+
parsed = safe_to_datetime(dates, errors="coerce")
|
|
1033
1032
|
|
|
1034
1033
|
if values is not None:
|
|
1035
1034
|
values = ensure_pandas_series(values)
|
|
@@ -1078,7 +1077,7 @@ class ChartBuilder:
|
|
|
1078
1077
|
"""Create a month x day-of-week heatmap for pattern discovery."""
|
|
1079
1078
|
import pandas as pd
|
|
1080
1079
|
dates = ensure_pandas_series(dates)
|
|
1081
|
-
parsed =
|
|
1080
|
+
parsed = safe_to_datetime(dates, errors="coerce").dropna()
|
|
1082
1081
|
|
|
1083
1082
|
if values is not None:
|
|
1084
1083
|
values = ensure_pandas_series(values)
|
|
@@ -1127,7 +1126,7 @@ class ChartBuilder:
|
|
|
1127
1126
|
dates = ensure_pandas_series(dates)
|
|
1128
1127
|
values = ensure_pandas_series(values)
|
|
1129
1128
|
|
|
1130
|
-
df = pd.DataFrame({"date":
|
|
1129
|
+
df = pd.DataFrame({"date": safe_to_datetime(dates), "value": values}).dropna()
|
|
1131
1130
|
df = df.sort_values("date")
|
|
1132
1131
|
|
|
1133
1132
|
df["rolling_mean"] = df["value"].rolling(window=window, center=True, min_periods=1).mean()
|
|
@@ -2222,7 +2221,7 @@ class ChartBuilder:
|
|
|
2222
2221
|
import pandas as pd
|
|
2223
2222
|
with warnings.catch_warnings():
|
|
2224
2223
|
warnings.simplefilter("ignore")
|
|
2225
|
-
dates = pd.
|
|
2224
|
+
dates = safe_to_datetime(pd.Series(series), errors='coerce').dropna()
|
|
2226
2225
|
if len(dates) == 0:
|
|
2227
2226
|
return
|
|
2228
2227
|
|
|
@@ -98,6 +98,8 @@ def merge(left: Any, right: Any, how: str = "inner", on: Any = None, **kwargs: A
|
|
|
98
98
|
return pd.merge(left, right, how=how, on=on, **kwargs)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
native_pd = _pandas
|
|
102
|
+
|
|
101
103
|
Timestamp = _pandas.Timestamp
|
|
102
104
|
Timedelta = _pandas.Timedelta
|
|
103
105
|
DatetimeIndex = _pandas.DatetimeIndex
|
|
@@ -147,6 +149,54 @@ def is_float_dtype(arr_or_dtype: Any) -> bool:
|
|
|
147
149
|
return _pandas.api.types.is_float_dtype(arr_or_dtype)
|
|
148
150
|
|
|
149
151
|
|
|
152
|
+
def _infer_epoch_unit(value: int) -> str:
|
|
153
|
+
"""Infer the epoch unit from a representative integer timestamp value.
|
|
154
|
+
|
|
155
|
+
Spark LongType timestamps become int64 after ``to_pandas()``. The bare
|
|
156
|
+
``pd.to_datetime()`` call assumes nanoseconds for large integers, which
|
|
157
|
+
silently produces wrong dates when the source used seconds or milliseconds.
|
|
158
|
+
This helper picks the right ``unit`` based on magnitude.
|
|
159
|
+
"""
|
|
160
|
+
abs_val = abs(int(value))
|
|
161
|
+
if abs_val > 1e17:
|
|
162
|
+
return "ns"
|
|
163
|
+
if abs_val > 1e14:
|
|
164
|
+
return "us"
|
|
165
|
+
if abs_val > 1e11:
|
|
166
|
+
return "ms"
|
|
167
|
+
return "s"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def safe_to_datetime(series: Any, **kwargs: Any) -> _pandas.Series:
|
|
171
|
+
"""Convert a Series to datetime, handling Spark LongType epoch integers.
|
|
172
|
+
|
|
173
|
+
Like ``pd.to_datetime`` but automatically detects integer epoch columns
|
|
174
|
+
and passes the correct ``unit`` parameter. Any extra *kwargs* are
|
|
175
|
+
forwarded to ``pd.to_datetime``.
|
|
176
|
+
"""
|
|
177
|
+
series = ensure_pandas_series(series)
|
|
178
|
+
if _pandas.api.types.is_datetime64_any_dtype(series):
|
|
179
|
+
return series
|
|
180
|
+
if _pandas.api.types.is_integer_dtype(series):
|
|
181
|
+
non_null = series.dropna()
|
|
182
|
+
if len(non_null) > 0:
|
|
183
|
+
unit = _infer_epoch_unit(non_null.iloc[0])
|
|
184
|
+
return _pandas.to_datetime(series, unit=unit, **kwargs)
|
|
185
|
+
return _pandas.to_datetime(series, **kwargs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def ensure_datetime_column(df: _pandas.DataFrame, column: str) -> _pandas.DataFrame:
|
|
189
|
+
"""Ensure *column* in a **pandas** DataFrame is ``datetime64``.
|
|
190
|
+
|
|
191
|
+
Call this after ``to_pandas()`` to safely convert columns that may have
|
|
192
|
+
arrived as int64 epoch values from Spark. Returns the DataFrame
|
|
193
|
+
(modified in-place).
|
|
194
|
+
"""
|
|
195
|
+
if not _pandas.api.types.is_datetime64_any_dtype(df[column]):
|
|
196
|
+
df[column] = safe_to_datetime(df[column])
|
|
197
|
+
return df
|
|
198
|
+
|
|
199
|
+
|
|
150
200
|
class PandasCompat:
|
|
151
201
|
@staticmethod
|
|
152
202
|
def value_counts_normalize(series: Any, normalize: bool = False) -> Any:
|
|
@@ -165,6 +215,7 @@ compat = PandasCompat()
|
|
|
165
215
|
|
|
166
216
|
__all__ = [
|
|
167
217
|
"pd",
|
|
218
|
+
"native_pd",
|
|
168
219
|
"DataFrame",
|
|
169
220
|
"Series",
|
|
170
221
|
"Timestamp",
|
|
@@ -208,6 +259,8 @@ __all__ = [
|
|
|
208
259
|
"is_notebook",
|
|
209
260
|
"get_display_function",
|
|
210
261
|
"get_dbutils",
|
|
262
|
+
"safe_to_datetime",
|
|
263
|
+
"ensure_datetime_column",
|
|
211
264
|
"ops",
|
|
212
265
|
"DataOps",
|
|
213
266
|
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from .column_config import ColumnConfig, ColumnType, DatasetGranularity
|
|
2
|
+
from .experiments import (
|
|
3
|
+
CATALOG,
|
|
4
|
+
DATA_DIR,
|
|
5
|
+
EXPERIMENT_NAME,
|
|
6
|
+
EXPERIMENTS_DIR,
|
|
7
|
+
FEATURE_STORE_DIR,
|
|
8
|
+
FINDINGS_DIR,
|
|
9
|
+
MLRUNS_DIR,
|
|
10
|
+
OUTPUT_DIR,
|
|
11
|
+
SCHEMA,
|
|
12
|
+
WORKSPACE_PATH,
|
|
13
|
+
get_catalog,
|
|
14
|
+
get_data_dir,
|
|
15
|
+
get_experiment_name,
|
|
16
|
+
get_experiments_dir,
|
|
17
|
+
get_feature_store_dir,
|
|
18
|
+
get_findings_dir,
|
|
19
|
+
get_mlruns_dir,
|
|
20
|
+
get_notebook_experiments_dir,
|
|
21
|
+
get_schema,
|
|
22
|
+
get_workspace_path,
|
|
23
|
+
setup_experiments_structure,
|
|
24
|
+
)
|
|
25
|
+
from .pipeline_config import (
|
|
26
|
+
BronzeConfig,
|
|
27
|
+
DedupStrategy,
|
|
28
|
+
GoldConfig,
|
|
29
|
+
ModelingConfig,
|
|
30
|
+
PathConfig,
|
|
31
|
+
PipelineConfig,
|
|
32
|
+
SilverConfig,
|
|
33
|
+
ValidationConfig,
|
|
34
|
+
)
|
|
35
|
+
from .source_config import DataSourceConfig, FileFormat, Grain, SourceType
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"ColumnType",
|
|
39
|
+
"ColumnConfig",
|
|
40
|
+
"DatasetGranularity",
|
|
41
|
+
"SourceType",
|
|
42
|
+
"FileFormat",
|
|
43
|
+
"Grain",
|
|
44
|
+
"DataSourceConfig",
|
|
45
|
+
"DedupStrategy",
|
|
46
|
+
"BronzeConfig",
|
|
47
|
+
"SilverConfig",
|
|
48
|
+
"GoldConfig",
|
|
49
|
+
"ModelingConfig",
|
|
50
|
+
"ValidationConfig",
|
|
51
|
+
"PathConfig",
|
|
52
|
+
"PipelineConfig",
|
|
53
|
+
"CATALOG",
|
|
54
|
+
"SCHEMA",
|
|
55
|
+
"WORKSPACE_PATH",
|
|
56
|
+
"EXPERIMENT_NAME",
|
|
57
|
+
"EXPERIMENTS_DIR",
|
|
58
|
+
"FINDINGS_DIR",
|
|
59
|
+
"DATA_DIR",
|
|
60
|
+
"MLRUNS_DIR",
|
|
61
|
+
"FEATURE_STORE_DIR",
|
|
62
|
+
"OUTPUT_DIR",
|
|
63
|
+
"get_catalog",
|
|
64
|
+
"get_schema",
|
|
65
|
+
"get_workspace_path",
|
|
66
|
+
"get_experiment_name",
|
|
67
|
+
"get_experiments_dir",
|
|
68
|
+
"get_findings_dir",
|
|
69
|
+
"get_data_dir",
|
|
70
|
+
"get_mlruns_dir",
|
|
71
|
+
"get_feature_store_dir",
|
|
72
|
+
"get_notebook_experiments_dir",
|
|
73
|
+
"setup_experiments_structure",
|
|
74
|
+
]
|
|
@@ -36,12 +36,32 @@ def get_feature_store_dir(default: Optional[str] = None) -> Path:
|
|
|
36
36
|
return get_experiments_dir(default) / "feature_repo"
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def get_catalog(default: str = "main") -> str:
|
|
40
|
+
return os.environ.get("CR_CATALOG", default)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_schema(default: str = "default") -> str:
|
|
44
|
+
return os.environ.get("CR_SCHEMA", default)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_workspace_path(default: str | None = None) -> str | None:
|
|
48
|
+
return os.environ.get("CR_WORKSPACE_PATH", default)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_experiment_name(default: str = "customer_retention") -> str:
|
|
52
|
+
return os.environ.get("CR_EXPERIMENT_NAME", default)
|
|
53
|
+
|
|
54
|
+
|
|
39
55
|
EXPERIMENTS_DIR = get_experiments_dir()
|
|
40
56
|
FINDINGS_DIR = get_findings_dir()
|
|
41
57
|
DATA_DIR = get_data_dir()
|
|
42
58
|
MLRUNS_DIR = get_mlruns_dir()
|
|
43
59
|
FEATURE_STORE_DIR = get_feature_store_dir()
|
|
44
60
|
OUTPUT_DIR = FINDINGS_DIR
|
|
61
|
+
CATALOG = get_catalog()
|
|
62
|
+
SCHEMA = get_schema()
|
|
63
|
+
WORKSPACE_PATH = get_workspace_path()
|
|
64
|
+
EXPERIMENT_NAME = get_experiment_name()
|
|
45
65
|
|
|
46
66
|
|
|
47
67
|
def setup_experiments_structure(experiments_dir: Optional[Path] = None) -> None:
|
|
@@ -55,7 +55,8 @@ else:
|
|
|
55
55
|
else:
|
|
56
56
|
print("Warning: No feature_timestamp column found. Using current date (may cause leakage).")
|
|
57
57
|
if "signup_date" in df.columns:
|
|
58
|
-
|
|
58
|
+
from customer_retention.core.compat import safe_to_datetime
|
|
59
|
+
df["tenure_days"] = (pd.Timestamp.now() - safe_to_datetime(df["signup_date"])).dt.days'''),
|
|
59
60
|
self.cb.section("Validate Point-in-Time Correctness"),
|
|
60
61
|
self.cb.code('''if "feature_timestamp" in df.columns:
|
|
61
62
|
pit_report = PointInTimeJoiner.validate_temporal_integrity(df)
|
|
@@ -290,6 +290,7 @@ from pathlib import Path
|
|
|
290
290
|
{% if ops %}
|
|
291
291
|
from customer_retention.transforms import {{ ops | sort | join(', ') }}
|
|
292
292
|
{% endif %}
|
|
293
|
+
from customer_retention.core.compat import ensure_datetime_column, safe_to_datetime
|
|
293
294
|
from config import SOURCES, get_bronze_path{{ ', RAW_SOURCES' if config.lifecycle else '' }}
|
|
294
295
|
|
|
295
296
|
SOURCE_NAME = "{{ source }}"
|
|
@@ -356,7 +357,7 @@ def _load_raw_events():
|
|
|
356
357
|
{% if config.lifecycle.include_recency_bucket %}
|
|
357
358
|
|
|
358
359
|
def add_recency_tenure(df: pd.DataFrame, raw_df: pd.DataFrame) -> pd.DataFrame:
|
|
359
|
-
raw_df
|
|
360
|
+
ensure_datetime_column(raw_df, TIME_COLUMN)
|
|
360
361
|
reference_date = raw_df[TIME_COLUMN].max()
|
|
361
362
|
entity_stats = raw_df.groupby(ENTITY_COLUMN)[TIME_COLUMN].agg(["min", "max"])
|
|
362
363
|
entity_stats["days_since_last"] = (reference_date - entity_stats["max"]).dt.days
|
|
@@ -398,7 +399,7 @@ def add_lifecycle_quadrant(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
398
399
|
{% if config.lifecycle.include_cyclical_features %}
|
|
399
400
|
|
|
400
401
|
def add_cyclical_features(df: pd.DataFrame, raw_df: pd.DataFrame) -> pd.DataFrame:
|
|
401
|
-
raw_df
|
|
402
|
+
ensure_datetime_column(raw_df, TIME_COLUMN)
|
|
402
403
|
mean_dow = raw_df.groupby(ENTITY_COLUMN)[TIME_COLUMN].apply(lambda x: x.dt.dayofweek.mean())
|
|
403
404
|
df = df.merge(mean_dow.rename("mean_dow"), left_on=ENTITY_COLUMN, right_index=True, how="left")
|
|
404
405
|
df["dow_sin"] = np.sin(2 * np.pi * df["mean_dow"] / 7)
|
|
@@ -1447,6 +1448,7 @@ from pathlib import Path
|
|
|
1447
1448
|
{% if ops %}
|
|
1448
1449
|
from customer_retention.transforms import {{ ops | sort | join(', ') }}
|
|
1449
1450
|
{% endif %}
|
|
1451
|
+
from customer_retention.core.compat import ensure_datetime_column, safe_to_datetime
|
|
1450
1452
|
from config import PRODUCTION_DIR, RAW_SOURCES, TARGET_COLUMN
|
|
1451
1453
|
|
|
1452
1454
|
SOURCE_NAME = "{{ source }}"
|
|
@@ -1502,7 +1504,7 @@ AGG_FUNCS = {{ config.aggregation.agg_funcs }}
|
|
|
1502
1504
|
|
|
1503
1505
|
def apply_reshaping(df: pd.DataFrame) -> pd.DataFrame:
|
|
1504
1506
|
{% if config.aggregation %}
|
|
1505
|
-
df
|
|
1507
|
+
ensure_datetime_column(df, TIME_COLUMN)
|
|
1506
1508
|
reference_date = df[TIME_COLUMN].max()
|
|
1507
1509
|
result = df.groupby(ENTITY_COLUMN).agg("first")[[]]
|
|
1508
1510
|
if TARGET_COLUMN in df.columns:
|
|
@@ -1535,7 +1537,7 @@ def _load_raw_events():
|
|
|
1535
1537
|
{% if config.lifecycle.include_recency_bucket %}
|
|
1536
1538
|
|
|
1537
1539
|
def add_recency_tenure(df: pd.DataFrame, raw_df: pd.DataFrame) -> pd.DataFrame:
|
|
1538
|
-
raw_df
|
|
1540
|
+
ensure_datetime_column(raw_df, TIME_COLUMN)
|
|
1539
1541
|
reference_date = raw_df[TIME_COLUMN].max()
|
|
1540
1542
|
entity_stats = raw_df.groupby(ENTITY_COLUMN)[TIME_COLUMN].agg(["min", "max"])
|
|
1541
1543
|
entity_stats["days_since_last"] = (reference_date - entity_stats["max"]).dt.days
|
|
@@ -1577,7 +1579,7 @@ def add_lifecycle_quadrant(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
1577
1579
|
{% if config.lifecycle.include_cyclical_features %}
|
|
1578
1580
|
|
|
1579
1581
|
def add_cyclical_features(df: pd.DataFrame, raw_df: pd.DataFrame) -> pd.DataFrame:
|
|
1580
|
-
raw_df
|
|
1582
|
+
ensure_datetime_column(raw_df, TIME_COLUMN)
|
|
1581
1583
|
mean_dow = raw_df.groupby(ENTITY_COLUMN)[TIME_COLUMN].apply(lambda x: x.dt.dayofweek.mean())
|
|
1582
1584
|
df = df.merge(mean_dow.rename("mean_dow"), left_on=ENTITY_COLUMN, right_index=True, how="left")
|
|
1583
1585
|
df["dow_sin"] = np.sin(2 * np.pi * df["mean_dow"] / 7)
|