google-meridian 1.3.0__py3-none-any.whl → 1.3.2__py3-none-any.whl

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 (52) hide show
  1. google_meridian-1.3.2.dist-info/METADATA +209 -0
  2. google_meridian-1.3.2.dist-info/RECORD +76 -0
  3. {google_meridian-1.3.0.dist-info → google_meridian-1.3.2.dist-info}/top_level.txt +1 -0
  4. meridian/analysis/__init__.py +1 -2
  5. meridian/analysis/analyzer.py +0 -1
  6. meridian/analysis/optimizer.py +5 -3
  7. meridian/analysis/review/checks.py +81 -30
  8. meridian/analysis/review/constants.py +4 -0
  9. meridian/analysis/review/results.py +40 -9
  10. meridian/analysis/summarizer.py +1 -1
  11. meridian/analysis/visualizer.py +1 -1
  12. meridian/backend/__init__.py +229 -24
  13. meridian/backend/test_utils.py +194 -0
  14. meridian/constants.py +1 -0
  15. meridian/data/load.py +2 -0
  16. meridian/model/eda/__init__.py +0 -1
  17. meridian/model/eda/constants.py +12 -2
  18. meridian/model/eda/eda_engine.py +353 -45
  19. meridian/model/eda/eda_outcome.py +21 -1
  20. meridian/model/knots.py +17 -0
  21. meridian/model/model_test_data.py +15 -0
  22. meridian/{analysis/templates → templates}/card.html.jinja +1 -1
  23. meridian/{analysis/templates → templates}/chart.html.jinja +1 -1
  24. meridian/{analysis/templates → templates}/chips.html.jinja +1 -1
  25. meridian/{analysis → templates}/formatter.py +12 -1
  26. meridian/templates/formatter_test.py +216 -0
  27. meridian/{analysis/templates → templates}/insights.html.jinja +1 -1
  28. meridian/{analysis/templates → templates}/stats.html.jinja +1 -1
  29. meridian/{analysis/templates → templates}/style.css +1 -1
  30. meridian/{analysis/templates → templates}/style.scss +1 -1
  31. meridian/{analysis/templates → templates}/summary.html.jinja +4 -2
  32. meridian/{analysis/templates → templates}/table.html.jinja +1 -1
  33. meridian/version.py +1 -1
  34. schema/__init__.py +30 -0
  35. schema/serde/__init__.py +26 -0
  36. schema/serde/constants.py +48 -0
  37. schema/serde/distribution.py +515 -0
  38. schema/serde/eda_spec.py +192 -0
  39. schema/serde/function_registry.py +143 -0
  40. schema/serde/hyperparameters.py +363 -0
  41. schema/serde/inference_data.py +105 -0
  42. schema/serde/marketing_data.py +1321 -0
  43. schema/serde/meridian_serde.py +413 -0
  44. schema/serde/serde.py +47 -0
  45. schema/serde/test_data.py +4608 -0
  46. schema/utils/__init__.py +17 -0
  47. schema/utils/time_record.py +156 -0
  48. google_meridian-1.3.0.dist-info/METADATA +0 -409
  49. google_meridian-1.3.0.dist-info/RECORD +0 -62
  50. meridian/model/eda/meridian_eda.py +0 -220
  51. {google_meridian-1.3.0.dist-info → google_meridian-1.3.2.dist-info}/WHEEL +0 -0
  52. {google_meridian-1.3.0.dist-info → google_meridian-1.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: google-meridian
3
+ Version: 1.3.2
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
+ Author-email: The Meridian Authors <no-reply@google.com>
6
+ Project-URL: homepage, https://github.com/google/meridian
7
+ Project-URL: repository, https://github.com/google/meridian
8
+ Project-URL: changelog, https://github.com/google/meridian/blob/main/CHANGELOG.md
9
+ Project-URL: documentation, https://developers.google.com/meridian
10
+ Keywords: mmm
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Topic :: Other/Nonlisted Topic
14
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: arviz
19
+ Requires-Dist: altair>=5
20
+ Requires-Dist: immutabledict
21
+ Requires-Dist: joblib
22
+ Requires-Dist: natsort<8,>=7.1.1
23
+ Requires-Dist: numpy<3,>=2.0.2
24
+ Requires-Dist: pandas<3,>=2.2.2
25
+ Requires-Dist: patsy<1,>=0.5.3
26
+ Requires-Dist: scipy<2,>=1.13.1
27
+ Requires-Dist: statsmodels>=0.14.5
28
+ Requires-Dist: tensorflow<2.21,>=2.18
29
+ Requires-Dist: tensorflow-probability<0.26,>=0.25
30
+ Requires-Dist: tf-keras<2.21,>=2.18
31
+ Requires-Dist: xarray
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
34
+ Requires-Dist: pytest-xdist; extra == "dev"
35
+ Requires-Dist: pylint>=2.6.0; extra == "dev"
36
+ Requires-Dist: pyink; extra == "dev"
37
+ Provides-Extra: colab
38
+ Requires-Dist: psutil; extra == "colab"
39
+ Requires-Dist: python-calamine; extra == "colab"
40
+ Provides-Extra: and-cuda
41
+ Requires-Dist: tensorflow[and-cuda]<2.21,>=2.18; extra == "and-cuda"
42
+ Provides-Extra: mlflow
43
+ Requires-Dist: mlflow; extra == "mlflow"
44
+ Provides-Extra: jax
45
+ Requires-Dist: jax==0.5.3; extra == "jax"
46
+ Requires-Dist: jaxlib==0.5.3; extra == "jax"
47
+ Requires-Dist: tensorflow-probability[substrates-jax]==0.25.0; extra == "jax"
48
+ Provides-Extra: schema
49
+ Requires-Dist: mmm-proto-schema; extra == "schema"
50
+ Requires-Dist: semver; extra == "schema"
51
+ Dynamic: license-file
52
+
53
+ # About Meridian
54
+
55
+ Marketing mix modeling (MMM) is a statistical analysis technique that measures
56
+ the impact of marketing campaigns and activities to guide budget planning
57
+ decisions and improve overall media effectiveness. MMM uses aggregated data to
58
+ measure impact across marketing channels and account for non-marketing factors
59
+ that impact sales and other key performance indicators (KPIs). MMM is
60
+ privacy-safe and does not use any cookie or user-level information.
61
+
62
+ Meridian is an MMM framework that enables advertisers to set up and run their
63
+ own in-house models. Meridian helps you answer key questions such as:
64
+
65
+ * How did the marketing channels drive my revenue or other KPI?
66
+ * What was my marketing return on investment (ROI)?
67
+ * How do I optimize my marketing budget allocation for the future?
68
+
69
+ Meridian is a highly customizable modeling framework that is based on
70
+ [Bayesian causal inference](https://developers.google.com/meridian/docs/causal-inference/bayesian-inference).
71
+ It is capable of handling large scale geo-level data, which is encouraged if
72
+ available, but it can also be used for national-level modeling. Meridian
73
+ provides clear insights and visualizations to inform business decisions around
74
+ marketing budget and planning. Additionally, Meridian provides methodologies to
75
+ support calibration of MMM with experiments and other prior information, and to
76
+ optimize target ad frequency by utilizing reach and frequency data.
77
+
78
+ If you are using LightweightMMM, see the
79
+ [migration guide](https://developers.google.com/meridian/docs/migrate) to help
80
+ you understand the differences between these MMM projects.
81
+
82
+ ## Install Meridian
83
+
84
+ Python 3.11 or 3.12 is required to use Meridian. We also recommend using a
85
+ minimum of 1 GPU.
86
+
87
+ Note: This project has been tested on T4 GPU using 16 GB of RAM.
88
+
89
+ To install Meridian, run the following command to automatically install the
90
+ latest release from PyPI.
91
+
92
+ * For Linux-GPU users:
93
+
94
+ Note: CUDA toolchain and a compatible GPU device is necessary for
95
+ `[and-cuda]` extra to activate.
96
+
97
+ ```sh
98
+ $ pip install --upgrade google-meridian[and-cuda]
99
+ ```
100
+
101
+ * For macOS and general CPU users:
102
+
103
+ Note: There is no official GPU support for macOS.
104
+
105
+ ```sh
106
+ $ pip install --upgrade google-meridian
107
+ ```
108
+
109
+ Alternatively, run the following command to install the most recent, unreleased
110
+ version from GitHub.
111
+
112
+ * For GPU users:
113
+
114
+ ```sh
115
+ $ pip install --upgrade "google-meridian[and-cuda] @ git+https://github.com/google/meridian.git"
116
+ ```
117
+
118
+ * For CPU users:
119
+
120
+ ```sh
121
+ $ pip install --upgrade git+https://github.com/google/meridian.git
122
+ ```
123
+
124
+ We recommend to install Meridian in a fresh
125
+ [virtual environment](https://virtualenv.pypa.io/en/latest/user_guide.html#quick-start)
126
+ to make sure that correct versions of all the dependencies are installed, as
127
+ defined in [pyproject.toml](https://github.com/google/meridian/blob/main/pyproject.toml).
128
+
129
+ ## How to use the Meridian library
130
+
131
+ To get started with Meridian, you can run the code programmatically using sample
132
+ data with the [Getting Started Colab][3].
133
+
134
+ The Meridian model uses a holistic MCMC sampling approach called
135
+ [No U Turn Sampler (NUTS)](https://www.tensorflow.org/probability/api_docs/python/tfp/experimental/mcmc/NoUTurnSampler)
136
+ which can be compute intensive. To help with this, GPU support has been
137
+ developed across the library (out-of-the-box) using tensors. We recommend
138
+ running your Meridian model on GPUs to get real time optimization results and
139
+ significantly reduce training time.
140
+
141
+ ## Meridian Documentation & Tutorials
142
+
143
+ The following documentation, colab, and video resources will help you get
144
+ started quickly with using Meridian:
145
+
146
+ | Resource | Description |
147
+ | --------------------------- | ---------------------------------------------- |
148
+ | [Meridian documentation][1] | Main landing page for Meridian documentation. |
149
+ | [Meridian basics][2] | Learn about Meridian features, methodologies, and the model math. |
150
+ | [Getting started colab][3] | Install and quickly learn how to use Meridian with this colab tutorial using sample data. |
151
+ | [User guide][4] | A detailed walk-through of how to use Meridian and generating visualizations using your own data. |
152
+ | [Pre-modeling][5] | Prepare and analyze your data before modeling. |
153
+ | [Modeling][6] | Modeling guidance for model refinement and edge cases. |
154
+ | [Post-modeling][7] | Post-modeling guidance for model fit, visualizations, optimizations, refreshing the model, and debugging. |
155
+ | [Migrate from LMMM][8] | Learn about the differences between Meridian and LightweightMMM as you consider migrating. |
156
+ | [API Reference][9] | API reference documentation for the Meridian package. |
157
+ | [Reference list][10] | White papers and other referenced material. |
158
+
159
+ [1]: https://developers.google.com/meridian
160
+ [2]: https://developers.google.com/meridian/docs/basics/meridian-introduction
161
+ [3]: https://developers.google.com/meridian/notebook/meridian-getting-started
162
+ [4]: https://developers.google.com/meridian/docs/user-guide/installing
163
+ [5]: https://developers.google.com/meridian/docs/pre-modeling/collect-data
164
+ [6]: https://developers.google.com/meridian/docs/advanced-modeling/control-variables
165
+ [7]: https://developers.google.com/meridian/docs/post-modeling/model-fit
166
+ [8]: https://developers.google.com/meridian/docs/migrate
167
+ [9]: https://developers.google.com/meridian/reference/api/meridian
168
+ [10]: https://developers.google.com/meridian/docs/reference-list
169
+
170
+ ## Support
171
+
172
+ **Questions about methodology**: Please see the [Modeling](https://developers.google.com/meridian/docs/basics/meridian-introduction) tab in the technical documentation.
173
+
174
+ **Issues installing or using Meridian**: Feel free to post questions in the
175
+ [Discussions](https://github.com/google/meridian/discussions) or [Issues](https://github.com/google/meridian/issues) tabs of the Meridian GitHub repository. The Meridian team responds to
176
+ these questions weekly in batches, so please be patient and don't reach out
177
+ directly to your Google Account teams.
178
+
179
+ **Bug reports**: Please post bug reports to the [Issues](https://github.com/google/meridian/issues)
180
+ tab of the Meridian GitHub repository. We also encourage the community to share
181
+ tips and advice with each other on the [Issues](https://github.com/google/meridian/issues)
182
+ tab. When our team addresses or resolves a new bug, we will notify you through
183
+ the comments on the issue.
184
+
185
+ **Feature requests**: Please post these to the [Discussions](https://github.com/google/meridian/discussions)
186
+ tab of the Meridian GitHub repository. We have an internal roadmap for Meridian
187
+ development, but would love your inputs for new feature requests so that we can
188
+ prioritize them based on the roadmap.
189
+
190
+ **Pull requests**: These are appreciated but are very difficult for us to merge
191
+ because the code in this repository is linked to Google internal systems and has
192
+ to pass internal review. If you submit a pull request and we believe that we can
193
+ incorporate a change in the base code, we will reach out to you directly about
194
+ this.
195
+
196
+ ## Citing Meridian
197
+
198
+ To cite this repository:
199
+
200
+ <!-- mdlint off(SNIPPET_INVALID_LANGUAGE) -->
201
+ ```BibTeX
202
+ @software{meridian_github,
203
+ author = {Google Meridian Marketing Mix Modeling Team},
204
+ title = {Meridian: Marketing Mix Modeling},
205
+ url = {https://github.com/google/meridian},
206
+ version = {1.3.2},
207
+ year = {2025},
208
+ }
209
+ ```
@@ -0,0 +1,76 @@
1
+ google_meridian-1.3.2.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
2
+ meridian/__init__.py,sha256=0fOT5oNZF7-pbiWWGUefV-ysafttieG079m1ijMFQO8,861
3
+ meridian/constants.py,sha256=idvYuDmoULgHvVG5kGJb2j2VAdBF6BeOwDfKLP14-Fo,20322
4
+ meridian/version.py,sha256=eMNUh0AywCHr0ZyrDyJchcddVsAyWWKKHuXI0VxLScE,644
5
+ meridian/analysis/__init__.py,sha256=AM7xpqoeC-mmY4tPIyHisjQ2MICI7v3jSri--DhDqXA,874
6
+ meridian/analysis/analyzer.py,sha256=qGKYoEpWC6pDf2sDrwLhpa2putDnpUzbBpuNqL1lGi4,219910
7
+ meridian/analysis/optimizer.py,sha256=6OqJg8T09g4GqJc6_DnYDJsBAlY4ToUb3MRtS0EH4Nc,126316
8
+ meridian/analysis/summarizer.py,sha256=pah0osYYJFQTf2Fn7-nnvxlBupf9cfaKW87bcqRO1A4,19036
9
+ meridian/analysis/summary_text.py,sha256=I_smDkZJYp2j77ea-9AIbgeraDa7-qUYyb-IthP2qO4,12438
10
+ meridian/analysis/test_utils.py,sha256=aZq88pxtpMHwhcpfYz8nHhR0Alhi_OvgS9qBR4LBgO0,78346
11
+ meridian/analysis/visualizer.py,sha256=_z5vSnXyv3Zx4tmKLyevnMolzLzUzNFcNInOipEmMok,94092
12
+ meridian/analysis/review/__init__.py,sha256=cF24EbhiVSs-tvtRf59uVin39tu6aCTTCaeEdv6ISZ8,804
13
+ meridian/analysis/review/checks.py,sha256=QEUVwC8L9Pif3y0B_OVAUpZeN6EnFGKH2oXA_dsdgbc,26893
14
+ meridian/analysis/review/configs.py,sha256=5JJ8v6n22GNBmE78xNX6jwdjkZz2qar4Q9YTcVqzcoI,3653
15
+ meridian/analysis/review/constants.py,sha256=9tnnc_vAaJi9mnZ0GrRr86RsVq5fyhBaEtIvOLNmn8A,1498
16
+ meridian/analysis/review/results.py,sha256=ZZiAdFrqySzcbjrCEacBLQS_ddiklZ34yD1BWns5SYI,17418
17
+ meridian/analysis/review/reviewer.py,sha256=BcfmqHjp-30iZBlrzWfXDN1IJU-UIjINxZ7lsrj5Mts,6675
18
+ meridian/backend/__init__.py,sha256=i2XOZnIPo3xAKpMGT39-4CVnVdf4cJyO0jB9FEE4zlQ,41337
19
+ meridian/backend/config.py,sha256=B9VQnhBfg9RW04GNbt7F5uCugByenoJzt-keFLLYEp8,3561
20
+ meridian/backend/test_utils.py,sha256=oJNosF_x_BzNuia8LzLFb_YfjGWHRCzR5FXNN5KQ8sw,13738
21
+ meridian/data/__init__.py,sha256=StIe-wfYnnbfUbKtZHwnAQcRQUS8XCZk_PCaEzw90Ww,929
22
+ meridian/data/arg_builder.py,sha256=Kqlt88bOqFj6D3xNwvWo4MBwNwcDFHzd-wMfEOmLoPU,3741
23
+ meridian/data/data_frame_input_data_builder.py,sha256=_hexZMFAuAowgo6FaOGElHSFHqhGnHQwEEBcwnT3zUE,27295
24
+ meridian/data/input_data.py,sha256=Qlxm4El6h1SRPsWDqZoKkOcMtrjiRWr3z8sU2mtghRA,43151
25
+ meridian/data/input_data_builder.py,sha256=tbZjVXPDfmtndVyJA0fmzGzZwZb0RCEjXOTXb-ga8Nc,25648
26
+ meridian/data/load.py,sha256=ETX8Z62Gk6JcxFxvyB4XQhpNcRSqBRIO4_sTAN58mCY,40172
27
+ meridian/data/nd_array_input_data_builder.py,sha256=lfpmnENGuSGKyUd7bDGAwoLqHqteOKmHdKl0VI2wCQA,16341
28
+ meridian/data/test_utils.py,sha256=mw-QPTP15oXf32I7cxMe8iSFBLB3seqEiITZMTz_Eg8,59838
29
+ meridian/data/time_coordinates.py,sha256=C5A5fscSLjPH6G9YT8OspgIlCrkMY7y8dMFEt3tNSnE,9874
30
+ meridian/mlflow/__init__.py,sha256=elwXUqPQYi7VF9PYjelU1tydfcUrmtuoq6eJCOnV9bk,693
31
+ meridian/mlflow/autolog.py,sha256=SZsrynLjozcyrAFCNWiqchSa2yOszVnwFBGz23BmWUU,6379
32
+ meridian/model/__init__.py,sha256=mhF5VkRxvwamRa_0AihgbFuXLMueRCK-Je_ZZvU5IFw,1013
33
+ meridian/model/adstock_hill.py,sha256=HoRKjyL03pCTBz6Utof9wEvlQCFM43BvrEW_oupj7NU,17688
34
+ meridian/model/knots.py,sha256=B78oTQ97Zd0aON4CnhMPqJZ4eamy6d-esKMWqoDf9uQ,27273
35
+ meridian/model/media.py,sha256=skjy4Vd8LfDQWlqR_2lJ1qbG9UcS1dow5W45BAu4qk8,14599
36
+ meridian/model/model.py,sha256=jMtfl7woWtJ8M8AX42QeZ5hUS8hlhPdZ-9OU8KahjKA,68984
37
+ meridian/model/model_test_data.py,sha256=XGBz8RGdCsjAUOmgxX3CfWSj-_hdq2Lc8saFCqmImwM,23901
38
+ meridian/model/posterior_sampler.py,sha256=f3MayglIgBeBjWeXJU_RgT9cCugcjJ3aEjHqaWPsTbg,26806
39
+ meridian/model/prior_distribution.py,sha256=ZArW4uXIPPQL6hRWiGZUzcHktbkjE_vOklvlbp9LR64,57662
40
+ meridian/model/prior_sampler.py,sha256=iLvCefhA4WY0ENcnLK9471WUZPPyzQ1je58MRjxKv74,25460
41
+ meridian/model/spec.py,sha256=VlK6WJiPo2lzOF0O2judtJ6O3uEw7wYL5AT8bioq4gE,19188
42
+ meridian/model/transformers.py,sha256=HxlVJitxP-wu-NOHU0tArFUZ4NAO3c7adAYj4Zvqnvo,8363
43
+ meridian/model/eda/__init__.py,sha256=bMj9kd2LWU_LQZAjQv54FFggzdv4CKRYblvc-0cHXc4,768
44
+ meridian/model/eda/constants.py,sha256=Kt8x8hvC2WkeEtW0Wmid8GkqZbh_p6NdiZ_A5V3qzwM,1031
45
+ meridian/model/eda/eda_engine.py,sha256=AxvKxdH8Q_TWlGZ58bWcfGMSgxkHZ2wrHloPuzS5C4Y,73324
46
+ meridian/model/eda/eda_outcome.py,sha256=cR9M49e6bDrBNxHOThW3aQlX5gZCOENC7GBljKQx7OY,6475
47
+ meridian/model/eda/eda_spec.py,sha256=diieYyZH0ee3ZLy0rGFMcWrrgiUrz2HctMwOrmtJR6w,2871
48
+ meridian/templates/card.html.jinja,sha256=gaQSHh8xN-LqHzI-xsHaORsaeOD7hnxkq-Fcw-ytZ8M,1284
49
+ meridian/templates/chart.html.jinja,sha256=j8rNLOfXxKx4f5TpP9_W8uCM8zKys8ga75jv-XCny2g,1086
50
+ meridian/templates/chips.html.jinja,sha256=IhPmAI7qZkXj5NeLUKVMCSKFzwv9yR8Qq140t36U10A,1030
51
+ meridian/templates/formatter.py,sha256=75KtZA2y6iyxpkVM3CLmpUtJ1-umBP9xXxdXkkSZcr8,7547
52
+ meridian/templates/formatter_test.py,sha256=nlefFTQaSArNyZWzGUuzCKtt3P3PJNZFmkGm0XMYwBY,8543
53
+ meridian/templates/insights.html.jinja,sha256=IRm49X49HZX-rc6z0uZmpsA_1JLD83UhI2BacOanHLY,606
54
+ meridian/templates/stats.html.jinja,sha256=JMRC_W2PyReU30ISOd8uv2ugDpYmSU3XtPk2OTsaQus,772
55
+ meridian/templates/style.css,sha256=O7YCKVqXgK4Ms3nnGmCVW4hILn4GCBRe4e15XJaP4ww,5492
56
+ meridian/templates/style.scss,sha256=he5jXpGatNfI6vtNbqdENapLiwIlXjqIBkKwxNVEvyw,5076
57
+ meridian/templates/summary.html.jinja,sha256=kR4nQc-oGBwXN6buKcf_wPaoWASomR4fT4gJ1iD9hYw,1775
58
+ meridian/templates/table.html.jinja,sha256=ibgnjdKCjS2qbq61lxqwtlb7JigDrc2m42RDdQvQnKQ,1176
59
+ schema/__init__.py,sha256=grEfsjrcPRzhd89ZXLggnBnrryl97bsayKWP1ue1ffE,1206
60
+ schema/serde/__init__.py,sha256=xyydIcWB5IUpcn3wu1m9HL1fK4gMWURbwTyRsQtolF0,975
61
+ schema/serde/constants.py,sha256=aYtD_RuA0GCkpC4TIQq3VjMqEc837Wn-TlJNm-yn_4Y,1842
62
+ schema/serde/distribution.py,sha256=jy3h6JD1TSs4gwociMis814sz_Fm2kFQ2UbkgjYJW9k,19347
63
+ schema/serde/eda_spec.py,sha256=uOqBeZpUU3Dzzc19rU1LjHWmUhRmVcx8oIZvZfVJHT8,7180
64
+ schema/serde/function_registry.py,sha256=GbgC5_9NDcA9Y7nqmdJ-4-LK5JPhhfI50Lmfy5ZBJOg,4858
65
+ schema/serde/hyperparameters.py,sha256=Igm-PZmIozrsKZH6c-XkrU_Nlf8OAuxpnJJfv7W1SfQ,13524
66
+ schema/serde/inference_data.py,sha256=DrwE9hU8LMrl0z8W_sUSIaPrRdym_lu0iOqpT4KZxsA,3623
67
+ schema/serde/marketing_data.py,sha256=yb-fRTe84Sjg7-v3wsvYRRXvrxLSFWSenO0_ikMvUpk,44845
68
+ schema/serde/meridian_serde.py,sha256=ZG05JaBG4LW8mhl-Cunje9Q6xyR4tyNTtLYedzMBYjA,15985
69
+ schema/serde/serde.py,sha256=8vUqhJxvZgX9UY3rXTyWJznRgapwDzzaHXDHwV_kKTA,1612
70
+ schema/serde/test_data.py,sha256=7hfEWyvZ9WcAkVAOXt6elX8stJlsfhfd-ASlHo9SRb8,107342
71
+ schema/utils/__init__.py,sha256=AkC4NMbmXC3PFBY9dFYxlf3qFsxt5OOBVdc9zmFXsC8,675
72
+ schema/utils/time_record.py,sha256=-KzHFjvSBUUXsfESPAfcJP_VFxaFLqj90Ac0kgKWfpI,4624
73
+ google_meridian-1.3.2.dist-info/METADATA,sha256=BHbc_4zpZ5EBv1urcvQR_brE9WEJaia4F1ZLYaA8sG0,9545
74
+ google_meridian-1.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
+ google_meridian-1.3.2.dist-info/top_level.txt,sha256=yWkWDLV_UUanhKmk_xNPiKNdPDl1oyU1sBYwEnhaSf4,16
76
+ google_meridian-1.3.2.dist-info/RECORD,,
@@ -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
 
@@ -22,9 +22,9 @@ import altair as alt
22
22
  from meridian import backend
23
23
  from meridian import constants as c
24
24
  from meridian.analysis import analyzer
25
- from meridian.analysis import formatter
26
25
  from meridian.analysis import summary_text
27
26
  from meridian.model import model
27
+ from meridian.templates import formatter
28
28
  import numpy as np
29
29
  import pandas as pd
30
30
  import xarray as xr