google-meridian 1.3.2__py3-none-any.whl → 1.5.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.
- {google_meridian-1.3.2.dist-info → google_meridian-1.5.0.dist-info}/METADATA +18 -11
- google_meridian-1.5.0.dist-info/RECORD +112 -0
- {google_meridian-1.3.2.dist-info → google_meridian-1.5.0.dist-info}/WHEEL +1 -1
- {google_meridian-1.3.2.dist-info → google_meridian-1.5.0.dist-info}/top_level.txt +1 -0
- meridian/analysis/analyzer.py +558 -398
- meridian/analysis/optimizer.py +90 -68
- meridian/analysis/review/reviewer.py +4 -1
- meridian/analysis/summarizer.py +13 -3
- meridian/analysis/test_utils.py +2911 -2102
- meridian/analysis/visualizer.py +37 -14
- meridian/backend/__init__.py +106 -0
- meridian/constants.py +2 -0
- meridian/data/input_data.py +30 -52
- meridian/data/input_data_builder.py +2 -9
- meridian/data/test_utils.py +107 -51
- meridian/data/validator.py +48 -0
- meridian/mlflow/autolog.py +19 -9
- meridian/model/__init__.py +2 -0
- meridian/model/adstock_hill.py +3 -5
- meridian/model/context.py +1059 -0
- meridian/model/eda/constants.py +335 -4
- meridian/model/eda/eda_engine.py +723 -312
- meridian/model/eda/eda_outcome.py +177 -33
- meridian/model/equations.py +418 -0
- meridian/model/knots.py +58 -47
- meridian/model/model.py +228 -878
- meridian/model/model_test_data.py +38 -0
- meridian/model/posterior_sampler.py +103 -62
- meridian/model/prior_sampler.py +114 -94
- meridian/model/spec.py +23 -14
- meridian/templates/card.html.jinja +9 -7
- meridian/templates/chart.html.jinja +1 -6
- meridian/templates/finding.html.jinja +19 -0
- meridian/templates/findings.html.jinja +33 -0
- meridian/templates/formatter.py +41 -5
- meridian/templates/formatter_test.py +127 -0
- meridian/templates/style.css +66 -9
- meridian/templates/style.scss +85 -4
- meridian/templates/table.html.jinja +1 -0
- meridian/version.py +1 -1
- scenarioplanner/__init__.py +42 -0
- scenarioplanner/converters/__init__.py +25 -0
- scenarioplanner/converters/dataframe/__init__.py +28 -0
- scenarioplanner/converters/dataframe/budget_opt_converters.py +383 -0
- scenarioplanner/converters/dataframe/common.py +71 -0
- scenarioplanner/converters/dataframe/constants.py +137 -0
- scenarioplanner/converters/dataframe/converter.py +42 -0
- scenarioplanner/converters/dataframe/dataframe_model_converter.py +70 -0
- scenarioplanner/converters/dataframe/marketing_analyses_converters.py +543 -0
- scenarioplanner/converters/dataframe/rf_opt_converters.py +314 -0
- scenarioplanner/converters/mmm.py +743 -0
- scenarioplanner/converters/mmm_converter.py +58 -0
- scenarioplanner/converters/sheets.py +156 -0
- scenarioplanner/converters/test_data.py +714 -0
- scenarioplanner/linkingapi/__init__.py +47 -0
- scenarioplanner/linkingapi/constants.py +27 -0
- scenarioplanner/linkingapi/url_generator.py +131 -0
- scenarioplanner/mmm_ui_proto_generator.py +355 -0
- schema/__init__.py +5 -2
- schema/mmm_proto_generator.py +71 -0
- schema/model_consumer.py +133 -0
- schema/processors/__init__.py +77 -0
- schema/processors/budget_optimization_processor.py +832 -0
- schema/processors/common.py +64 -0
- schema/processors/marketing_processor.py +1137 -0
- schema/processors/model_fit_processor.py +367 -0
- schema/processors/model_kernel_processor.py +117 -0
- schema/processors/model_processor.py +415 -0
- schema/processors/reach_frequency_optimization_processor.py +584 -0
- schema/serde/distribution.py +12 -7
- schema/serde/hyperparameters.py +54 -107
- schema/serde/meridian_serde.py +6 -1
- schema/test_data.py +380 -0
- schema/utils/__init__.py +2 -0
- schema/utils/date_range_bucketing.py +117 -0
- schema/utils/proto_enum_converter.py +127 -0
- google_meridian-1.3.2.dist-info/RECORD +0 -76
- {google_meridian-1.3.2.dist-info → google_meridian-1.5.0.dist-info}/licenses/LICENSE +0 -0
schema/model_consumer.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
"""Consumes a trained Meridian model and produces an `Mmm` proto.
|
|
16
|
+
|
|
17
|
+
The `Mmm` proto contains parts collected from the core model as well as
|
|
18
|
+
analysis results from trained model processors.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from collections.abc import Mapping, Sequence
|
|
22
|
+
import functools
|
|
23
|
+
import inspect
|
|
24
|
+
from typing import Any, Generic, TypeAlias, TypeVar
|
|
25
|
+
|
|
26
|
+
from meridian.model import model
|
|
27
|
+
from mmm.v1 import mmm_pb2 as mmm_pb
|
|
28
|
+
from schema.processors import model_kernel_processor
|
|
29
|
+
from schema.processors import model_processor
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"ModelConsumer",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
SpecType: TypeAlias = type[model_processor.Spec]
|
|
38
|
+
ProcType = TypeVar("ProcType", bound=type[model_processor.ModelProcessor])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ModelConsumer(Generic[ProcType]):
|
|
42
|
+
"""Consumes a trained Meridian model and produces an `Mmm` proto.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
model_processors: A preset list of model processor types.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
model_processors_classes: Sequence[ProcType],
|
|
51
|
+
):
|
|
52
|
+
self._model_processors_classes = model_processors_classes
|
|
53
|
+
|
|
54
|
+
@functools.cached_property
|
|
55
|
+
def specs_to_processors_classes(
|
|
56
|
+
self,
|
|
57
|
+
) -> dict[SpecType, ProcType]:
|
|
58
|
+
"""Returns a mapping of spec types to their corresponding processor types.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ValueError: If multiple model processors are found for the same spec type.
|
|
62
|
+
"""
|
|
63
|
+
specs_to_processors_classes = {}
|
|
64
|
+
for processor_class in self._model_processors_classes:
|
|
65
|
+
if (
|
|
66
|
+
specs_to_processors_classes.get(processor_class.spec_type())
|
|
67
|
+
is not None
|
|
68
|
+
):
|
|
69
|
+
raise ValueError(
|
|
70
|
+
"Multiple model processors found for spec type:"
|
|
71
|
+
f" {processor_class.spec_type()}"
|
|
72
|
+
)
|
|
73
|
+
specs_to_processors_classes[processor_class.spec_type()] = processor_class
|
|
74
|
+
return specs_to_processors_classes
|
|
75
|
+
|
|
76
|
+
def __call__(
|
|
77
|
+
self,
|
|
78
|
+
mmm: model.Meridian,
|
|
79
|
+
specs: Sequence[model_processor.Spec],
|
|
80
|
+
model_id: str = "",
|
|
81
|
+
) -> mmm_pb.Mmm:
|
|
82
|
+
"""Produces an `Mmm` schema for the model along with its analyses results.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
mmm: A trained Meridian model. A trained model has its posterior
|
|
86
|
+
distributions already sampled.
|
|
87
|
+
specs: A sequence of specs that specify the analyses to run on the model.
|
|
88
|
+
Specs of the same type will be grouped together and executed together by
|
|
89
|
+
the corresponding model processor.
|
|
90
|
+
model_id: An optional model identifier.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
A proto containing the model kernel at rest and its analysis results.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
# Group specs by their type.
|
|
97
|
+
specs_by_type = {}
|
|
98
|
+
for spec in specs:
|
|
99
|
+
specs_by_type.setdefault(spec.__class__, []).append(spec)
|
|
100
|
+
|
|
101
|
+
tmodel = model_processor.TrainedModel(mmm)
|
|
102
|
+
processor_params = {
|
|
103
|
+
"trained_model": tmodel,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
output = mmm_pb.Mmm()
|
|
107
|
+
# Attach the model kernel to the Mmm proto.
|
|
108
|
+
model_kernel_processor.ModelKernelProcessor(mmm, model_id)(output)
|
|
109
|
+
|
|
110
|
+
# Perform analysis or optimization.
|
|
111
|
+
for spec_type, specs in specs_by_type.items():
|
|
112
|
+
processor_type = self.specs_to_processors_classes[spec_type]
|
|
113
|
+
processor = _create_processor(processor_type, processor_params)
|
|
114
|
+
# Attach the output of the processor to the output proto.
|
|
115
|
+
processor(specs, output)
|
|
116
|
+
|
|
117
|
+
return output
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _create_processor(
|
|
121
|
+
processor_type: ProcType,
|
|
122
|
+
processor_params: Mapping[str, Any],
|
|
123
|
+
) -> model_processor.ModelProcessor:
|
|
124
|
+
"""Creates a processor of the given type with a subset of the given params."""
|
|
125
|
+
# Clone the given parameters dict first.
|
|
126
|
+
params = dict(processor_params)
|
|
127
|
+
# Remove any parameters that are not in the processor's constructor signature.
|
|
128
|
+
sig = inspect.signature(processor_type.__init__)
|
|
129
|
+
if not any(p.kind == p.VAR_KEYWORD for p in sig.parameters.values()):
|
|
130
|
+
for missing in params.keys() - sig.parameters.keys():
|
|
131
|
+
del params[missing]
|
|
132
|
+
# Finally, construct the concrete processor.
|
|
133
|
+
return processor_type(**params)
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
"""Meridian Model Processor Library.
|
|
16
|
+
|
|
17
|
+
This package provides a collection of processors designed to operate on trained
|
|
18
|
+
Meridian models. These processors facilitate various post-training tasks,
|
|
19
|
+
including model analysis, insight generation, and budget optimization.
|
|
20
|
+
|
|
21
|
+
The processors are built upon a common framework defined in the
|
|
22
|
+
`model_processor` module, which establishes the base classes and interfaces for
|
|
23
|
+
builtin processors in this package, as well as for creating custom processors.
|
|
24
|
+
Each processor typically takes a trained Meridian model object and additional
|
|
25
|
+
specifications as input, producing structured output, in protobuf format.
|
|
26
|
+
|
|
27
|
+
These structured outputs can then be used to generate insights, visualizations,
|
|
28
|
+
and other artifacts that help users understand and optimize their marketing
|
|
29
|
+
strategy. For instance, the `schema.converters` package provides tools to
|
|
30
|
+
flatten these outputs into tabular Google Sheets tables suitable for a Meridian
|
|
31
|
+
Looker Studio dashboard's data sources.
|
|
32
|
+
|
|
33
|
+
Available Processor Modules:
|
|
34
|
+
|
|
35
|
+
- `model_processor`: Defines the abstract base classes `ModelProcessor` and
|
|
36
|
+
`ModelProcessorSpec`, which serve as the foundation for all processors
|
|
37
|
+
in this package.
|
|
38
|
+
- `model_kernel_processor`: A processor to extract and serialize the core
|
|
39
|
+
components and parameters of the trained Meridian model.
|
|
40
|
+
- `model_fit_processor`: Generates various goodness-of-fit statistics and
|
|
41
|
+
diagnostic metrics for the trained model.
|
|
42
|
+
- `marketing_processor`: Performs marketing mix analysis, including
|
|
43
|
+
contribution analysis, response curves, and ROI calculations.
|
|
44
|
+
- `budget_optimization_processor`: Provides tools for optimizing marketing
|
|
45
|
+
budgets based on the model's predictions to achieve specific goals.
|
|
46
|
+
- `reach_frequency_processor`: Analyzes and optimizes based on reach and
|
|
47
|
+
frequency metrics, if applicable to the model structure.
|
|
48
|
+
|
|
49
|
+
Each processor defines its own spec language. For instance, the budget
|
|
50
|
+
optimization processor would take a `BudgetOptimizationSpec` object as input,
|
|
51
|
+
which defines the constraints and parameters of the optimization problem a
|
|
52
|
+
user wants to explore.
|
|
53
|
+
|
|
54
|
+
A trained Meridian model is generally a requisite input for all processors.
|
|
55
|
+
Generally, a `model_processor.TrainedModel` wrapper object is passed to each
|
|
56
|
+
processor, along with its processor-specific spec. For example:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# Assuming 'trained_model' is a loaded Meridian model object
|
|
60
|
+
processor = model_fit_processor.ModelFitProcessor(trained_model)
|
|
61
|
+
result = processor([model_fit_processor.ModelFitSpec()])
|
|
62
|
+
|
|
63
|
+
# `result` is a structured `ModelFit` proto that describes the model's goodness
|
|
64
|
+
# of fit analysis.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For more details on these processors' sub-API, please refer to the documentation
|
|
68
|
+
of the individual modules.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
from schema.processors import budget_optimization_processor
|
|
72
|
+
from schema.processors import common
|
|
73
|
+
from schema.processors import marketing_processor
|
|
74
|
+
from schema.processors import model_fit_processor
|
|
75
|
+
from schema.processors import model_kernel_processor
|
|
76
|
+
from schema.processors import model_processor
|
|
77
|
+
from schema.processors import reach_frequency_optimization_processor
|