google-meridian 1.3.1__tar.gz → 1.4.0__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 (116) hide show
  1. google_meridian-1.4.0/MANIFEST.in +2 -0
  2. {google_meridian-1.3.1/google_meridian.egg-info → google_meridian-1.4.0}/PKG-INFO +13 -9
  3. {google_meridian-1.3.1 → google_meridian-1.4.0}/README.md +1 -1
  4. {google_meridian-1.3.1 → google_meridian-1.4.0/google_meridian.egg-info}/PKG-INFO +13 -9
  5. {google_meridian-1.3.1 → google_meridian-1.4.0}/google_meridian.egg-info/SOURCES.txt +42 -10
  6. {google_meridian-1.3.1 → google_meridian-1.4.0}/google_meridian.egg-info/requires.txt +12 -7
  7. {google_meridian-1.3.1 → google_meridian-1.4.0}/google_meridian.egg-info/top_level.txt +1 -0
  8. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/__init__.py +1 -2
  9. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/analyzer.py +0 -1
  10. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/optimizer.py +5 -3
  11. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/review/checks.py +81 -30
  12. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/review/constants.py +4 -0
  13. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/review/results.py +40 -9
  14. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/summarizer.py +8 -3
  15. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/test_utils.py +934 -485
  16. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/visualizer.py +11 -7
  17. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/backend/__init__.py +53 -5
  18. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/backend/test_utils.py +72 -0
  19. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/constants.py +2 -0
  20. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/load.py +2 -0
  21. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/test_utils.py +82 -10
  22. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/__init__.py +2 -0
  23. google_meridian-1.4.0/meridian/model/context.py +925 -0
  24. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/eda/__init__.py +0 -1
  25. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/eda/constants.py +13 -2
  26. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/eda/eda_engine.py +299 -37
  27. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/eda/eda_outcome.py +21 -1
  28. google_meridian-1.4.0/meridian/model/equations.py +418 -0
  29. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/knots.py +75 -47
  30. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/model.py +93 -792
  31. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/card.html.jinja +1 -1
  32. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/chart.html.jinja +1 -1
  33. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/chips.html.jinja +1 -1
  34. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian/templates}/formatter.py +12 -1
  35. google_meridian-1.4.0/meridian/templates/formatter_test.py +216 -0
  36. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/insights.html.jinja +1 -1
  37. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/stats.html.jinja +1 -1
  38. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/style.scss +1 -1
  39. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/summary.html.jinja +4 -2
  40. {google_meridian-1.3.1/meridian/analysis → google_meridian-1.4.0/meridian}/templates/table.html.jinja +1 -1
  41. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/version.py +1 -1
  42. {google_meridian-1.3.1 → google_meridian-1.4.0}/pyproject.toml +15 -8
  43. google_meridian-1.4.0/scenarioplanner/__init__.py +42 -0
  44. google_meridian-1.4.0/scenarioplanner/converters/__init__.py +25 -0
  45. google_meridian-1.4.0/scenarioplanner/converters/dataframe/__init__.py +28 -0
  46. google_meridian-1.4.0/scenarioplanner/converters/dataframe/budget_opt_converters.py +383 -0
  47. google_meridian-1.4.0/scenarioplanner/converters/dataframe/common.py +71 -0
  48. google_meridian-1.4.0/scenarioplanner/converters/dataframe/constants.py +137 -0
  49. google_meridian-1.4.0/scenarioplanner/converters/dataframe/converter.py +42 -0
  50. google_meridian-1.4.0/scenarioplanner/converters/dataframe/dataframe_model_converter.py +70 -0
  51. google_meridian-1.4.0/scenarioplanner/converters/dataframe/marketing_analyses_converters.py +543 -0
  52. google_meridian-1.4.0/scenarioplanner/converters/dataframe/rf_opt_converters.py +314 -0
  53. google_meridian-1.4.0/scenarioplanner/converters/mmm.py +743 -0
  54. google_meridian-1.4.0/scenarioplanner/converters/mmm_converter.py +58 -0
  55. google_meridian-1.4.0/scenarioplanner/converters/sheets.py +156 -0
  56. google_meridian-1.4.0/scenarioplanner/converters/test_data.py +714 -0
  57. google_meridian-1.4.0/scenarioplanner/linkingapi/__init__.py +47 -0
  58. google_meridian-1.4.0/scenarioplanner/linkingapi/constants.py +27 -0
  59. google_meridian-1.4.0/scenarioplanner/linkingapi/url_generator.py +131 -0
  60. google_meridian-1.4.0/scenarioplanner/mmm_ui_proto_generator.py +354 -0
  61. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/__init__.py +15 -0
  62. google_meridian-1.4.0/schema/mmm_proto_generator.py +71 -0
  63. google_meridian-1.4.0/schema/model_consumer.py +133 -0
  64. google_meridian-1.4.0/schema/processors/__init__.py +77 -0
  65. google_meridian-1.4.0/schema/processors/budget_optimization_processor.py +832 -0
  66. google_meridian-1.4.0/schema/processors/common.py +64 -0
  67. google_meridian-1.4.0/schema/processors/marketing_processor.py +1136 -0
  68. google_meridian-1.4.0/schema/processors/model_fit_processor.py +367 -0
  69. google_meridian-1.4.0/schema/processors/model_kernel_processor.py +117 -0
  70. google_meridian-1.4.0/schema/processors/model_processor.py +412 -0
  71. google_meridian-1.4.0/schema/processors/reach_frequency_optimization_processor.py +584 -0
  72. google_meridian-1.4.0/schema/test_data.py +380 -0
  73. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/utils/__init__.py +1 -0
  74. google_meridian-1.4.0/schema/utils/date_range_bucketing.py +117 -0
  75. {google_meridian-1.3.1 → google_meridian-1.4.0}/setup.py +1 -1
  76. google_meridian-1.3.1/MANIFEST.in +0 -2
  77. google_meridian-1.3.1/meridian/model/eda/meridian_eda.py +0 -220
  78. {google_meridian-1.3.1 → google_meridian-1.4.0}/LICENSE +0 -0
  79. {google_meridian-1.3.1 → google_meridian-1.4.0}/google_meridian.egg-info/dependency_links.txt +0 -0
  80. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/__init__.py +0 -0
  81. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/review/__init__.py +0 -0
  82. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/review/configs.py +0 -0
  83. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/review/reviewer.py +0 -0
  84. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/analysis/summary_text.py +0 -0
  85. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/backend/config.py +0 -0
  86. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/__init__.py +0 -0
  87. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/arg_builder.py +0 -0
  88. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/data_frame_input_data_builder.py +0 -0
  89. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/input_data.py +0 -0
  90. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/input_data_builder.py +0 -0
  91. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/nd_array_input_data_builder.py +0 -0
  92. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/data/time_coordinates.py +0 -0
  93. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/mlflow/__init__.py +0 -0
  94. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/mlflow/autolog.py +0 -0
  95. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/adstock_hill.py +0 -0
  96. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/eda/eda_spec.py +0 -0
  97. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/media.py +0 -0
  98. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/model_test_data.py +0 -0
  99. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/posterior_sampler.py +0 -0
  100. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/prior_distribution.py +0 -0
  101. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/prior_sampler.py +0 -0
  102. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/spec.py +0 -0
  103. {google_meridian-1.3.1 → google_meridian-1.4.0}/meridian/model/transformers.py +0 -0
  104. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/__init__.py +0 -0
  105. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/constants.py +0 -0
  106. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/distribution.py +0 -0
  107. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/eda_spec.py +0 -0
  108. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/function_registry.py +0 -0
  109. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/hyperparameters.py +0 -0
  110. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/inference_data.py +0 -0
  111. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/marketing_data.py +0 -0
  112. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/meridian_serde.py +0 -0
  113. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/serde.py +0 -0
  114. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/serde/test_data.py +0 -0
  115. {google_meridian-1.3.1 → google_meridian-1.4.0}/schema/utils/time_record.py +0 -0
  116. {google_meridian-1.3.1 → google_meridian-1.4.0}/setup.cfg +0 -0
