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.
Files changed (326) hide show
  1. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/PKG-INFO +5 -2
  2. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/README.md +4 -1
  3. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/pyproject.toml +1 -1
  4. churnkit-0.76.0a1/scripts/release.sh +47 -0
  5. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/__init__.py +11 -1
  6. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/notebook_progress.py +4 -2
  7. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/chart_builder.py +6 -7
  8. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/__init__.py +53 -0
  9. churnkit-0.76.0a1/src/customer_retention/core/config/__init__.py +74 -0
  10. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/experiments.py +20 -0
  11. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s05_feature_engineering.py +2 -1
  12. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/renderer.py +7 -5
  13. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/mlflow_pipeline_generator.py +223 -149
  14. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/factory.py +8 -5
  15. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/base.py +1 -0
  16. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/databricks.py +58 -10
  17. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/base.py +8 -0
  18. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/databricks.py +15 -2
  19. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/local.py +7 -0
  20. churnkit-0.76.0a1/src/customer_retention/integrations/databricks_init.py +141 -0
  21. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/temporal_features.py +12 -12
  22. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/column_profiler.py +2 -2
  23. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/pattern_analysis_config.py +4 -3
  24. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_feature_analyzer.py +5 -5
  25. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_feature_engineer.py +8 -8
  26. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_pattern_analyzer.py +28 -10
  27. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_quality_checks.py +9 -4
  28. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/time_series_profiler.py +11 -10
  29. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/time_window_aggregator.py +7 -4
  30. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/datetime_transformer.py +10 -2
  31. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/timeseries_detector.py +4 -1
  32. churnkit-0.75.1a1/src/customer_retention/core/config/__init__.py +0 -39
  33. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/.gitignore +0 -0
  34. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/LICENSE +0 -0
  35. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/constraints/.gitkeep +0 -0
  36. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/00_start_here.ipynb +0 -0
  37. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01_data_discovery.ipynb +0 -0
  38. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01a_a_temporal_text_deep_dive.ipynb +0 -0
  39. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01a_temporal_deep_dive.ipynb +0 -0
  40. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01b_temporal_quality.ipynb +0 -0
  41. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01c_temporal_patterns.ipynb +0 -0
  42. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/01d_event_aggregation.ipynb +0 -0
  43. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/02_column_deep_dive.ipynb +0 -0
  44. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/02a_text_columns_deep_dive.ipynb +0 -0
  45. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/03_quality_assessment.ipynb +0 -0
  46. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/04_relationship_analysis.ipynb +0 -0
  47. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/05_multi_dataset.ipynb +0 -0
  48. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/06_feature_opportunities.ipynb +0 -0
  49. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/07_modeling_readiness.ipynb +0 -0
  50. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/08_baseline_experiments.ipynb +0 -0
  51. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/09_business_alignment.ipynb +0 -0
  52. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/10_spec_generation.ipynb +0 -0
  53. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/11_scoring_validation.ipynb +0 -0
  54. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/exploration_notebooks/12_view_documentation.ipynb +0 -0
  55. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/README.md +0 -0
  56. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/create_snapshot.py +0 -0
  57. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/generate_retail_dataset.py +0 -0
  58. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/generate_test_data.py +0 -0
  59. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/migrate_parquet_to_delta.py +0 -0
  60. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/data/migrate_to_temporal.py +0 -0
  61. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/build_wheel.sh +0 -0
  62. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/capture_runtime.py +0 -0
  63. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/dbr_init.sh +0 -0
  64. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/generate_constraints.py +0 -0
  65. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/databricks/notebook_setup.py +0 -0
  66. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/clean_notebook_outputs.py +0 -0
  67. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/export_tutorial_html.py +0 -0
  68. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/init_project.py +0 -0
  69. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/plotly_image_preprocessor.py +0 -0
  70. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/run_exploration.py +0 -0
  71. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/notebooks/test_notebooks.py +0 -0
  72. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/templates/tutorial_html/conf.json +0 -0
  73. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/templates/tutorial_html/index.html.j2 +0 -0
  74. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/scripts/update_notebook_paths.py +0 -0
  75. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/__init__.py +0 -0
  76. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/__init__.py +0 -0
  77. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/exploration_manager.py +0 -0
  78. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/explorer.py +0 -0
  79. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/findings.py +0 -0
  80. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/layered_recommendations.py +0 -0
  81. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/recommendation_builder.py +0 -0
  82. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/auto_explorer/recommendations.py +0 -0
  83. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/__init__.py +0 -0
  84. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/ab_test_designer.py +0 -0
  85. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/fairness_analyzer.py +0 -0
  86. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/intervention_matcher.py +0 -0
  87. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/report_generator.py +0 -0
  88. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/risk_profile.py +0 -0
  89. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/business/roi_analyzer.py +0 -0
  90. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/__init__.py +0 -0
  91. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/calibration_analyzer.py +0 -0
  92. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/cv_analyzer.py +0 -0
  93. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/error_analyzer.py +0 -0
  94. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/leakage_detector.py +0 -0
  95. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/noise_tester.py +0 -0
  96. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/overfitting_analyzer.py +0 -0
  97. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/diagnostics/segment_analyzer.py +0 -0
  98. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/__init__.py +0 -0
  99. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/config_generator.py +0 -0
  100. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/discovery_flow.py +0 -0
  101. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/discovery/type_inferencer.py +0 -0
  102. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/__init__.py +0 -0
  103. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/cohort_analyzer.py +0 -0
  104. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/counterfactual.py +0 -0
  105. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/individual_explainer.py +0 -0
  106. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/pdp_generator.py +0 -0
  107. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/interpretability/shap_explainer.py +0 -0
  108. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/jupyter_save_hook.py +0 -0
  109. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/notebook_html_exporter.py +0 -0
  110. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/plotly_preprocessor.py +0 -0
  111. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/__init__.py +0 -0
  112. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/base.py +0 -0
  113. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/__init__.py +0 -0
  114. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/consistency.py +0 -0
  115. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/deduplicate.py +0 -0
  116. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/impute.py +0 -0
  117. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/cleaning/outlier.py +0 -0
  118. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/datetime/__init__.py +0 -0
  119. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/datetime/extract.py +0 -0
  120. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/encoding/__init__.py +0 -0
  121. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/encoding/categorical.py +0 -0
  122. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/pipeline.py +0 -0
  123. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/registry.py +0 -0
  124. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/selection/__init__.py +0 -0
  125. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/selection/drop_column.py +0 -0
  126. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/transform/__init__.py +0 -0
  127. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/transform/power.py +0 -0
  128. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/recommendations/transform/scale.py +0 -0
  129. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/__init__.py +0 -0
  130. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/console.py +0 -0
  131. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/display.py +0 -0
  132. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/analysis/visualization/number_formatter.py +0 -0
  133. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/artifacts/__init__.py +0 -0
  134. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/artifacts/fit_artifact_registry.py +0 -0
  135. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/cli.py +0 -0
  136. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/__init__.py +0 -0
  137. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/detection.py +0 -0
  138. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/ops.py +0 -0
  139. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/pandas_backend.py +0 -0
  140. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/compat/spark_backend.py +0 -0
  141. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/__init__.py +0 -0
  142. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/base.py +0 -0
  143. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/__init__.py +0 -0
  144. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/deployer.py +0 -0
  145. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/explainer.py +0 -0
  146. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/feature_eng.py +0 -0
  147. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/ingester.py +0 -0
  148. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/profiler.py +0 -0
  149. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/trainer.py +0 -0
  150. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/transformer.py +0 -0
  151. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/components/validator.py +0 -0
  152. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/enums.py +0 -0
  153. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/orchestrator.py +0 -0
  154. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/components/registry.py +0 -0
  155. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/column_config.py +0 -0
  156. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/pipeline_config.py +0 -0
  157. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/config/source_config.py +0 -0
  158. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/__init__.py +0 -0
  159. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/leakage.py +0 -0
  160. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/severity.py +0 -0
  161. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/core/utils/statistics.py +0 -0
  162. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/__init__.py +0 -0
  163. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/__init__.py +0 -0
  164. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/base.py +0 -0
  165. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/cell_builder.py +0 -0
  166. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/config.py +0 -0
  167. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/databricks_generator.py +0 -0
  168. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/local_generator.py +0 -0
  169. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/project_init.py +0 -0
  170. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/runner.py +0 -0
  171. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/script_generator.py +0 -0
  172. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/__init__.py +0 -0
  173. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/base_stage.py +0 -0
  174. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s01_ingestion.py +0 -0
  175. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s02_profiling.py +0 -0
  176. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s03_cleaning.py +0 -0
  177. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s04_transformation.py +0 -0
  178. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s06_feature_selection.py +0 -0
  179. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s07_model_training.py +0 -0
  180. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s08_deployment.py +0 -0
  181. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s09_monitoring.py +0 -0
  182. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s10_batch_inference.py +0 -0
  183. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/notebook_generator/stages/s11_feature_store.py +0 -0
  184. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/__init__.py +0 -0
  185. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/code_generator.py +0 -0
  186. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/context.py +0 -0
  187. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/data_materializer.py +0 -0
  188. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/databricks_exporter.py +0 -0
  189. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/orchestration/doc_generator.py +0 -0
  190. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/__init__.py +0 -0
  191. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/findings_parser.py +0 -0
  192. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/generator.py +0 -0
  193. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/pipeline_generator/models.py +0 -0
  194. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/__init__.py +0 -0
  195. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/databricks_generator.py +0 -0
  196. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/generic_generator.py +0 -0
  197. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/generators/spec_generator/pipeline_spec.py +0 -0
  198. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/__init__.py +0 -0
  199. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/__init__.py +0 -0
  200. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/base.py +0 -0
  201. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/__init__.py +0 -0
  202. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/feast_adapter.py +0 -0
  203. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/feature_store/local.py +0 -0
  204. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/__init__.py +0 -0
  205. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/mlflow/experiment_tracker.py +0 -0
  206. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/__init__.py +0 -0
  207. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/base.py +0 -0
  208. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/databricks.py +0 -0
  209. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/adapters/storage/local.py +0 -0
  210. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/__init__.py +0 -0
  211. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/definitions.py +0 -0
  212. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/manager.py +0 -0
  213. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/feature_store/registry.py +0 -0
  214. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/__init__.py +0 -0
  215. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/context.py +0 -0
  216. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/feedback_collector.py +0 -0
  217. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/orchestrator.py +0 -0
  218. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/recommendation_tracker.py +0 -0
  219. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/iteration/signals.py +0 -0
  220. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/llm_context/__init__.py +0 -0
  221. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/llm_context/context_builder.py +0 -0
  222. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/llm_context/prompts.py +0 -0
  223. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/__init__.py +0 -0
  224. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/batch_integration.py +0 -0
  225. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/early_warning_model.py +0 -0
  226. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/event_schema.py +0 -0
  227. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/online_store_writer.py +0 -0
  228. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/realtime_scorer.py +0 -0
  229. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/trigger_engine.py +0 -0
  230. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/integrations/streaming/window_aggregator.py +0 -0
  231. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/__init__.py +0 -0
  232. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/__init__.py +0 -0
  233. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/base.py +0 -0
  234. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/missing_handler.py +0 -0
  235. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/cleaning/outlier_handler.py +0 -0
  236. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/__init__.py +0 -0
  237. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/batch_scorer.py +0 -0
  238. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/champion_challenger.py +0 -0
  239. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/model_registry.py +0 -0
  240. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/deployment/retraining_trigger.py +0 -0
  241. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/__init__.py +0 -0
  242. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/behavioral_features.py +0 -0
  243. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/customer_segmentation.py +0 -0
  244. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_definitions.py +0 -0
  245. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_engineer.py +0 -0
  246. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_manifest.py +0 -0
  247. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/feature_selector.py +0 -0
  248. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/features/interaction_features.py +0 -0
  249. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/__init__.py +0 -0
  250. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/load_result.py +0 -0
  251. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/loaders.py +0 -0
  252. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/ingestion/source_registry.py +0 -0
  253. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/__init__.py +0 -0
  254. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/baseline_trainer.py +0 -0
  255. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/cross_validator.py +0 -0
  256. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/data_splitter.py +0 -0
  257. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/feature_scaler.py +0 -0
  258. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/hyperparameter_tuner.py +0 -0
  259. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/imbalance_handler.py +0 -0
  260. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/mlflow_logger.py +0 -0
  261. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/model_comparator.py +0 -0
  262. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/model_evaluator.py +0 -0
  263. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/modeling/threshold_optimizer.py +0 -0
  264. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/__init__.py +0 -0
  265. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/alert_manager.py +0 -0
  266. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/drift_detector.py +0 -0
  267. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/monitoring/performance_monitor.py +0 -0
  268. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/preprocessing/__init__.py +0 -0
  269. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/preprocessing/transformer_manager.py +0 -0
  270. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/__init__.py +0 -0
  271. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/categorical_distribution.py +0 -0
  272. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/categorical_target_analyzer.py +0 -0
  273. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/distribution_analysis.py +0 -0
  274. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/drift_detector.py +0 -0
  275. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/feature_capacity.py +0 -0
  276. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/profile_result.py +0 -0
  277. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/quality_checks.py +0 -0
  278. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/relationship_detector.py +0 -0
  279. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/relationship_recommender.py +0 -0
  280. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/report_generator.py +0 -0
  281. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/scd_analyzer.py +0 -0
  282. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/segment_analyzer.py +0 -0
  283. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/segment_aware_outlier.py +0 -0
  284. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/target_level_analyzer.py +0 -0
  285. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_analyzer.py +0 -0
  286. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_coverage.py +0 -0
  287. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/temporal_target_analyzer.py +0 -0
  288. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/text_embedder.py +0 -0
  289. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/text_processor.py +0 -0
  290. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/text_reducer.py +0 -0
  291. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/type_detector.py +0 -0
  292. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/profiling/window_recommendation.py +0 -0
  293. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/__init__.py +0 -0
  294. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/access_guard.py +0 -0
  295. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/cutoff_analyzer.py +0 -0
  296. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/data_preparer.py +0 -0
  297. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/point_in_time_join.py +0 -0
  298. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/point_in_time_registry.py +0 -0
  299. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/scenario_detector.py +0 -0
  300. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/snapshot_manager.py +0 -0
  301. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/synthetic_coordinator.py +0 -0
  302. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/timestamp_discovery.py +0 -0
  303. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/temporal/timestamp_manager.py +0 -0
  304. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/__init__.py +0 -0
  305. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/binary_handler.py +0 -0
  306. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/categorical_encoder.py +0 -0
  307. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/numeric_transformer.py +0 -0
  308. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/transformation/pipeline.py +0 -0
  309. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/__init__.py +0 -0
  310. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/adversarial_scoring_validator.py +0 -0
  311. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/business_sense_gate.py +0 -0
  312. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/data_quality_gate.py +0 -0
  313. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/data_validators.py +0 -0
  314. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/feature_quality_gate.py +0 -0
  315. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/gates.py +0 -0
  316. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/leakage_gate.py +0 -0
  317. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/model_validity_gate.py +0 -0
  318. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/pipeline_validation_runner.py +0 -0
  319. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/quality_scorer.py +0 -0
  320. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/rule_generator.py +0 -0
  321. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/stages/validation/scoring_pipeline_validator.py +0 -0
  322. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/__init__.py +0 -0
  323. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/artifact_store.py +0 -0
  324. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/executor.py +0 -0
  325. {churnkit-0.75.1a1 → churnkit-0.76.0a1}/src/customer_retention/transforms/fitted.py +0 -0
  326. {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.75.1a1
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.75.1a1"
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.75.1a1"
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}")
@@ -4,7 +4,7 @@ import threading
4
4
  from pathlib import Path
5
5
  from typing import Optional
6
6
 
7
- from customer_retention.analysis.notebook_html_exporter import export_notebook_html
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 = pd.to_datetime(dates, errors="coerce").dropna()
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 = pd.to_datetime(dates, errors="coerce")
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 = pd.to_datetime(dates, errors="coerce").dropna()
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": pd.to_datetime(dates), "value": values}).dropna()
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.to_datetime(series, errors='coerce').dropna()
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
- df["tenure_days"] = (pd.Timestamp.now() - pd.to_datetime(df["signup_date"])).dt.days'''),
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[TIME_COLUMN] = pd.to_datetime(raw_df[TIME_COLUMN])
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[TIME_COLUMN] = pd.to_datetime(raw_df[TIME_COLUMN])
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[TIME_COLUMN] = pd.to_datetime(df[TIME_COLUMN])
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[TIME_COLUMN] = pd.to_datetime(raw_df[TIME_COLUMN])
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[TIME_COLUMN] = pd.to_datetime(raw_df[TIME_COLUMN])
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)