google-meridian 1.1.0__py3-none-any.whl → 1.1.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.
- {google_meridian-1.1.0.dist-info → google_meridian-1.1.2.dist-info}/METADATA +6 -2
- google_meridian-1.1.2.dist-info/RECORD +46 -0
- {google_meridian-1.1.0.dist-info → google_meridian-1.1.2.dist-info}/WHEEL +1 -1
- meridian/__init__.py +2 -2
- meridian/analysis/__init__.py +1 -1
- meridian/analysis/analyzer.py +29 -22
- meridian/analysis/formatter.py +1 -1
- meridian/analysis/optimizer.py +70 -44
- meridian/analysis/summarizer.py +1 -1
- meridian/analysis/summary_text.py +1 -1
- meridian/analysis/test_utils.py +1 -1
- meridian/analysis/visualizer.py +17 -8
- meridian/constants.py +3 -3
- meridian/data/__init__.py +4 -1
- meridian/data/arg_builder.py +1 -1
- meridian/data/data_frame_input_data_builder.py +614 -0
- meridian/data/input_data.py +12 -8
- meridian/data/input_data_builder.py +817 -0
- meridian/data/load.py +121 -428
- meridian/data/nd_array_input_data_builder.py +509 -0
- meridian/data/test_utils.py +60 -43
- meridian/data/time_coordinates.py +1 -1
- meridian/mlflow/__init__.py +17 -0
- meridian/mlflow/autolog.py +54 -0
- meridian/model/__init__.py +1 -1
- meridian/model/adstock_hill.py +1 -1
- meridian/model/knots.py +1 -1
- meridian/model/media.py +1 -1
- meridian/model/model.py +65 -37
- meridian/model/model_test_data.py +75 -1
- meridian/model/posterior_sampler.py +19 -15
- meridian/model/prior_distribution.py +1 -1
- meridian/model/prior_sampler.py +32 -26
- meridian/model/spec.py +18 -8
- meridian/model/transformers.py +1 -1
- google_meridian-1.1.0.dist-info/RECORD +0 -41
- {google_meridian-1.1.0.dist-info → google_meridian-1.1.2.dist-info}/licenses/LICENSE +0 -0
- {google_meridian-1.1.0.dist-info → google_meridian-1.1.2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-meridian
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
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
|
License:
|
|
@@ -222,6 +222,7 @@ Requires-Dist: arviz
|
|
|
222
222
|
Requires-Dist: altair>=5
|
|
223
223
|
Requires-Dist: immutabledict
|
|
224
224
|
Requires-Dist: joblib
|
|
225
|
+
Requires-Dist: natsort<8,>=7.1.1
|
|
225
226
|
Requires-Dist: numpy<3,>=2.0.2
|
|
226
227
|
Requires-Dist: pandas<3,>=2.2.2
|
|
227
228
|
Requires-Dist: scipy<2,>=1.13.1
|
|
@@ -236,8 +237,11 @@ Requires-Dist: pylint>=2.6.0; extra == "dev"
|
|
|
236
237
|
Requires-Dist: pyink; extra == "dev"
|
|
237
238
|
Provides-Extra: colab
|
|
238
239
|
Requires-Dist: psutil; extra == "colab"
|
|
240
|
+
Requires-Dist: python-calamine; extra == "colab"
|
|
239
241
|
Provides-Extra: and-cuda
|
|
240
242
|
Requires-Dist: tensorflow[and-cuda]<2.19,>=2.18; extra == "and-cuda"
|
|
243
|
+
Provides-Extra: mlflow
|
|
244
|
+
Requires-Dist: mlflow; extra == "mlflow"
|
|
241
245
|
Dynamic: license-file
|
|
242
246
|
|
|
243
247
|
# About Meridian
|
|
@@ -393,7 +397,7 @@ To cite this repository:
|
|
|
393
397
|
author = {Google Meridian Marketing Mix Modeling Team},
|
|
394
398
|
title = {Meridian: Marketing Mix Modeling},
|
|
395
399
|
url = {https://github.com/google/meridian},
|
|
396
|
-
version = {1.1.
|
|
400
|
+
version = {1.1.2},
|
|
397
401
|
year = {2025},
|
|
398
402
|
}
|
|
399
403
|
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
google_meridian-1.1.2.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
2
|
+
meridian/__init__.py,sha256=rWkSMlr2TiRmH7Xf9z1Bj3grQiSbmrxl3dtGB9YGn9o,714
|
|
3
|
+
meridian/constants.py,sha256=AWhDEP9VcyQtPCbZhM6cPXHeWuz19wjaqB5lGz6qBsw,17161
|
|
4
|
+
meridian/analysis/__init__.py,sha256=nGBYz7k9FVdadO_WVGMKJcfq7Yy_TuuP8zgee4i9pSA,836
|
|
5
|
+
meridian/analysis/analyzer.py,sha256=AP2YJpM2R2qMJ-rwtMmgu-cM-xJLJCFodSaP9K8f0Do,204458
|
|
6
|
+
meridian/analysis/formatter.py,sha256=ENIdR1CRiaVqIGEXx1HcnsA4ewgDD_nhsYCweJAThaw,7270
|
|
7
|
+
meridian/analysis/optimizer.py,sha256=Se6_sg0O3A4p80vdVnRtDeyNaE5s-ywxKoU0CODQsWM,107608
|
|
8
|
+
meridian/analysis/summarizer.py,sha256=IthOUTMufGvAvbxiDhaKwe7uYCyiTyiQ8vgdmUtdevs,18855
|
|
9
|
+
meridian/analysis/summary_text.py,sha256=I_smDkZJYp2j77ea-9AIbgeraDa7-qUYyb-IthP2qO4,12438
|
|
10
|
+
meridian/analysis/test_utils.py,sha256=ES1r1akhRjD4pf2oTaGqzDfGNu9weAcLv6UZRuIkfEc,77699
|
|
11
|
+
meridian/analysis/visualizer.py,sha256=hVY0JxDZSgK7ekav3jTYBfxXXn-J0g7uQWMtEj3obx4,94512
|
|
12
|
+
meridian/analysis/templates/card.html.jinja,sha256=pv4MVbQ25CcvtZY-LH7bFW0OSeHobkeEkAleB1sfQ14,1284
|
|
13
|
+
meridian/analysis/templates/chart.html.jinja,sha256=87i0xnXHRBoLLxBpKv2i960TLToWq4r1aVQZqaXIeMQ,1086
|
|
14
|
+
meridian/analysis/templates/chips.html.jinja,sha256=Az0tQwF_-b03JDLyOzpeH-8fb-6jgJgbNfnUUSm-q6E,645
|
|
15
|
+
meridian/analysis/templates/insights.html.jinja,sha256=6hEWipbOMiMzs9QGZ6dcB_73tNkj0ZtNiC8E89a98zg,606
|
|
16
|
+
meridian/analysis/templates/stats.html.jinja,sha256=9hQOG02FX1IHVIvdWS_-LI2bbSaqdyHEtCZkiArwAg0,772
|
|
17
|
+
meridian/analysis/templates/style.css,sha256=RODTWc2pXcG9zW3q9SEJpVXgeD-WwQgzLpmFcbXPhLg,5492
|
|
18
|
+
meridian/analysis/templates/style.scss,sha256=nSrZOpcIrVyiL4eC9jLUlxIZtAKZ0Rt8pwfk4H1nMrs,5076
|
|
19
|
+
meridian/analysis/templates/summary.html.jinja,sha256=LuENVDHYIpNo4pzloYaCR2K9XN1Ow6_9oQOcOwD9nGg,1707
|
|
20
|
+
meridian/analysis/templates/table.html.jinja,sha256=mvLMZx92RcD2JAS2w2eZtfYG-6WdfwYVo7pM8TbHp4g,1176
|
|
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=3m6wrcC0psmD2ijsXk3R4uByA0Tu2gJxZBGaTS6Z7Io,22040
|
|
24
|
+
meridian/data/input_data.py,sha256=teJPKTBfW-AzBWgf_fEO_S_Z1J_veqQkCvctINaid6I,39749
|
|
25
|
+
meridian/data/input_data_builder.py,sha256=fFJTmUuIdTnTnZPtZNTiEf4_fsqR_haY7O9ZOFj47bE,25409
|
|
26
|
+
meridian/data/load.py,sha256=cvvesjL6Dc7pYu2nOl558gUOZVAW_B69GirzHocyY3Q,42855
|
|
27
|
+
meridian/data/nd_array_input_data_builder.py,sha256=lfpmnENGuSGKyUd7bDGAwoLqHqteOKmHdKl0VI2wCQA,16341
|
|
28
|
+
meridian/data/test_utils.py,sha256=6GJrPmeaF4uzMxxRgzERGv4g1XMUHwI0s7qDVMZUjuI,55565
|
|
29
|
+
meridian/data/time_coordinates.py,sha256=C5A5fscSLjPH6G9YT8OspgIlCrkMY7y8dMFEt3tNSnE,9874
|
|
30
|
+
meridian/mlflow/__init__.py,sha256=elwXUqPQYi7VF9PYjelU1tydfcUrmtuoq6eJCOnV9bk,693
|
|
31
|
+
meridian/mlflow/autolog.py,sha256=Duubd_Z2Exlk_MJqGTfMIfFjaDUqG_YnsRGjzY4Hn84,1696
|
|
32
|
+
meridian/model/__init__.py,sha256=9NFfqUE5WgFc-9lQMkbfkwwV-bQIz0tsQ_3Jyq0A4SU,982
|
|
33
|
+
meridian/model/adstock_hill.py,sha256=20A_6rbDUAADEkkHspB7JpCm5tYfYS1FQ6hJMLu21Pk,9283
|
|
34
|
+
meridian/model/knots.py,sha256=KPEgnb-UdQQ4QBugOYEke-zBgEghgTmeCMoeiJ30meY,8054
|
|
35
|
+
meridian/model/media.py,sha256=R0LnMUNTuGzXD2lzNRRORA4-p21xpdhkVVsvFaWtEK0,13819
|
|
36
|
+
meridian/model/model.py,sha256=KM2EU7eAK5UHDAn1jbUEI_SBrDkz-Bc93R8qRBEiic8,61500
|
|
37
|
+
meridian/model/model_test_data.py,sha256=hDDTEzm72LknW9c5E_dNsy4Mm4Tfs6AirhGf_QxykFs,15552
|
|
38
|
+
meridian/model/posterior_sampler.py,sha256=jjLqcYEAorVJ_2nmhpkVUjCGAyNUZYPTEXVTDHufbqA,27727
|
|
39
|
+
meridian/model/prior_distribution.py,sha256=IEDU1rabcmKNY8lxwbbO4OUAlMHPIMa7flM_zsu3DLM,42417
|
|
40
|
+
meridian/model/prior_sampler.py,sha256=jSaxFmJzyN2OKqKyU059Ar4Yr565w4zlInPl4zxjGZk,23212
|
|
41
|
+
meridian/model/spec.py,sha256=0HNiMQUWQpYvWYOZr1_fj2ah8tH-bEyfEjoqgBZ9Lc0,18049
|
|
42
|
+
meridian/model/transformers.py,sha256=nRjzq1fQG0ypldxboM7Gqok6WSAXAS1witRXoAzeH9Q,7763
|
|
43
|
+
google_meridian-1.1.2.dist-info/METADATA,sha256=qoLl6RDBz8LxrnJ3c4-hDiIMcL5OC8G8X61rotn6PGs,22201
|
|
44
|
+
google_meridian-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
google_meridian-1.1.2.dist-info/top_level.txt,sha256=nwaCebZvvU34EopTKZsjK0OMTFjVnkf4FfnBN_TAc0g,9
|
|
46
|
+
google_meridian-1.1.2.dist-info/RECORD,,
|
meridian/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"""Meridian API."""
|
|
16
16
|
|
|
17
|
-
__version__ = "1.1.
|
|
17
|
+
__version__ = "1.1.2"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
from meridian import analysis
|
meridian/analysis/__init__.py
CHANGED
meridian/analysis/analyzer.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -788,7 +788,7 @@ class Analyzer:
|
|
|
788
788
|
tensors are expected to be scaled by their corresponding transformers.
|
|
789
789
|
dist_tensors: A `DistributionTensors` container with the distribution
|
|
790
790
|
tensors for media, RF, organic media, organic RF, non-media treatments,
|
|
791
|
-
and controls.
|
|
791
|
+
and controls (if available).
|
|
792
792
|
|
|
793
793
|
Returns:
|
|
794
794
|
Tensor representing computed kpi means.
|
|
@@ -803,17 +803,15 @@ class Analyzer:
|
|
|
803
803
|
)
|
|
804
804
|
)
|
|
805
805
|
|
|
806
|
-
result = (
|
|
807
|
-
|
|
808
|
-
+ tf.einsum(
|
|
809
|
-
"...gtm,...gm->...gt", combined_media_transformed, combined_beta
|
|
810
|
-
)
|
|
811
|
-
+ tf.einsum(
|
|
812
|
-
"...gtc,...gc->...gt",
|
|
813
|
-
data_tensors.controls,
|
|
814
|
-
dist_tensors.gamma_gc,
|
|
815
|
-
)
|
|
806
|
+
result = tau_gt + tf.einsum(
|
|
807
|
+
"...gtm,...gm->...gt", combined_media_transformed, combined_beta
|
|
816
808
|
)
|
|
809
|
+
if self._meridian.controls is not None:
|
|
810
|
+
result += tf.einsum(
|
|
811
|
+
"...gtc,...gc->...gt",
|
|
812
|
+
data_tensors.controls,
|
|
813
|
+
dist_tensors.gamma_gc,
|
|
814
|
+
)
|
|
817
815
|
if data_tensors.non_media_treatments is not None:
|
|
818
816
|
result += tf.einsum(
|
|
819
817
|
"...gtm,...gm->...gt",
|
|
@@ -1464,11 +1462,14 @@ class Analyzer:
|
|
|
1464
1462
|
(n_chains, 0, self._meridian.n_geos, self._meridian.n_times)
|
|
1465
1463
|
)
|
|
1466
1464
|
batch_starting_indices = np.arange(n_draws, step=batch_size)
|
|
1467
|
-
param_list =
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1465
|
+
param_list = (
|
|
1466
|
+
[
|
|
1467
|
+
constants.MU_T,
|
|
1468
|
+
constants.TAU_G,
|
|
1469
|
+
]
|
|
1470
|
+
+ ([constants.GAMMA_GC] if self._meridian.n_controls else [])
|
|
1471
|
+
+ self._get_causal_param_names(include_non_paid_channels=True)
|
|
1472
|
+
)
|
|
1472
1473
|
outcome_means_temps = []
|
|
1473
1474
|
for start_index in batch_starting_indices:
|
|
1474
1475
|
stop_index = np.min([n_draws, start_index + batch_size])
|
|
@@ -3960,11 +3961,17 @@ class Analyzer:
|
|
|
3960
3961
|
) -> xr.Dataset:
|
|
3961
3962
|
"""Method to generate a response curves xarray.Dataset.
|
|
3962
3963
|
|
|
3963
|
-
Response curves are calculated
|
|
3964
|
-
historical flighting pattern across geos and time periods for
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3964
|
+
Response curves are calculated in aggregate across geos and time periods,
|
|
3965
|
+
assuming the historical flighting pattern across geos and time periods for
|
|
3966
|
+
each media channel.
|
|
3967
|
+
|
|
3968
|
+
A list of multipliers is applied to each media channel's total historical
|
|
3969
|
+
spend within `selected_geos` and `selected_times` to obtain the x-axis
|
|
3970
|
+
values. The y-axis values are the incremental ouctcome generated by each
|
|
3971
|
+
channel within `selected_geos` and `selected_times` under the counterfactual
|
|
3972
|
+
where media units in each geo and time period are scaled by the
|
|
3973
|
+
corresponding multiplier. (Media units for time periods prior to
|
|
3974
|
+
`selected_times` are also scaled by the multiplier.)
|
|
3968
3975
|
|
|
3969
3976
|
Args:
|
|
3970
3977
|
spend_multipliers: List of multipliers. Each channel's total spend is
|
meridian/analysis/formatter.py
CHANGED
meridian/analysis/optimizer.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -223,7 +223,7 @@ class OptimizationGrid:
|
|
|
223
223
|
if spend_constraint_upper is None:
|
|
224
224
|
spend_constraint_upper = spend_constraint_default
|
|
225
225
|
(optimization_lower_bound, optimization_upper_bound) = (
|
|
226
|
-
|
|
226
|
+
get_optimization_bounds(
|
|
227
227
|
n_channels=len(self.channels),
|
|
228
228
|
spend=spend,
|
|
229
229
|
round_factor=self.round_factor,
|
|
@@ -1307,36 +1307,57 @@ class BudgetOptimizer:
|
|
|
1307
1307
|
) -> OptimizationResults:
|
|
1308
1308
|
"""Finds the optimal budget allocation that maximizes outcome.
|
|
1309
1309
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
`
|
|
1320
|
-
|
|
1321
|
-
`
|
|
1322
|
-
media
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1310
|
+
Define B to be the historical spend of a channel within `selected_geos` and
|
|
1311
|
+
between `start_date` and `end_date`. When the optimization assigns a new
|
|
1312
|
+
budget N to this channel, the historical media units for each geo and time
|
|
1313
|
+
period are assumed to scale by the ratio N / B. Media units prior to
|
|
1314
|
+
`selected_times` are also scaled by N / B. The incremental outcome of each
|
|
1315
|
+
channel is aggregated over `selected_geos` and between `start_date` and
|
|
1316
|
+
`end_date`.
|
|
1317
|
+
|
|
1318
|
+
The incremental outcome includes the (lagged) amount generated between
|
|
1319
|
+
`start_date` and `end_date` by media executed prior to `start_date`, but it
|
|
1320
|
+
excludes the (lagged) amount generated after `end_date` by media executed
|
|
1321
|
+
between `start_date` and `end_date`. This definition does not require any
|
|
1322
|
+
assumptions about media execution levels, media costs, or revenue per kpi
|
|
1323
|
+
for time periods after `end_date`.
|
|
1324
|
+
|
|
1325
|
+
These assumptions are equivalent to assuming that for each channel, neither
|
|
1326
|
+
the flighting pattern nor the cost per media unit depend on the overall
|
|
1327
|
+
budget assigned to that channel.
|
|
1328
|
+
|
|
1329
|
+
The following optimization parameters are assigned default values based on
|
|
1330
|
+
the model input data:
|
|
1331
|
+
1. Flighting pattern. This is the relative allocation of a channel's media
|
|
1332
|
+
units across geos and time periods. By default, the historical flighting
|
|
1333
|
+
pattern is used. The default can be overridden by passing
|
|
1334
|
+
`new_data.media`. The flighting pattern is held constant during
|
|
1335
|
+
optimization and does not depend on the overall budget assigned to the
|
|
1336
|
+
channel.
|
|
1337
|
+
2. Cost per media unit. By default, the historical spend divided by
|
|
1338
|
+
historical media units is used. This can optionally vary by geo or time
|
|
1339
|
+
period or both depending on whether the spend data has geo and time
|
|
1340
|
+
dimensions. The default can be overridden by passing `new_data.spend`.
|
|
1341
|
+
The cost per media unit is held constant during optimization and does not
|
|
1342
|
+
depend on the overall budget assigned to the channel.
|
|
1343
|
+
3. Center of the spend box constraint for each channel. By default, the
|
|
1344
|
+
historical percentage of spend within `selected_geos` and between
|
|
1345
|
+
`start_date` and `end_date` is used. This can be overridden by passing
|
|
1346
|
+
`pct_of_spend`.
|
|
1347
|
+
4. Total budget to be allocated (for fixed budget scenarios only). By
|
|
1348
|
+
default, the historical spend within `selected_geos` and between
|
|
1349
|
+
`start_date` and `end_date` is used. This can be overridden by passing
|
|
1350
|
+
`budget`.
|
|
1351
|
+
|
|
1352
|
+
Passing `new_data.media` (or `new_data.reach` or `new_data.frequency`) will
|
|
1353
|
+
override both the flighting pattern and cost per media unit. Passing
|
|
1354
|
+
`new_data.spend` (or `new_data.rf_spend) will only override the cost per
|
|
1355
|
+
media unit.
|
|
1356
|
+
|
|
1357
|
+
If `start_date` or `end_date` is specified, these values must be selected
|
|
1358
|
+
from `new_data.time` (if provided) or from `Meridian.n_times` (if
|
|
1359
|
+
`new_data.time` is not provided). The `start_date` and `end_date` default to
|
|
1360
|
+
the first and last time periods, respectively.
|
|
1340
1361
|
|
|
1341
1362
|
Args:
|
|
1342
1363
|
new_data: An optional `DataTensors` container with optional tensors:
|
|
@@ -1355,9 +1376,13 @@ class BudgetOptimizer:
|
|
|
1355
1376
|
dimension coordinates for the duration to run the optimization on.
|
|
1356
1377
|
Please Use `start_date` and `end_date` instead.
|
|
1357
1378
|
start_date: Optional start date selector, *inclusive*, in _yyyy-mm-dd_
|
|
1358
|
-
format. Default is
|
|
1379
|
+
format. Default is the first time period of `Meridian.InputData.time` if
|
|
1380
|
+
`new_data` is not provided; otherwise it is the first time period of
|
|
1381
|
+
`new_data.time`.
|
|
1359
1382
|
end_date: Optional end date selector, *inclusive* in _yyyy-mm-dd_ format.
|
|
1360
|
-
Default is
|
|
1383
|
+
Default is the last time period of `Meridian.InputData.time` if
|
|
1384
|
+
`new_data` is not provided; otherwise it is the last time period of
|
|
1385
|
+
`new_data.time`.
|
|
1361
1386
|
fixed_budget: Boolean indicating whether it's a fixed budget optimization
|
|
1362
1387
|
or flexible budget optimization. Defaults to `True`. If `False`, must
|
|
1363
1388
|
specify either `target_roi` or `target_mroi`.
|
|
@@ -1664,7 +1689,7 @@ class BudgetOptimizer:
|
|
|
1664
1689
|
)
|
|
1665
1690
|
spend = budget * valid_pct_of_spend
|
|
1666
1691
|
(optimization_lower_bound, optimization_upper_bound) = (
|
|
1667
|
-
|
|
1692
|
+
get_optimization_bounds(
|
|
1668
1693
|
n_channels=n_channels,
|
|
1669
1694
|
spend=spend,
|
|
1670
1695
|
round_factor=optimization_grid.round_factor,
|
|
@@ -1829,7 +1854,7 @@ class BudgetOptimizer:
|
|
|
1829
1854
|
spend = budget * valid_pct_of_spend
|
|
1830
1855
|
round_factor = _get_round_factor(budget, gtol)
|
|
1831
1856
|
(optimization_lower_bound, optimization_upper_bound) = (
|
|
1832
|
-
|
|
1857
|
+
get_optimization_bounds(
|
|
1833
1858
|
n_channels=n_paid_channels,
|
|
1834
1859
|
spend=spend,
|
|
1835
1860
|
round_factor=round_factor,
|
|
@@ -2059,17 +2084,17 @@ class BudgetOptimizer:
|
|
|
2059
2084
|
c.PAID_DATA + (c.TIME,),
|
|
2060
2085
|
self._meridian,
|
|
2061
2086
|
)
|
|
2062
|
-
|
|
2087
|
+
spend_tensor = tf.convert_to_tensor(spend, dtype=tf.float32)
|
|
2063
2088
|
hist_spend = tf.convert_to_tensor(hist_spend, dtype=tf.float32)
|
|
2064
2089
|
(new_media, new_media_spend, new_reach, new_frequency, new_rf_spend) = (
|
|
2065
2090
|
self._get_incremental_outcome_tensors(
|
|
2066
2091
|
hist_spend,
|
|
2067
|
-
|
|
2092
|
+
spend_tensor,
|
|
2068
2093
|
new_data=filled_data.filter_fields(c.PAID_CHANNELS),
|
|
2069
2094
|
optimal_frequency=optimal_frequency,
|
|
2070
2095
|
)
|
|
2071
2096
|
)
|
|
2072
|
-
budget = np.sum(
|
|
2097
|
+
budget = np.sum(spend_tensor)
|
|
2073
2098
|
|
|
2074
2099
|
# incremental_outcome here is a tensor with the shape
|
|
2075
2100
|
# (n_chains, n_draws, n_channels)
|
|
@@ -2123,7 +2148,7 @@ class BudgetOptimizer:
|
|
|
2123
2148
|
)
|
|
2124
2149
|
|
|
2125
2150
|
roi = analyzer.get_central_tendency_and_ci(
|
|
2126
|
-
data=tf.math.divide_no_nan(incremental_outcome,
|
|
2151
|
+
data=tf.math.divide_no_nan(incremental_outcome, spend_tensor),
|
|
2127
2152
|
confidence_level=confidence_level,
|
|
2128
2153
|
include_median=True,
|
|
2129
2154
|
)
|
|
@@ -2148,7 +2173,7 @@ class BudgetOptimizer:
|
|
|
2148
2173
|
)
|
|
2149
2174
|
|
|
2150
2175
|
cpik = analyzer.get_central_tendency_and_ci(
|
|
2151
|
-
data=tf.math.divide_no_nan(
|
|
2176
|
+
data=tf.math.divide_no_nan(spend_tensor, incremental_outcome),
|
|
2152
2177
|
confidence_level=confidence_level,
|
|
2153
2178
|
include_median=True,
|
|
2154
2179
|
)
|
|
@@ -2159,9 +2184,10 @@ class BudgetOptimizer:
|
|
|
2159
2184
|
)
|
|
2160
2185
|
|
|
2161
2186
|
total_spend = np.sum(spend) if np.sum(spend) > 0 else 1
|
|
2187
|
+
pct_of_spend = spend / total_spend
|
|
2162
2188
|
data_vars = {
|
|
2163
|
-
c.SPEND: ([c.CHANNEL], spend),
|
|
2164
|
-
c.PCT_OF_SPEND: ([c.CHANNEL],
|
|
2189
|
+
c.SPEND: ([c.CHANNEL], spend.data),
|
|
2190
|
+
c.PCT_OF_SPEND: ([c.CHANNEL], pct_of_spend.data),
|
|
2165
2191
|
c.INCREMENTAL_OUTCOME: (
|
|
2166
2192
|
[c.CHANNEL, c.METRIC],
|
|
2167
2193
|
incremental_outcome_with_mean_median_and_ci,
|
|
@@ -2510,7 +2536,7 @@ def _get_spend_bounds(
|
|
|
2510
2536
|
return spend_bounds
|
|
2511
2537
|
|
|
2512
2538
|
|
|
2513
|
-
def
|
|
2539
|
+
def get_optimization_bounds(
|
|
2514
2540
|
n_channels: int,
|
|
2515
2541
|
spend: np.ndarray,
|
|
2516
2542
|
round_factor: int,
|
meridian/analysis/summarizer.py
CHANGED
meridian/analysis/test_utils.py
CHANGED
meridian/analysis/visualizer.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -876,9 +876,14 @@ class MediaEffects:
|
|
|
876
876
|
Args:
|
|
877
877
|
confidence_level: Confidence level for modeled response credible
|
|
878
878
|
intervals, represented as a value between zero and one. Default is 0.9.
|
|
879
|
-
selected_times: Optional list
|
|
880
|
-
|
|
881
|
-
|
|
879
|
+
selected_times: Optional list containing a subset of time dimensions to
|
|
880
|
+
include. The x-axis corresponds to spend within these time periods. The
|
|
881
|
+
y-axis corresponds to the incremental outcome generated within these
|
|
882
|
+
time periods under the counterfactual where media units in each geo and
|
|
883
|
+
time period are scaled by the ratio of x-axis spend to historical spend.
|
|
884
|
+
(Media units for time periods prior to to `selected_times` are also
|
|
885
|
+
scaled by this ratio). By default, all times are included. Times should
|
|
886
|
+
match the time dimensions from `meridian.InputData`.
|
|
882
887
|
by_reach: For the channel w/ reach and frequency, return the response
|
|
883
888
|
curves by reach given fixed frequency if true; return the response
|
|
884
889
|
curves by frequency given fixed reach if false.
|
|
@@ -972,8 +977,13 @@ class MediaEffects:
|
|
|
972
977
|
Args:
|
|
973
978
|
confidence_level: Confidence level to update to for the response curve
|
|
974
979
|
credible intervals, represented as a value between zero and one.
|
|
975
|
-
selected_times: Optional list containing a subset of
|
|
976
|
-
|
|
980
|
+
selected_times: Optional list containing a subset of time dimensions to
|
|
981
|
+
include. The x-axis corresponds to spend within these time periods. The
|
|
982
|
+
y-axis corresponds to the incremental outcome generated within these
|
|
983
|
+
time periods under the counterfactual where media units in each geo and
|
|
984
|
+
time period are multiplied by the corresponding multiplier (including
|
|
985
|
+
time periods prior to to `selected_times`). By default, all time periods
|
|
986
|
+
are included.
|
|
977
987
|
by_reach: For the channel w/ reach and frequency, return the response
|
|
978
988
|
curves by reach given fixed frequency if true; return the response
|
|
979
989
|
curves by frequency given fixed reach if false.
|
|
@@ -1493,8 +1503,7 @@ class MediaSummary:
|
|
|
1493
1503
|
Returns:
|
|
1494
1504
|
An `xarray.Dataset` containing the following:
|
|
1495
1505
|
- **Coordinates:** `channel`, `metric` (`mean`, `median`, `ci_lo`,
|
|
1496
|
-
|
|
1497
|
-
`distribution` (`prior`, `posterior`)
|
|
1506
|
+
`ci_hi`), `distribution` (`prior`, `posterior`)
|
|
1498
1507
|
- **Data variables:** `incremental_outcome`, `pct_of_contribution`,
|
|
1499
1508
|
`effectiveness`.
|
|
1500
1509
|
"""
|
meridian/constants.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -72,10 +72,10 @@ REVENUE = 'revenue'
|
|
|
72
72
|
NON_REVENUE = 'non_revenue'
|
|
73
73
|
REQUIRED_INPUT_DATA_ARRAY_NAMES = (
|
|
74
74
|
KPI,
|
|
75
|
-
CONTROLS,
|
|
76
75
|
POPULATION,
|
|
77
76
|
)
|
|
78
77
|
OPTIONAL_INPUT_DATA_ARRAY_NAMES = (
|
|
78
|
+
CONTROLS,
|
|
79
79
|
REVENUE_PER_KPI,
|
|
80
80
|
ORGANIC_MEDIA,
|
|
81
81
|
ORGANIC_REACH,
|
|
@@ -148,7 +148,6 @@ REQUIRED_INPUT_DATA_COORD_NAMES = (
|
|
|
148
148
|
GEO,
|
|
149
149
|
TIME,
|
|
150
150
|
MEDIA_TIME,
|
|
151
|
-
CONTROL_VARIABLE,
|
|
152
151
|
)
|
|
153
152
|
NON_PAID_MEDIA_INPUT_DATA_COORD_NAMES = (
|
|
154
153
|
ORGANIC_MEDIA_CHANNEL,
|
|
@@ -159,6 +158,7 @@ MEDIA_INPUT_DATA_COORD_NAMES = (MEDIA_CHANNEL,)
|
|
|
159
158
|
RF_INPUT_DATA_COORD_NAMES = (RF_CHANNEL,)
|
|
160
159
|
POSSIBLE_INPUT_DATA_COORD_NAMES = (
|
|
161
160
|
REQUIRED_INPUT_DATA_COORD_NAMES
|
|
161
|
+
+ (CONTROL_VARIABLE,)
|
|
162
162
|
+ NON_PAID_MEDIA_INPUT_DATA_COORD_NAMES
|
|
163
163
|
+ MEDIA_INPUT_DATA_COORD_NAMES
|
|
164
164
|
+ RF_INPUT_DATA_COORD_NAMES
|
meridian/data/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"""Data handling API for Meridian."""
|
|
16
16
|
|
|
17
17
|
from meridian.data import arg_builder
|
|
18
|
+
from meridian.data import data_frame_input_data_builder
|
|
18
19
|
from meridian.data import input_data
|
|
20
|
+
from meridian.data import input_data_builder
|
|
19
21
|
from meridian.data import load
|
|
22
|
+
from meridian.data import nd_array_input_data_builder
|
|
20
23
|
from meridian.data import time_coordinates
|
meridian/data/arg_builder.py
CHANGED