@@ -0,0 +1,2 @@
1
+ global-exclude *_test.py
2
+ include meridian/templates/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-meridian
3
- Version: 1.3.1
3
+ Version: 1.4.0
4
4
  Summary: Google's open source mixed marketing model library, helps you understand your return on investment and direct your ad spend with confidence.
5
5
  Author-email: The Meridian Authors <no-reply@google.com>
6
6
  Project-URL: homepage, https://github.com/google/meridian
@@ -22,12 +22,11 @@ Requires-Dist: joblib
22
22
  Requires-Dist: natsort<8,>=7.1.1
23
23
  Requires-Dist: numpy<3,>=2.0.2
24
24
  Requires-Dist: pandas<3,>=2.2.2
25
- Requires-Dist: patsy<1,>=0.5.3
26
25
  Requires-Dist: scipy<2,>=1.13.1
27
26
  Requires-Dist: statsmodels>=0.14.5
28
- Requires-Dist: tensorflow<2.19,>=2.18
27
+ Requires-Dist: tensorflow<2.21,>=2.18
29
28
  Requires-Dist: tensorflow-probability<0.26,>=0.25
30
- Requires-Dist: tf-keras<2.19,>=2.18
29
+ Requires-Dist: tf-keras<2.21,>=2.18
31
30
  Requires-Dist: xarray
