google-meridian 1.3.1__py3-none-any.whl → 1.4.0__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 (74) hide show
  1. {google_meridian-1.3.1.dist-info → google_meridian-1.4.0.dist-info}/METADATA +13 -9
  2. google_meridian-1.4.0.dist-info/RECORD +108 -0
  3. {google_meridian-1.3.1.dist-info → google_meridian-1.4.0.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 +8 -3
  11. meridian/analysis/test_utils.py +934 -485
  12. meridian/analysis/visualizer.py +11 -7
  13. meridian/backend/__init__.py +53 -5
  14. meridian/backend/test_utils.py +72 -0
  15. meridian/constants.py +2 -0
  16. meridian/data/load.py +2 -0
  17. meridian/data/test_utils.py +82 -10
  18. meridian/model/__init__.py +2 -0
  19. meridian/model/context.py +925 -0
  20. meridian/model/eda/__init__.py +0 -1
  21. meridian/model/eda/constants.py +13 -2
  22. meridian/model/eda/eda_engine.py +299 -37
  23. meridian/model/eda/eda_outcome.py +21 -1
  24. meridian/model/equations.py +418 -0
  25. meridian/model/knots.py +75 -47
  26. meridian/model/model.py +93 -792
  27. meridian/{analysis/templates → templates}/card.html.jinja +1 -1
  28. meridian/{analysis/templates → templates}/chart.html.jinja +1 -1
  29. meridian/{analysis/templates → templates}/chips.html.jinja +1 -1
  30. meridian/{analysis → templates}/formatter.py +12 -1
  31. meridian/templates/formatter_test.py +216 -0
  32. meridian/{analysis/templates → templates}/insights.html.jinja +1 -1
  33. meridian/{analysis/templates → templates}/stats.html.jinja +1 -1
  34. meridian/{analysis/templates → templates}/style.css +1 -1
  35. meridian/{analysis/templates → templates}/style.scss +1 -1
  36. meridian/{analysis/templates → templates}/summary.html.jinja +4 -2
  37. meridian/{analysis/templates → templates}/table.html.jinja +1 -1
  38. meridian/version.py +1 -1
  39. scenarioplanner/__init__.py +42 -0
  40. scenarioplanner/converters/__init__.py +25 -0
  41. scenarioplanner/converters/dataframe/__init__.py +28 -0
  42. scenarioplanner/converters/dataframe/budget_opt_converters.py +383 -0
  43. scenarioplanner/converters/dataframe/common.py +71 -0
  44. scenarioplanner/converters/dataframe/constants.py +137 -0
  45. scenarioplanner/converters/dataframe/converter.py +42 -0
  46. scenarioplanner/converters/dataframe/dataframe_model_converter.py +70 -0
  47. scenarioplanner/converters/dataframe/marketing_analyses_converters.py +543 -0
  48. scenarioplanner/converters/dataframe/rf_opt_converters.py +314 -0
  49. scenarioplanner/converters/mmm.py +743 -0
  50. scenarioplanner/converters/mmm_converter.py +58 -0
  51. scenarioplanner/converters/sheets.py +156 -0
  52. scenarioplanner/converters/test_data.py +714 -0
  53. scenarioplanner/linkingapi/__init__.py +47 -0
  54. scenarioplanner/linkingapi/constants.py +27 -0
  55. scenarioplanner/linkingapi/url_generator.py +131 -0
  56. scenarioplanner/mmm_ui_proto_generator.py +354 -0
  57. schema/__init__.py +15 -0
  58. schema/mmm_proto_generator.py +71 -0
  59. schema/model_consumer.py +133 -0
  60. schema/processors/__init__.py +77 -0
  61. schema/processors/budget_optimization_processor.py +832 -0
  62. schema/processors/common.py +64 -0
  63. schema/processors/marketing_processor.py +1136 -0
  64. schema/processors/model_fit_processor.py +367 -0
  65. schema/processors/model_kernel_processor.py +117 -0
  66. schema/processors/model_processor.py +412 -0
  67. schema/processors/reach_frequency_optimization_processor.py +584 -0
  68. schema/test_data.py +380 -0
  69. schema/utils/__init__.py +1 -0
  70. schema/utils/date_range_bucketing.py +117 -0
  71. google_meridian-1.3.1.dist-info/RECORD +0 -76
  72. meridian/model/eda/meridian_eda.py +0 -220
  73. {google_meridian-1.3.1.dist-info → google_meridian-1.4.0.dist-info}/WHEEL +0 -0
  74. {google_meridian-1.3.1.dist-info → google_meridian-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,220 +0,0 @@
1
- # Copyright 2025 The Meridian Authors.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- """Module containing Meridian related exploratory data analysis (EDA) functionalities."""
16
- from __future__ import annotations
17
-
18
- from typing import Literal, TYPE_CHECKING, Union
19
-
20
- import altair as alt
21
- from meridian import constants
22
- from meridian.model.eda import constants as eda_constants
23
- import pandas as pd
24
-
25
- if TYPE_CHECKING:
26
- from meridian.model import model # pylint: disable=g-bad-import-order,g-import-not-at-top
27
-
28
- __all__ = [
29
- 'MeridianEDA',
30
- ]
31
-
32
-
33
- class MeridianEDA:
34
- """Class for running pre-modeling exploratory data analysis for Meridian InputData."""
35
-
36
- _PAIRWISE_CORR_COLOR_SCALE = alt.Scale(
37
- domain=[-1.0, 0.0, 1.0],
38
- range=['#1f78b4', '#f7f7f7', '#e34a33'], # Blue-light grey-Orange
39
- type='linear',
40
- )
41
-
42
- def __init__(
43
- self,
44
- meridian: model.Meridian,
45
- ):
46
- self._meridian = meridian
47
-
48
- def generate_and_save_report(self, filename: str, filepath: str):
49
- """Generates and saves the 2 page HTML report containing findings in EDA about given InputData.
50
-
51
- Args:
52
- filename: The filename for the generated HTML output.
53
- filepath: The path to the directory where the file will be saved.
54
- """
55
- # TODO: Implement.
56
- raise NotImplementedError()
57
-
58
- def plot_pairwise_correlation(
59
- self, geos: Union[int, list[str], Literal['nationalize']] = 1
60
- ) -> alt.Chart:
61
- """Plots the Pairwise Correlation data.
62
-
63
- Args:
64
- geos: Defines which geos to plot. - int: The number of top geos to plot,
65
- ranked by population. - list[str]: A specific list of geo names to plot.
66
- - 'nationalize': Aggregates all geos into a single national view.
67
- Defaults to 1 (plotting the top geo). If the data is already at a
68
- national level, this parameter is ignored and a national plot is
69
- generated.
70
-
71
- Returns:
72
- Altair chart(s) of the Pairwise Correlation data.
73
- """
74
- geos_to_plot = self._validate_and_get_geos_to_plot(geos)
75
- is_national = self._meridian.is_national
76
- nationalize_geos = geos == 'nationalize'
77
-
78
- if is_national or nationalize_geos:
79
- pairwise_corr_artifact = (
80
- self._meridian.eda_engine.check_national_pairwise_corr().get_national_artifact
81
- )
82
- if pairwise_corr_artifact is None:
83
- raise ValueError('EDAOutcome does not have national artifact.')
84
- else:
85
- pairwise_corr_artifact = (
86
- self._meridian.eda_engine.check_geo_pairwise_corr().get_geo_artifact
87
- )
88
- if pairwise_corr_artifact is None:
89
- raise ValueError('EDAOutcome does not have geo artifact.')
90
- pairwise_corr_data = pairwise_corr_artifact.corr_matrix.to_dataframe()
91
-
92
- charts = []
93
- for geo_to_plot in geos_to_plot:
94
- title = (
95
- 'Pairwise correlations among all treatments and controls for'
96
- f' {geo_to_plot}'
97
- )
98
-
99
- if not (is_national or nationalize_geos):
100
- plot_data = (
101
- pairwise_corr_data.xs(geo_to_plot, level=constants.GEO)
102
- .rename_axis(
103
- index=[eda_constants.VARIABLE_1, eda_constants.VARIABLE_2]
104
- )
105
- .reset_index()
106
- )
107
- else:
108
- plot_data = pairwise_corr_data.rename_axis(
109
- index=[eda_constants.VARIABLE_1, eda_constants.VARIABLE_2]
110
- ).reset_index()
111
- plot_data.columns = [
112
- eda_constants.VARIABLE_1,
113
- eda_constants.VARIABLE_2,
114
- eda_constants.CORRELATION,
115
- ]
116
- unique_variables = plot_data[eda_constants.VARIABLE_1].unique()
117
- variable_to_index = {name: i for i, name in enumerate(unique_variables)}
118
-
119
- plot_data['idx1'] = plot_data[eda_constants.VARIABLE_1].map(
120
- variable_to_index
121
- )
122
- plot_data['idx2'] = plot_data[eda_constants.VARIABLE_2].map(
123
- variable_to_index
124
- )
125
- lower_triangle_data = plot_data[plot_data['idx2'] > plot_data['idx1']]
126
-
127
- charts.append(
128
- self._plot_2d_heatmap(lower_triangle_data, title, unique_variables)
129
- )
130
- final_chart = (
131
- alt.vconcat(*charts)
132
- .resolve_legend(color='independent')
133
- .configure_axis(labelAngle=315)
134
- .configure_title(anchor='start')
135
- .configure_view(stroke=None)
136
- )
137
- return final_chart
138
-
139
- def _plot_2d_heatmap(
140
- self, data: pd.DataFrame, title: str, unique_variables: list[str]
141
- ) -> alt.Chart:
142
- """Plots a 2D heatmap."""
143
- # Base chart with position encodings
144
- base = (
145
- alt.Chart(data)
146
- .encode(
147
- x=alt.X(
148
- f'{eda_constants.VARIABLE_1}:N',
149
- title=None,
150
- sort=unique_variables,
151
- scale=alt.Scale(domain=unique_variables),
152
- ),
153
- y=alt.Y(
154
- f'{eda_constants.VARIABLE_2}:N',
155
- title=None,
156
- sort=unique_variables,
157
- scale=alt.Scale(domain=unique_variables),
158
- ),
159
- )
160
- .properties(title=title)
161
- )
162
-
163
- # Heatmap layer (rectangles)
164
- heatmap = base.mark_rect().encode(
165
- color=alt.Color(
166
- f'{eda_constants.CORRELATION}:Q',
167
- scale=self._PAIRWISE_CORR_COLOR_SCALE,
168
- legend=alt.Legend(title=eda_constants.CORRELATION),
169
- ),
170
- tooltip=[
171
- eda_constants.VARIABLE_1,
172
- eda_constants.VARIABLE_2,
173
- alt.Tooltip(f'{eda_constants.CORRELATION}:Q', format='.3f'),
174
- ],
175
- )
176
-
177
- # Text annotation layer (values)
178
- text = base.mark_text().encode(
179
- text=alt.Text(f'{eda_constants.CORRELATION}:Q', format='.3f'),
180
- color=alt.value('black'),
181
- )
182
-
183
- # Combine layers and apply final configurations
184
- chart = (heatmap + text).properties(width=350, height=350)
185
-
186
- return chart
187
-
188
- def _generate_pairwise_correlation_report(self) -> str:
189
- """Creates the HTML snippet for Pairwise Correlation report section."""
190
- # TODO: Implement.
191
- raise NotImplementedError()
192
-
193
- def _validate_and_get_geos_to_plot(
194
- self, geos: Union[int, list[str], Literal['nationalize']]
195
- ) -> list[str]:
196
- """Validates and returns the geos to plot."""
197
- ## Validate
198
- is_national = self._meridian.is_national
199
- if is_national or geos == 'nationalize':
200
- geos_to_plot = [constants.NATIONAL_MODEL_DEFAULT_GEO_NAME]
201
- elif isinstance(geos, int):
202
- if geos > len(self._meridian.input_data.geo) or geos <= 0:
203
- raise ValueError(
204
- 'geos must be a positive integer less than or equal to the number'
205
- ' of geos in the data.'
206
- )
207
- geos_to_plot = self._meridian.input_data.get_n_top_largest_geos(geos)
208
- else:
209
- geos_to_plot = geos
210
-
211
- if (
212
- not is_national and geos != 'nationalize'
213
- ): # if national then geos_to_plot will be ignored
214
- for geo in geos_to_plot:
215
- if geo not in self._meridian.input_data.geo:
216
- raise ValueError(f'Geo {geo} does not exist in the data.')
217
- if len(geos_to_plot) != len(set(geos_to_plot)):
218
- raise ValueError('geos must not contain duplicate values.')
219
-
220
- return geos_to_plot