google-meridian 1.0.7__py3-none-any.whl → 1.0.9__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.0.7.dist-info → google_meridian-1.0.9.dist-info}/METADATA +2 -2
- {google_meridian-1.0.7.dist-info → google_meridian-1.0.9.dist-info}/RECORD +18 -18
- {google_meridian-1.0.7.dist-info → google_meridian-1.0.9.dist-info}/WHEEL +1 -1
- meridian/__init__.py +1 -1
- meridian/analysis/analyzer.py +491 -338
- meridian/analysis/optimizer.py +712 -299
- meridian/analysis/summarizer.py +40 -4
- meridian/analysis/summary_text.py +20 -1
- meridian/analysis/templates/chart.html.jinja +1 -0
- meridian/analysis/test_utils.py +47 -99
- meridian/analysis/visualizer.py +455 -85
- meridian/constants.py +32 -0
- meridian/data/input_data.py +49 -5
- meridian/model/model.py +20 -4
- meridian/model/posterior_sampler.py +15 -5
- meridian/model/prior_distribution.py +22 -1
- {google_meridian-1.0.7.dist-info → google_meridian-1.0.9.dist-info}/licenses/LICENSE +0 -0
- {google_meridian-1.0.7.dist-info → google_meridian-1.0.9.dist-info}/top_level.txt +0 -0
meridian/analysis/summarizer.py
CHANGED
|
@@ -167,7 +167,9 @@ class Summarizer:
|
|
|
167
167
|
self._create_model_fit_card_html(
|
|
168
168
|
template_env, selected_times=selected_times
|
|
169
169
|
),
|
|
170
|
-
self._create_outcome_contrib_card_html(
|
|
170
|
+
self._create_outcome_contrib_card_html(
|
|
171
|
+
template_env, media_summary, selected_times=selected_times
|
|
172
|
+
),
|
|
171
173
|
self._create_performance_breakdown_card_html(
|
|
172
174
|
template_env, media_summary
|
|
173
175
|
),
|
|
@@ -267,9 +269,41 @@ class Summarizer:
|
|
|
267
269
|
self,
|
|
268
270
|
template_env: jinja2.Environment,
|
|
269
271
|
media_summary: visualizer.MediaSummary,
|
|
272
|
+
selected_times: Sequence[str] | None,
|
|
270
273
|
) -> str:
|
|
271
274
|
"""Creates the HTML snippet for the Outcome Contrib card."""
|
|
272
275
|
outcome = self._kpi_or_revenue()
|
|
276
|
+
|
|
277
|
+
num_selected_times = (
|
|
278
|
+
self._meridian.n_times
|
|
279
|
+
if selected_times is None
|
|
280
|
+
else len(selected_times)
|
|
281
|
+
)
|
|
282
|
+
time_granularity = (
|
|
283
|
+
c.WEEKLY
|
|
284
|
+
if num_selected_times < c.QUARTERLY_SUMMARY_THRESHOLD_WEEKS
|
|
285
|
+
else c.QUARTERLY
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
channel_contrib_area_chart = formatter.ChartSpec(
|
|
289
|
+
id=summary_text.CHANNEL_CONTRIB_BY_TIME_CHART_ID,
|
|
290
|
+
description=summary_text.CHANNEL_CONTRIB_BY_TIME_CHART_DESCRIPTION.format(
|
|
291
|
+
outcome=outcome
|
|
292
|
+
),
|
|
293
|
+
chart_json=media_summary.plot_channel_contribution_area_chart(
|
|
294
|
+
time_granularity=time_granularity
|
|
295
|
+
).to_json(),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
channel_contrib_bump_chart = formatter.ChartSpec(
|
|
299
|
+
id=summary_text.CHANNEL_CONTRIB_RANK_CHART_ID,
|
|
300
|
+
description=summary_text.CHANNEL_CONTRIB_RANK_CHART_DESCRIPTION.format(
|
|
301
|
+
outcome=outcome
|
|
302
|
+
),
|
|
303
|
+
chart_json=media_summary.plot_channel_contribution_bump_chart(
|
|
304
|
+
time_granularity=time_granularity
|
|
305
|
+
).to_json(),
|
|
306
|
+
)
|
|
273
307
|
channel_drivers_chart = formatter.ChartSpec(
|
|
274
308
|
id=summary_text.CHANNEL_DRIVERS_CHART_ID,
|
|
275
309
|
description=summary_text.CHANNEL_DRIVERS_CHART_DESCRIPTION.format(
|
|
@@ -308,6 +342,8 @@ class Summarizer:
|
|
|
308
342
|
channel_drivers_chart,
|
|
309
343
|
spend_outcome_chart,
|
|
310
344
|
outcome_contribution_chart,
|
|
345
|
+
channel_contrib_area_chart,
|
|
346
|
+
channel_contrib_bump_chart,
|
|
311
347
|
],
|
|
312
348
|
)
|
|
313
349
|
|
|
@@ -318,7 +354,7 @@ class Summarizer:
|
|
|
318
354
|
ascending: bool = False,
|
|
319
355
|
) -> pd.DataFrame:
|
|
320
356
|
return (
|
|
321
|
-
media_summary.
|
|
357
|
+
media_summary.get_paid_summary_metrics()[metrics]
|
|
322
358
|
.sel(distribution=c.POSTERIOR, metric=c.MEAN)
|
|
323
359
|
.drop_sel(channel=c.ALL_CHANNELS)
|
|
324
360
|
.to_dataframe()
|
|
@@ -334,7 +370,7 @@ class Summarizer:
|
|
|
334
370
|
ascending: bool = False,
|
|
335
371
|
) -> pd.DataFrame:
|
|
336
372
|
return (
|
|
337
|
-
media_summary.
|
|
373
|
+
media_summary.get_paid_summary_metrics()[metrics]
|
|
338
374
|
.sel(distribution=c.POSTERIOR, metric=c.MEDIAN)
|
|
339
375
|
.drop_sel(channel=c.ALL_CHANNELS)
|
|
340
376
|
.to_dataframe()
|
|
@@ -479,7 +515,7 @@ class Summarizer:
|
|
|
479
515
|
rf_channels = reach_frequency.optimal_frequency_data.rf_channel
|
|
480
516
|
assert rf_channels.size > 0
|
|
481
517
|
# This will raise KeyError if not all `rf_channels` can be found in here:
|
|
482
|
-
rf_channel_spends = media_summary.
|
|
518
|
+
rf_channel_spends = media_summary.get_paid_summary_metrics()[c.SPEND].sel(
|
|
483
519
|
channel=rf_channels
|
|
484
520
|
)
|
|
485
521
|
most_spend_rf_channel = rf_channel_spends.idxmax()
|
|
@@ -20,7 +20,9 @@ MODEL_RESULTS_TITLE = 'Marketing Mix Modeling Report'
|
|
|
20
20
|
MODEL_FIT_CARD_ID = 'model-fit'
|
|
21
21
|
MODEL_FIT_CARD_TITLE = 'Model fit'
|
|
22
22
|
MODEL_FIT_INSIGHTS_FORMAT = """Model fit is a measure of how well your MMM fits
|
|
23
|
-
|
|
23
|
+
the data used to train the model. The best model for causal inference may differ
|
|
24
|
+
from the best fitting model, because causal inference models must also estimate
|
|
25
|
+
the unobserved baseline."""
|
|
24
26
|
|
|
25
27
|
EXPECTED_ACTUAL_OUTCOME_CHART_ID = 'expected-actual-outcome-chart'
|
|
26
28
|
EXPECTED_ACTUAL_OUTCOME_CHART_TITLE = 'Expected {outcome} vs. actual {outcome}'
|
|
@@ -42,6 +44,23 @@ CHANNEL_CONTRIB_INSIGHTS_FORMAT = """Your channel contributions help you
|
|
|
42
44
|
understand what drove your {outcome}. {lead_channels} drove the most overall
|
|
43
45
|
{outcome}."""
|
|
44
46
|
|
|
47
|
+
CHANNEL_CONTRIB_BY_TIME_CHART_ID = 'channel-contrib-by-time-chart'
|
|
48
|
+
CHANNEL_CONTRIB_BY_TIME_CHART_TITLE = (
|
|
49
|
+
'Contribution over time by baseline and marketing channels'
|
|
50
|
+
)
|
|
51
|
+
CHANNEL_CONTRIB_BY_TIME_CHART_DESCRIPTION = """Note: This chart shows the
|
|
52
|
+
estimated incremental {outcome} attributed to each channel and the baseline over
|
|
53
|
+
the selected time period. It helps visualize how contributions have changed."""
|
|
54
|
+
|
|
55
|
+
CHANNEL_CONTRIB_RANK_CHART_ID = 'channel-contrib-rank-chart'
|
|
56
|
+
CHANNEL_CONTRIB_RANK_CHART_TITLE = (
|
|
57
|
+
'Contribution rank over time by baseline and marketing channels'
|
|
58
|
+
)
|
|
59
|
+
CHANNEL_CONTRIB_RANK_CHART_DESCRIPTION = """Note: This chart shows the relative
|
|
60
|
+
rank of each channel's contribution, including the baseline, based on
|
|
61
|
+
incremental {outcome} at the end of each quarter. Rank 1 represents the highest
|
|
62
|
+
contribution."""
|
|
63
|
+
|
|
45
64
|
CHANNEL_DRIVERS_CHART_ID = 'channel-drivers-chart'
|
|
46
65
|
CHANNEL_DRIVERS_CHART_TITLE = 'Contribution by baseline and marketing channels'
|
|
47
66
|
CHANNEL_DRIVERS_CHART_DESCRIPTION = """Note: This graphic encompasses all of
|
meridian/analysis/test_utils.py
CHANGED
|
@@ -1813,32 +1813,6 @@ MROI_MEDIA_AND_RF_USE_POSTERIOR_BY_REACH = np.array([
|
|
|
1813
1813
|
[0.2282, 0.3271, 0.1354, 1.8194, 0.5598],
|
|
1814
1814
|
],
|
|
1815
1815
|
])
|
|
1816
|
-
MROI_MEDIA_AND_RF_NEW_TIMES_DATA = np.array([
|
|
1817
|
-
[
|
|
1818
|
-
[1.4194793, 1.4562954, 0.42110616, 0.46693847, 2.224705],
|
|
1819
|
-
[1.4079778, 1.4408227, 0.42190424, 0.46729392, 2.2684476],
|
|
1820
|
-
[1.4133729, 1.455379, 0.42965493, 0.46928722, 2.2630167],
|
|
1821
|
-
[1.3786758, 1.4669218, 0.4399548, 0.466466, 2.2499163],
|
|
1822
|
-
[1.3667594, 1.4595027, 0.430115, 0.46791035, 2.2718313],
|
|
1823
|
-
[1.3636469, 1.4838042, 0.43774834, 0.46620503, 2.228661],
|
|
1824
|
-
[1.3479362, 1.5108197, 0.42130333, 0.46699694, 2.2348554],
|
|
1825
|
-
[1.3684787, 1.5252702, 0.4246083, 0.4682973, 2.2124028],
|
|
1826
|
-
[1.3500556, 1.5139565, 0.42371163, 0.46387878, 2.2052019],
|
|
1827
|
-
[1.3509449, 1.5152782, 0.42376328, 0.46421173, 2.2056532],
|
|
1828
|
-
],
|
|
1829
|
-
[
|
|
1830
|
-
[0.19265468, 0.3131754, 0.11835674, 1.7264867, 0.45867893],
|
|
1831
|
-
[0.19271582, 0.31310934, 0.11833327, 1.7265227, 0.45878735],
|
|
1832
|
-
[0.1927399, 0.3133164, 0.11834618, 1.7260367, 0.45711297],
|
|
1833
|
-
[0.19282141, 0.31354108, 0.11858677, 1.724129, 0.45773327],
|
|
1834
|
-
[0.19184875, 0.31352347, 0.11844476, 1.724147, 0.4579198],
|
|
1835
|
-
[0.19213778, 0.3136204, 0.11846119, 1.7245249, 0.4575424],
|
|
1836
|
-
[0.19298446, 0.3144178, 0.11840369, 1.7243268, 0.45999327],
|
|
1837
|
-
[0.19473709, 0.3146425, 0.1182382, 1.7254068, 0.46479526],
|
|
1838
|
-
[0.19482231, 0.31446627, 0.11806685, 1.7242908, 0.46568453],
|
|
1839
|
-
[0.19479823, 0.31506982, 0.11783329, 1.7254248, 0.46606192],
|
|
1840
|
-
],
|
|
1841
|
-
])
|
|
1842
1816
|
MROI_MEDIA_ONLY_USE_PRIOR = np.array([[
|
|
1843
1817
|
[1.0740, 1.3019, 0.7984],
|
|
1844
1818
|
[0.8990, 0.4201, 0.7120],
|
|
@@ -2025,32 +1999,6 @@ SAMPLE_ROI_NEW_DATA = np.array([
|
|
|
2025
1999
|
[4.11828271, 1.58598067],
|
|
2026
2000
|
],
|
|
2027
2001
|
])
|
|
2028
|
-
ROI_NEW_TIMES_DATA = np.array([
|
|
2029
|
-
[
|
|
2030
|
-
[1.6587073, 1.6422542, 0.5339541, 0.46695647, 2.2247107],
|
|
2031
|
-
[1.6448834, 1.6238545, 0.5349834, 0.46725115, 2.268456],
|
|
2032
|
-
[1.6512104, 1.6399834, 0.54364294, 0.46939594, 2.2630484],
|
|
2033
|
-
[1.6124083, 1.6505513, 0.55594736, 0.46645072, 2.2499137],
|
|
2034
|
-
[1.597995, 1.6415955, 0.5437353, 0.46789172, 2.2718024],
|
|
2035
|
-
[1.5946435, 1.6684949, 0.5538136, 0.46621037, 2.2286298],
|
|
2036
|
-
[1.5757265, 1.6986614, 0.5317232, 0.4669749, 2.2348094],
|
|
2037
|
-
[1.5993005, 1.7154359, 0.535261, 0.4682574, 2.2124054],
|
|
2038
|
-
[1.5770503, 1.7027655, 0.5336275, 0.4639002, 2.2051802],
|
|
2039
|
-
[1.5781163, 1.704347, 0.53381217, 0.4641966, 2.2056897],
|
|
2040
|
-
],
|
|
2041
|
-
[
|
|
2042
|
-
[0.22893494, 0.54465544, 0.17262405, 1.726445, 0.4586947],
|
|
2043
|
-
[0.22897178, 0.54465, 0.17261527, 1.7264631, 0.45878458],
|
|
2044
|
-
[0.22901268, 0.5448365, 0.17263621, 1.7260604, 0.45709604],
|
|
2045
|
-
[0.22900137, 0.54496145, 0.17280822, 1.7242719, 0.4577607],
|
|
2046
|
-
[0.22795935, 0.54490256, 0.17272605, 1.7241275, 0.4579781],
|
|
2047
|
-
[0.22818668, 0.5450325, 0.1726518, 1.7245975, 0.45759043],
|
|
2048
|
-
[0.22910665, 0.5459215, 0.17257686, 1.7243998, 0.4600002],
|
|
2049
|
-
[0.23114449, 0.54561883, 0.17233835, 1.7253716, 0.46480042],
|
|
2050
|
-
[0.23121877, 0.5451343, 0.17214388, 1.7242151, 0.46571606],
|
|
2051
|
-
[0.23116706, 0.54571074, 0.17181544, 1.7254194, 0.4660912],
|
|
2052
|
-
],
|
|
2053
|
-
])
|
|
2054
2002
|
SAMPLE_ROI_KPI = np.array([
|
|
2055
2003
|
[
|
|
2056
2004
|
[0.4906, 0.3487],
|
|
@@ -2617,9 +2565,12 @@ SAMPLE_BASELINE_PCT_OF_CONTRIBUTION_NON_PAID = np.array([
|
|
|
2617
2565
|
[-1.204522e-02, -4.079909e02],
|
|
2618
2566
|
[1.350970e-02, -2.750774e02],
|
|
2619
2567
|
])
|
|
2620
|
-
ADSTOCK_DECAY_CI_HI = np.array([1.0, 1.0, 0.
|
|
2621
|
-
ADSTOCK_DECAY_CI_LO = np.array([1.0, 1.0, 0.
|
|
2622
|
-
ADSTOCK_DECAY_MEAN = np.array([1.0, 1.0, 0.
|
|
2568
|
+
ADSTOCK_DECAY_CI_HI = np.array([1.0, 1.0, 0.8658, 0.9709, 0.7496])
|
|
2569
|
+
ADSTOCK_DECAY_CI_LO = np.array([1.0, 1.0, 0.8328, 0.5749, 0.6936])
|
|
2570
|
+
ADSTOCK_DECAY_MEAN = np.array([1.0, 1.0, 0.8493, 0.8630, 0.7215])
|
|
2571
|
+
ORGANIC_ADSTOCK_DECAY_CI_HI = np.array([1.0, 0.9636, 0.9291, 0.8962, 0.8650])
|
|
2572
|
+
ORGANIC_ADSTOCK_DECAY_CI_LO = np.array([1.0, 0.6623, 0.4394, 0.2920, 0.1944])
|
|
2573
|
+
ORGANIC_ADSTOCK_DECAY_MEAN = np.array([1.0, 0.8076, 0.6633, 0.5537, 0.4693])
|
|
2623
2574
|
HILL_CURVES_CI_HI = np.array([0.0, 0.0, 0.00098, 0.00895, 0.00195])
|
|
2624
2575
|
HILL_CURVES_CI_LO = np.array([0.0, 0.0, 0.00085, 0.00322, 0.00169])
|
|
2625
2576
|
HILL_CURVES_MEAN = np.array([0.0, 0.0, 0.00091, 0.00606, 0.00183])
|
|
@@ -2864,16 +2815,13 @@ def generate_predictive_accuracy_table(
|
|
|
2864
2815
|
shape = [len(metric), len(geo_granularity)]
|
|
2865
2816
|
dims = [c.METRIC, c.GEO_GRANULARITY]
|
|
2866
2817
|
coords = {
|
|
2867
|
-
c.METRIC:
|
|
2868
|
-
c.GEO_GRANULARITY:
|
|
2818
|
+
c.METRIC: metric,
|
|
2819
|
+
c.GEO_GRANULARITY: geo_granularity,
|
|
2869
2820
|
}
|
|
2870
2821
|
if with_holdout:
|
|
2871
2822
|
shape.append(len(evaluation_set))
|
|
2872
2823
|
dims.append(c.EVALUATION_SET_VAR)
|
|
2873
|
-
coords[c.EVALUATION_SET_VAR] =
|
|
2874
|
-
[c.EVALUATION_SET_VAR],
|
|
2875
|
-
evaluation_set,
|
|
2876
|
-
)
|
|
2824
|
+
coords[c.EVALUATION_SET_VAR] = evaluation_set
|
|
2877
2825
|
np.random.seed(0)
|
|
2878
2826
|
value = np.random.lognormal(0, 1, size=shape)
|
|
2879
2827
|
ds = xr.Dataset(
|
|
@@ -2969,7 +2917,7 @@ def generate_paid_summary_metrics() -> xr.Dataset:
|
|
|
2969
2917
|
)
|
|
2970
2918
|
|
|
2971
2919
|
|
|
2972
|
-
def generate_all_summary_metrics() -> xr.Dataset:
|
|
2920
|
+
def generate_all_summary_metrics(aggregate_times: bool = True) -> xr.Dataset:
|
|
2973
2921
|
"""Helper method to generate simulated summary metrics data."""
|
|
2974
2922
|
channel = (
|
|
2975
2923
|
[f"ch_{i}" for i in range(3)]
|
|
@@ -2981,33 +2929,38 @@ def generate_all_summary_metrics() -> xr.Dataset:
|
|
|
2981
2929
|
channel.append(c.ALL_CHANNELS)
|
|
2982
2930
|
metric = [c.MEAN, c.MEDIAN, c.CI_LO, c.CI_HI]
|
|
2983
2931
|
distribution = [c.PRIOR, c.POSTERIOR]
|
|
2932
|
+
time = pd.date_range("2023-01-01", freq="W-SUN", periods=5).format(
|
|
2933
|
+
formatter=lambda x: x.strftime("%Y-%m-%d")
|
|
2934
|
+
)
|
|
2984
2935
|
|
|
2985
2936
|
np.random.seed(0)
|
|
2986
|
-
|
|
2937
|
+
|
|
2938
|
+
if aggregate_times:
|
|
2939
|
+
shape = (len(channel), len(metric), len(distribution))
|
|
2940
|
+
dims = [c.CHANNEL, c.METRIC, c.DISTRIBUTION]
|
|
2941
|
+
else:
|
|
2942
|
+
shape = (len(time), len(channel), len(metric), len(distribution))
|
|
2943
|
+
dims = [c.TIME, c.CHANNEL, c.METRIC, c.DISTRIBUTION]
|
|
2944
|
+
|
|
2987
2945
|
incremental_outcome = np.random.lognormal(10, 1, size=shape)
|
|
2988
2946
|
effectiveness = np.random.lognormal(1, 1, size=shape)
|
|
2989
2947
|
pct_of_contribution = np.random.randint(low=0, high=50, size=shape)
|
|
2990
2948
|
|
|
2949
|
+
coords = {
|
|
2950
|
+
c.CHANNEL: channel,
|
|
2951
|
+
c.METRIC: metric,
|
|
2952
|
+
c.DISTRIBUTION: distribution,
|
|
2953
|
+
}
|
|
2954
|
+
if not aggregate_times:
|
|
2955
|
+
coords[c.TIME] = time
|
|
2956
|
+
|
|
2991
2957
|
return xr.Dataset(
|
|
2992
2958
|
data_vars={
|
|
2993
|
-
c.INCREMENTAL_OUTCOME: (
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
),
|
|
2997
|
-
c.PCT_OF_CONTRIBUTION: (
|
|
2998
|
-
[c.CHANNEL, c.METRIC, c.DISTRIBUTION],
|
|
2999
|
-
pct_of_contribution,
|
|
3000
|
-
),
|
|
3001
|
-
c.EFFECTIVENESS: (
|
|
3002
|
-
[c.CHANNEL, c.METRIC, c.DISTRIBUTION],
|
|
3003
|
-
effectiveness,
|
|
3004
|
-
),
|
|
3005
|
-
},
|
|
3006
|
-
coords={
|
|
3007
|
-
c.CHANNEL: channel,
|
|
3008
|
-
c.METRIC: metric,
|
|
3009
|
-
c.DISTRIBUTION: distribution,
|
|
2959
|
+
c.INCREMENTAL_OUTCOME: (dims, incremental_outcome),
|
|
2960
|
+
c.PCT_OF_CONTRIBUTION: (dims, pct_of_contribution),
|
|
2961
|
+
c.EFFECTIVENESS: (dims, effectiveness),
|
|
3010
2962
|
},
|
|
2963
|
+
coords=coords,
|
|
3011
2964
|
attrs={c.CONFIDENCE_LEVEL: c.DEFAULT_CONFIDENCE_LEVEL},
|
|
3012
2965
|
)
|
|
3013
2966
|
|
|
@@ -3063,14 +3016,8 @@ def generate_predictive_accuracy_data(holdout_id: bool = False) -> xr.Dataset:
|
|
|
3063
3016
|
|
|
3064
3017
|
xr_dims = [c.METRIC, c.GEO_GRANULARITY]
|
|
3065
3018
|
xr_coords = {
|
|
3066
|
-
c.METRIC:
|
|
3067
|
-
|
|
3068
|
-
[c.R_SQUARED, c.MAPE, c.WMAPE],
|
|
3069
|
-
),
|
|
3070
|
-
c.GEO_GRANULARITY: (
|
|
3071
|
-
[c.GEO_GRANULARITY],
|
|
3072
|
-
[c.GEO, c.NATIONAL],
|
|
3073
|
-
),
|
|
3019
|
+
c.METRIC: [c.R_SQUARED, c.MAPE, c.WMAPE],
|
|
3020
|
+
c.GEO_GRANULARITY: [c.GEO, c.NATIONAL],
|
|
3074
3021
|
}
|
|
3075
3022
|
rsquared_arr = [np.random.uniform(0.0, 1.0) for _ in range(2)]
|
|
3076
3023
|
mape_arr = [np.random.uniform(0.0, 1.0) for _ in range(2)]
|
|
@@ -3095,10 +3042,7 @@ def generate_predictive_accuracy_data(holdout_id: bool = False) -> xr.Dataset:
|
|
|
3095
3042
|
)
|
|
3096
3043
|
|
|
3097
3044
|
xr_dims.append(c.EVALUATION_SET_VAR)
|
|
3098
|
-
xr_coords[c.EVALUATION_SET_VAR] = (
|
|
3099
|
-
[c.EVALUATION_SET_VAR],
|
|
3100
|
-
list(c.EVALUATION_SET),
|
|
3101
|
-
)
|
|
3045
|
+
xr_coords[c.EVALUATION_SET_VAR] = list(c.EVALUATION_SET)
|
|
3102
3046
|
xr_data = {c.VALUE: (xr_dims, stacked_total)}
|
|
3103
3047
|
|
|
3104
3048
|
return xr.Dataset(data_vars=xr_data, coords=xr_coords)
|
|
@@ -3194,18 +3138,22 @@ def generate_optimal_frequency_data(
|
|
|
3194
3138
|
|
|
3195
3139
|
def generate_hill_curves_dataframe() -> pd.DataFrame:
|
|
3196
3140
|
"""Helper method to generate simulated hill curve data."""
|
|
3197
|
-
channel_names =
|
|
3198
|
-
f"
|
|
3199
|
-
|
|
3141
|
+
channel_names = (
|
|
3142
|
+
[f"ch_{i}" for i in range(3)]
|
|
3143
|
+
+ [f"rf_ch_{i}" for i in range(2)]
|
|
3144
|
+
+ [f"organic_ch_{i}" for i in range(2)]
|
|
3145
|
+
)
|
|
3200
3146
|
channel_array = []
|
|
3201
3147
|
channel_type_array = []
|
|
3202
|
-
for
|
|
3148
|
+
for channel_name in channel_names:
|
|
3203
3149
|
for _ in range(100):
|
|
3204
|
-
channel_array.append(
|
|
3205
|
-
if
|
|
3150
|
+
channel_array.append(channel_name)
|
|
3151
|
+
if channel_name.startswith("ch_"):
|
|
3206
3152
|
channel_type_array.append(c.MEDIA)
|
|
3207
|
-
|
|
3153
|
+
elif channel_name.startswith("rf_ch_"):
|
|
3208
3154
|
channel_type_array.append(c.RF)
|
|
3155
|
+
elif channel_name.startswith("organic_ch_"):
|
|
3156
|
+
channel_type_array.append(c.ORGANIC_MEDIA)
|
|
3209
3157
|
|
|
3210
3158
|
np.random.seed(0)
|
|
3211
3159
|
media_units_array = [
|