32
31
  Provides-Extra: dev
33
32
  Requires-Dist: pytest>=8.0.0; extra == "dev"
@@ -38,16 +37,21 @@ Provides-Extra: colab
38
37
  Requires-Dist: psutil; extra == "colab"
39
38
  Requires-Dist: python-calamine; extra == "colab"
40
39
  Provides-Extra: and-cuda
41
- Requires-Dist: tensorflow[and-cuda]<2.19,>=2.18; extra == "and-cuda"
40
+ Requires-Dist: tensorflow[and-cuda]<2.21,>=2.18; extra == "and-cuda"
42
41
  Provides-Extra: mlflow
43
42
  Requires-Dist: mlflow; extra == "mlflow"
44
43
  Provides-Extra: jax
45
- Requires-Dist: jax==0.4.26; extra == "jax"
46
- Requires-Dist: jaxlib==0.4.26; extra == "jax"
44
+ Requires-Dist: jax==0.5.3; extra == "jax"
45
+ Requires-Dist: jaxlib==0.5.3; extra == "jax"
47
46
  Requires-Dist: tensorflow-probability[substrates-jax]==0.25.0; extra == "jax"
48
47
  Provides-Extra: schema
49
- Requires-Dist: mmm-proto-schema; extra == "schema"
48
+ Requires-Dist: mmm-proto-schema>=1.1.0; extra == "schema"
50
49
  Requires-Dist: semver; extra == "schema"
50
+ Provides-Extra: scenarioplanner
51
+ Requires-Dist: google-api-python-client; extra == "scenarioplanner"
52
+ Requires-Dist: google-auth; extra == "scenarioplanner"
53
+ Requires-Dist: mmm-proto-schema>=1.1.0; extra == "scenarioplanner"
54
+ Requires-Dist: semver; extra == "scenarioplanner"
51
55
  Dynamic: license-file
52
56
 
53
57
  # About Meridian
@@ -203,7 +207,7 @@ To cite this repository:
203
207
  author = {Google Meridian Marketing Mix Modeling Team},
204
208
  title = {Meridian: Marketing Mix Modeling},
205
209
  url = {https://github.com/google/meridian},
206
- version = {1.3.1},
210
+ version = {1.4.0},
207
211
  year = {2025},
208
212
  }
209
213
  ```
@@ -151,7 +151,7 @@ To cite this repository:
151
151
  author = {Google Meridian Marketing Mix Modeling Team},
152
152
  title = {Meridian: Marketing Mix Modeling},
153
153
  url = {https://github.com/google/meridian},
154
- version = {1.3.1},
154
+ version = {1.4.0},
155
155
  year = {2025},
156
156
  }
157
157
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-meridian
3
- Version: 1.3.1
3
+ Version: 1.4.0
4
4
  Summary: Google's open source mixed marketing model library, helps you understand your return on investment and direct your ad spend with confidence.
5
5
  Author-email: The Meridian Authors <no-reply@google.com>
6
6
  Project-URL: homepage, https://github.com/google/meridian
@@ -22,12 +22,11 @@ Requires-Dist: joblib
22
22
  Requires-Dist: natsort<8,>=7.1.1
23
23
  Requires-Dist: numpy<3,>=2.0.2
24
24
  Requires-Dist: pandas<3,>=2.2.2
25
- Requires-Dist: patsy<1,>=0.5.3
26
25
  Requires-Dist: scipy<2,>=1.13.1
27
26
  Requires-Dist: statsmodels>=0.14.5
28
- Requires-Dist: tensorflow<2.19,>=2.18
27
+ Requires-Dist: tensorflow<2.21,>=2.18
29
28
  Requires-Dist: tensorflow-probability<0.26,>=0.25
30
- Requires-Dist: tf-keras<2.19,>=2.18
29
+ Requires-Dist: tf-keras<2.21,>=2.18
31
30
  Requires-Dist: xarray
32
31
  Provides-Extra: dev
33
32
  Requires-Dist: pytest>=8.0.0; extra == "dev"
@@ -38,16 +37,21 @@ Provides-Extra: colab
38
37
  Requires-Dist: psutil; extra == "colab"
39
38
  Requires-Dist: python-calamine; extra == "colab"
40
39
  Provides-Extra: and-cuda
41
- Requires-Dist: tensorflow[and-cuda]<2.19,>=2.18; extra == "and-cuda"
40
+ Requires-Dist: tensorflow[and-cuda]<2.21,>=2.18; extra == "and-cuda"
42
41
  Provides-Extra: mlflow
43
42
  Requires-Dist: mlflow; extra == "mlflow"
44
43
  Provides-Extra: jax
45
- Requires-Dist: jax==0.4.26; extra == "jax"
46
- Requires-Dist: jaxlib==0.4.26; extra == "jax"
44
+ Requires-Dist: jax==0.5.3; extra == "jax"
45
+ Requires-Dist: jaxlib==0.5.3; extra == "jax"
47
46
  Requires-Dist: tensorflow-probability[substrates-jax]==0.25.0; extra == "jax"
48
47
  Provides-Extra: schema
49
- Requires-Dist: mmm-proto-schema; extra == "schema"
48
+ Requires-Dist: mmm-proto-schema>=1.1.0; extra == "schema"
50
49
  Requires-Dist: semver; extra == "schema"
50
+ Provides-Extra: scenarioplanner
51
+ Requires-Dist: google-api-python-client; extra == "scenarioplanner"
52
+ Requires-Dist: google-auth; extra == "scenarioplanner"
53
+ Requires-Dist: mmm-proto-schema>=1.1.0; extra == "scenarioplanner"
54
+ Requires-Dist: semver; extra == "scenarioplanner"
51
55
  Dynamic: license-file
52
56
 
53
57
  # About Meridian
@@ -203,7 +207,7 @@ To cite this repository:
203
207
  author = {Google Meridian Marketing Mix Modeling Team},
204
208
  title = {Meridian: Marketing Mix Modeling},
205
209
  url = {https://github.com/google/meridian},
206
- version = {1.3.1},
210
+ version = {1.4.0},
207
211
  year = {2025},
208
212
  }
209
213
  ```
@@ -13,7 +13,6 @@ meridian/constants.py
13
13
  meridian/version.py
14
14
  meridian/analysis/__init__.py
15
15
  meridian/analysis/analyzer.py
16
- meridian/analysis/formatter.py
17
16
  meridian/analysis/optimizer.py
18
17
  meridian/analysis/summarizer.py
19
18
  meridian/analysis/summary_text.py
@@ -25,14 +24,6 @@ meridian/analysis/review/configs.py
25
24
  meridian/analysis/review/constants.py
26
25
  meridian/analysis/review/results.py
27
26
  meridian/analysis/review/reviewer.py
28
- meridian/analysis/templates/card.html.jinja
29
- meridian/analysis/templates/chart.html.jinja
30
- meridian/analysis/templates/chips.html.jinja
31
- meridian/analysis/templates/insights.html.jinja
32
- meridian/analysis/templates/stats.html.jinja
33
- meridian/analysis/templates/style.scss
34
- meridian/analysis/templates/summary.html.jinja
35
- meridian/analysis/templates/table.html.jinja
36
27
  meridian/backend/__init__.py
37
28
  meridian/backend/config.py
38
29
  meridian/backend/test_utils.py
@@ -49,6 +40,8 @@ meridian/mlflow/__init__.py
49
40
  meridian/mlflow/autolog.py
50
41
  meridian/model/__init__.py
51
42
  meridian/model/adstock_hill.py
43
+ meridian/model/context.py
44
+ meridian/model/equations.py
52
45
  meridian/model/knots.py
53
46
  meridian/model/media.py
54
47
  meridian/model/model.py
@@ -63,8 +56,46 @@ meridian/model/eda/constants.py
63
56
  meridian/model/eda/eda_engine.py
64
57
  meridian/model/eda/eda_outcome.py
65
58
  meridian/model/eda/eda_spec.py
66
- meridian/model/eda/meridian_eda.py
59
+ meridian/templates/card.html.jinja
60
+ meridian/templates/chart.html.jinja
61
+ meridian/templates/chips.html.jinja
62
+ meridian/templates/formatter.py
63
+ meridian/templates/formatter_test.py
64
+ meridian/templates/insights.html.jinja
65
+ meridian/templates/stats.html.jinja
66
+ meridian/templates/style.scss
67
+ meridian/templates/summary.html.jinja
68
+ meridian/templates/table.html.jinja
69
+ scenarioplanner/__init__.py
70
+ scenarioplanner/mmm_ui_proto_generator.py
71
+ scenarioplanner/converters/__init__.py
72
+ scenarioplanner/converters/mmm.py
73
+ scenarioplanner/converters/mmm_converter.py
74
+ scenarioplanner/converters/sheets.py
75
+ scenarioplanner/converters/test_data.py
76
+ scenarioplanner/converters/dataframe/__init__.py
77
+ scenarioplanner/converters/dataframe/budget_opt_converters.py
78
+ scenarioplanner/converters/dataframe/common.py
79
+ scenarioplanner/converters/dataframe/constants.py
80
+ scenarioplanner/converters/dataframe/converter.py
81
+ scenarioplanner/converters/dataframe/dataframe_model_converter.py
82
+ scenarioplanner/converters/dataframe/marketing_analyses_converters.py
83
+ scenarioplanner/converters/dataframe/rf_opt_converters.py
84
+ scenarioplanner/linkingapi/__init__.py
85
+ scenarioplanner/linkingapi/constants.py
86
+ scenarioplanner/linkingapi/url_generator.py
67
87
  schema/__init__.py
88
+ schema/mmm_proto_generator.py
89
+ schema/model_consumer.py
90
+ schema/test_data.py
91
+ schema/processors/__init__.py
92
+ schema/processors/budget_optimization_processor.py
93
+ schema/processors/common.py
94
+ schema/processors/marketing_processor.py
95
+ schema/processors/model_fit_processor.py
96
+ schema/processors/model_kernel_processor.py
97
+ schema/processors/model_processor.py
98
+ schema/processors/reach_frequency_optimization_processor.py
68
99
  schema/serde/__init__.py
69
100
  schema/serde/constants.py
70
101
  schema/serde/distribution.py
@@ -77,4 +108,5 @@ schema/serde/meridian_serde.py
77
108
  schema/serde/serde.py
78
109
  schema/serde/test_data.py
79
110
  schema/utils/__init__.py
111
+ schema/utils/date_range_bucketing.py
80
112
  schema/utils/time_record.py
@@ -5,16 +5,15 @@ joblib
5
5
  natsort<8,>=7.1.1
6
6
  numpy<3,>=2.0.2
7
7
  pandas<3,>=2.2.2
8
- patsy<1,>=0.5.3
9
8
  scipy<2,>=1.13.1
10
9
  statsmodels>=0.14.5
11
- tensorflow<2.19,>=2.18
10
+ tensorflow<2.21,>=2.18
12
11
  tensorflow-probability<0.26,>=0.25
13
- tf-keras<2.19,>=2.18
12
+ tf-keras<2.21,>=2.18
14
13
  xarray
15
14
 
16
15
  [and-cuda]
17
- tensorflow[and-cuda]<2.19,>=2.18
16
+ tensorflow[and-cuda]<2.21,>=2.18
18
17
 
19
18
  [colab]
20
19
  psutil
@@ -27,13 +26,19 @@ pylint>=2.6.0
27
26
  pyink
28
27
 
29
28
  [jax]
30
- jax==0.4.26
31
- jaxlib==0.4.26
29
+ jax==0.5.3
30
+ jaxlib==0.5.3
32
31
  tensorflow-probability[substrates-jax]==0.25.0
33
32
 
34
33
  [mlflow]
35
34
  mlflow
36
35
 
36
+ [scenarioplanner]
37
+ google-api-python-client
38
+ google-auth
39
+ mmm-proto-schema>=1.1.0
40
+ semver
41
+
37
42
  [schema]
38
- mmm-proto-schema
43
+ mmm-proto-schema>=1.1.0
39
44
  semver
@@ -1,2 +1,3 @@
1
1
  meridian
2
+ scenarioplanner
2
3
  schema
@@ -15,9 +15,8 @@
15
15
  """Meridian analysis API for trained models."""
16
16
 
17
17
  from meridian.analysis import analyzer
18
- from meridian.analysis import formatter
19
18
  from meridian.analysis import optimizer
20
19
  from meridian.analysis import review
21
20
  from meridian.analysis import summarizer
22
21
  from meridian.analysis import visualizer
23
-
22
+ from meridian.templates import formatter
@@ -53,7 +53,6 @@ def _validate_non_media_baseline_values_numbers(
53
53
  )
54
54
 
55
55
 
56
- # TODO: Refactor the related unit tests to be under DataTensors.
57
56
  @dataclasses.dataclass
58
57
  class DataTensors(backend.ExtensionType):
59
58
  """Container for data variable arguments of Analyzer methods.
@@ -27,10 +27,10 @@ import jinja2
27
27
  from meridian import backend
28
28
  from meridian import constants as c
29
29
  from meridian.analysis import analyzer as analyzer_module
30
- from meridian.analysis import formatter
31
30
  from meridian.analysis import summary_text
32
31
  from meridian.data import time_coordinates as tc
33
32
  from meridian.model import model
33
+ from meridian.templates import formatter
34
34
  import numpy as np
35
35
  import pandas as pd
36
36
  import xarray as xr
@@ -1174,10 +1174,12 @@ class OptimizationResults:
1174
1174
  diff = self.optimized_data.total_cpik - self.nonoptimized_data.total_cpik
1175
1175
  non_optimized_performance_title = summary_text.NON_OPTIMIZED_CPIK_LABEL
1176
1176
  non_optimized_performance_stat = (
1177
- f'${self.nonoptimized_data.total_cpik:.2f}'
1177
+ f'{currency}{self.nonoptimized_data.total_cpik:.2f}'
1178
1178
  )
1179
1179
  optimized_performance_title = summary_text.OPTIMIZED_CPIK_LABEL
1180
- optimized_performance_stat = f'${self.optimized_data.total_cpik:.2f}'
1180
+ optimized_performance_stat = (
1181
+ f'{currency}{self.optimized_data.total_cpik:.2f}'
1182
+ )
1181
1183
  optimized_performance_diff = formatter.compact_number(diff, 2, currency)
1182
1184
  non_optimized_performance = formatter.StatsSpec(
1183
1185
  title=non_optimized_performance_title,
@@ -28,6 +28,7 @@ from meridian.analysis.review import constants as review_constants
28
28
  from meridian.analysis.review import results
29
29
  from meridian.model import model
30
30
  import numpy as np
31
+ import pandas as pd
31
32
 
32
33
  ConfigType = TypeVar("ConfigType", bound=configs.BaseConfig)
33
34
  ResultType = TypeVar("ResultType", bound=results.CheckResult)
@@ -207,6 +208,51 @@ class BayesianPPPCheck(
207
208
  # ==============================================================================
208
209
  # Check: Goodness of Fit
209
210
  # ==============================================================================
211
+ def _set_details_from_gof_dataframe(
212
+ details: dict[str, float],
213
+ gof_df: pd.DataFrame,
214
+ geo_granularity: str,
215
+ suffix: str | None = None,
216
+ ) -> None:
217
+ """Sets the `details` variable of the GoodnessOfFitCheckResult.
218
+
219
+ This method takes a DataFrame containing goodness of fit metrics and pivots it
220
+ to a Series, which is then added to the `details` variable of the
221
+ `GoodnessOfFitCheckResult`.
222
+
223
+ Args:
224
+ details: A dictionary to store the goodness of fit metrics in.
225
+ gof_df: A DataFrame containing predictive accuracy of the whole data (if
226
+ holdout set is not used) of filtered to a single evaluation set ("all",
227
+ "train", or "test").
228
+ geo_granularity: The geo granularity of the data ("geo" or "national").
229
+ suffix: A suffix to add to the metric names (e.g., "all", "train", "test").
230
+ If None, the metrics are added without a suffix.
231
+ """
232
+ gof_metrics_pivoted = gof_df.pivot(
233
+ index=constants.GEO_GRANULARITY,
234
+ columns=constants.METRIC,
235
+ values=constants.VALUE,
236
+ )
237
+ gof_metrics_series = gof_metrics_pivoted.loc[geo_granularity]
238
+ if suffix is not None:
239
+ details[f"{review_constants.R_SQUARED}_{suffix}"] = gof_metrics_series[
240
+ constants.R_SQUARED
241
+ ]
242
+ details[f"{review_constants.MAPE}_{suffix}"] = gof_metrics_series[
243
+ constants.MAPE
244
+ ]
245
+ details[f"{review_constants.WMAPE}_{suffix}"] = gof_metrics_series[
246
+ constants.WMAPE
247
+ ]
248
+ else:
249
+ details[review_constants.R_SQUARED] = gof_metrics_series[
250
+ constants.R_SQUARED
251
+ ]
252
+ details[review_constants.MAPE] = gof_metrics_series[constants.MAPE]
253
+ details[review_constants.WMAPE] = gof_metrics_series[constants.WMAPE]
254
+
255
+
210
256
  class GoodnessOfFitCheck(
211
257
  BaseCheck[configs.GoodnessOfFitConfig, results.GoodnessOfFitCheckResult]
212
258
  ):
@@ -221,38 +267,43 @@ class GoodnessOfFitCheck(
221
267
  )
222
268
 
223
269
  gof_metrics = gof_df[gof_df[constants.GEO_GRANULARITY] == geo_granularity]
224
- if constants.EVALUATION_SET_VAR in gof_df.columns:
225
- gof_metrics = gof_metrics[
226
- gof_metrics[constants.EVALUATION_SET_VAR] == constants.ALL_DATA
227
- ]
228
-
229
- gof_metrics_pivoted = gof_metrics.pivot(
230
- index=constants.GEO_GRANULARITY,
231
- columns=constants.METRIC,
232
- values=constants.VALUE,
233
- )
234
- gof_metrics_series = gof_metrics_pivoted.loc[geo_granularity]
235
-
236
- r_squared = gof_metrics_series[constants.R_SQUARED]
237
- mape = gof_metrics_series[constants.MAPE]
238
- wmape = gof_metrics_series[constants.WMAPE]
239
-
240
- details = {
241
- review_constants.R_SQUARED: r_squared,
242
- review_constants.MAPE: mape,
243
- review_constants.WMAPE: wmape,
244
- }
245
-
246
- if r_squared > 0:
247
- return results.GoodnessOfFitCheckResult(
248
- case=results.GoodnessOfFitCases.PASS,
249
- details=details,
250
- )
251
- else: # r_squared <= 0
252
- return results.GoodnessOfFitCheckResult(
253
- case=results.GoodnessOfFitCases.REVIEW,
270
+ is_holdout = constants.EVALUATION_SET_VAR in gof_df.columns
271
+
272
+ details = {}
273
+ case = results.GoodnessOfFitCases.PASS
274
+
275
+ if is_holdout:
276
+ for evaluation_set, suffix in [
277
+ (constants.ALL_DATA, review_constants.ALL_SUFFIX),
278
+ (constants.TRAIN, review_constants.TRAIN_SUFFIX),
279
+ (constants.TEST, review_constants.TEST_SUFFIX),
280
+ ]:
281
+ set_metrics = gof_metrics[
282
+ gof_metrics[constants.EVALUATION_SET_VAR] == evaluation_set
283
+ ]
284
+ _set_details_from_gof_dataframe(
285
+ details=details,
286
+ gof_df=set_metrics,
287
+ geo_granularity=geo_granularity,
288
+ suffix=suffix,
289
+ )
290
+ if details[f"{review_constants.R_SQUARED}_{suffix}"] <= 0:
291
+ case = results.GoodnessOfFitCases.REVIEW
292
+ else:
293
+ _set_details_from_gof_dataframe(
254
294
  details=details,
295
+ gof_df=gof_metrics,
296
+ geo_granularity=geo_granularity,
297
+ suffix=None,
255
298
  )
299
+ if details[review_constants.R_SQUARED] <= 0:
300
+ case = results.GoodnessOfFitCases.REVIEW
301
+
302
+ return results.GoodnessOfFitCheckResult(
303
+ case=case,
304
+ details=details,
305
+ is_holdout=is_holdout,
306
+ )
256
307
 
257
308
 
258
309
  # ==============================================================================
@@ -32,6 +32,10 @@ NEGATIVE_BASELINE_PROB_REVIEW_THRESHOLD = (
32
32
  R_SQUARED = "r_squared"
33
33
  MAPE = "mape"
34
34
  WMAPE = "wmape"
35
+ ALL_SUFFIX = "all"
36
+ TRAIN_SUFFIX = "train"
37
+ TEST_SUFFIX = "test"
38
+ EVALUATION_SET_SUFFIXES = (ALL_SUFFIX, TRAIN_SUFFIX, TEST_SUFFIX)
35
39
  MEAN = "mean"
36
40
  VARIANCE = "variance"
37
41
  MEDIAN = "median"
@@ -319,18 +319,12 @@ class GoodnessOfFitCases(ModelCheckCase, enum.Enum):
319
319
 
320
320
  PASS = (
321
321
  Status.PASS,
322
- (
323
- "R-squared = {r_squared:.4f}, MAPE = {mape:.4f}, and wMAPE ="
324
- " {wmape:.4f}."
325
- ),
322
+ "R-squared = {r_squared:.4f}, MAPE = {mape:.4f}, and wMAPE = {wmape:.4f}",
326
323
  _GOODNESS_OF_FIT_PASS_RECOMMENDATION,
327
324
  )
328
325
  REVIEW = (
329
326
  Status.REVIEW,
330
- (
331
- "R-squared = {r_squared:.4f}, MAPE = {mape:.4f}, and wMAPE ="
332
- " {wmape:.4f}."
333
- ),
327
+ "R-squared = {r_squared:.4f}, MAPE = {mape:.4f}, and wMAPE = {wmape:.4f}",
334
328
  _GOODNESS_OF_FIT_REVIEW_RECOMMENDATION,
335
329
  )
336
330
 
@@ -348,9 +342,28 @@ class GoodnessOfFitCheckResult(CheckResult):
348
342
  """The immutable result of the Goodness of Fit Check."""
349
343
 
350
344
  case: GoodnessOfFitCases
345
+ is_holdout: bool = False
351
346
 
352
347
  def __post_init__(self):
353
- if any(
348
+ if self.is_holdout:
349
+ required_keys = []
350
+ for suffix in [
351
+ constants.ALL_SUFFIX,
352
+ constants.TRAIN_SUFFIX,
353
+ constants.TEST_SUFFIX,
354
+ ]:
355
+ required_keys.extend([
356
+ f"{constants.R_SQUARED}_{suffix}",
357
+ f"{constants.MAPE}_{suffix}",
358
+ f"{constants.WMAPE}_{suffix}",
359
+ ])
360
+ if any(key not in self.details for key in required_keys):
361
+ raise ValueError(
362
+ "The message template is missing required formatting arguments for"
363
+ f" holdout case. Required keys: {required_keys}. Details:"
364
+ f" {self.details}."
365
+ )
366
+ elif any(
354
367
  key not in self.details
355
368
  for key in (
356
369
  constants.R_SQUARED,
@@ -364,6 +377,24 @@ class GoodnessOfFitCheckResult(CheckResult):
364
377
  f" {self.details}."
365
378
  )
366
379
 
380
+ @property
381
+ def recommendation(self) -> str:
382
+ """Returns the check result message."""
383
+ if self.is_holdout:
384
+ report_str = (
385
+ "R-squared = {r_squared_all:.4f} (All),"
386
+ " {r_squared_train:.4f} (Train), {r_squared_test:.4f} (Test); MAPE"
387
+ " = {mape_all:.4f} (All), {mape_train:.4f} (Train),"
388
+ " {mape_test:.4f} (Test); wMAPE = {wmape_all:.4f} (All),"
389
+ " {wmape_train:.4f} (Train), {wmape_test:.4f} (Test)".format(
390
+ **self.details
391
+ )
392
+ )
393
+ else:
394
+ report_str = self.case.message_template.format(**self.details)
395
+
396
+ return f"{report_str}. {self.case.recommendation}"
397
+
367
398
 
368
399
  # ==============================================================================
369
400
  # Check: ROI Consistency
@@ -21,11 +21,11 @@ import os
21
21
  import jinja2
22
22
  from meridian import constants as c
23
23
  from meridian.analysis import analyzer
24
- from meridian.analysis import formatter
25
24
  from meridian.analysis import summary_text
26
25
  from meridian.analysis import visualizer
27
26
  from meridian.data import time_coordinates as tc
28
27
  from meridian.model import model
28
+ from meridian.templates import formatter
29
29
  import pandas as pd
30
30
  import xarray as xr
31
31
 
@@ -318,7 +318,7 @@ class Summarizer:
318
318
  chart_json=media_summary.plot_contribution_waterfall_chart().to_json(),
319
319
  )
320
320
  lead_channels = self._get_sorted_posterior_mean_metrics_df(
321
- media_summary, [c.INCREMENTAL_OUTCOME]
321
+ media_summary, [c.INCREMENTAL_OUTCOME], include_non_paid_channels=True
322
322
  )[c.CHANNEL][:2]
323
323
  formatted_channels = [channel.title() for channel in lead_channels]
324
324
 
@@ -358,9 +358,14 @@ class Summarizer:
358
358
  media_summary: visualizer.MediaSummary,
359
359
  metrics: Sequence[str],
360
360
  ascending: bool = False,
361
+ include_non_paid_channels: bool = False,
361
362
  ) -> pd.DataFrame:
363
+ if include_non_paid_channels:
364
+ summary_metrics = media_summary.get_all_summary_metrics()
365
+ else:
366
+ summary_metrics = media_summary.get_paid_summary_metrics()
362
367
  return (
363
- media_summary.get_paid_summary_metrics()[metrics]
368
+ summary_metrics[metrics]
364
369
  .sel(distribution=c.POSTERIOR, metric=c.MEAN)
365
370
  .drop_sel(channel=c.ALL_CHANNELS)
366
371
  .to_dataframe()