add_saturation_observations#

pymc_marketing.mmm.lift_test.add_saturation_observations(df_lift_test, variable_mapping, saturation_function, model=None, dist=<class 'pymc.dims.distributions.scalar.Gamma'>, name='lift_measurements', get_indices=<function exact_row_indices>)[source]#

Add saturation observations to the likelihood of the model.

General function to add lift measurements to the likelihood of the model.

Not to be used directly for general use. Use MMM.add_lift_test_measurements() or add_lift_measurements_to_likelihood_from_saturation() instead.

Parameters:
df_lift_testpd.DataFrame
DataFrame with lift test results with at least the following columns:
  • x: x axis value of the lift test.

  • delta_x: change in x axis value of the lift test.

  • delta_y: change in y axis value of the lift test.

  • sigma: standard deviation of the lift test.

Any additional columns are assumed to be coordinates in the model.

variable_mappingdict[str, str]

Dictionary of variable names to dimensions.

saturation_functionCallable[[np.ndarray], np.ndarray]

Function that takes spend and returns saturation.

modelOptional[Model], optional

PyMC model with arbitrary number of coordinates, by default None

distpymc.dims.DimDistribution class, optional

PyMC dim distribution to use for the likelihood, by default Gamma

namestr, optional

Name of the likelihood, by default “lift_measurements”

get_indicesCallable[[pd.DataFrame, pm.Model], Indices], optional

Function to get the indices of the DataFrame in the model, by default exact_row_indices which assumes that the columns map exactly to the model coordinates.

Examples

Add lift tests for time-varying saturation to a model:

import pymc as pm
import pymc.dims as pmd
import pandas as pd
from pymc_marketing.mmm.lift_test import add_saturation_observations

df_base_lift_test = pd.DataFrame(
    {
        "x": [1, 2, 3],
        "delta_x": [1, 2, 3],
        "delta_y": [1, 2, 3],
        "sigma": [0.1, 0.2, 0.3],
    }
)

def saturation_function(x, alpha, lam):
    return alpha * x / (x + lam)

# These are required since alpha and lam
# have both channel and date dimensions
df_lift_test = df_base_lift_test.assign(
    channel="channel_1",
    date=["2019-01-01", "2019-01-02", "2019-01-03"],
)

coords = {
    "channel": ["channel_1", "channel_2"],
    "date": ["2019-01-01", "2019-01-02", "2019-01-03", "2019-01-04"],
}
with pm.Model(coords=coords) as model:
    # Usually defined in a larger model.
    # Distributions don't matter here, just the shape
    alpha = pmd.HalfNormal("alpha_in_model", dims=("channel", "date"))
    lam = pmd.HalfNormal("lam_in_model", dims="channel")

    add_saturation_observations(
        df_lift_test,
        variable_mapping={
            "alpha": "alpha_in_model",
            "lam": "lam_in_model",
        },
        saturation_function=saturation_function,
    )

Use the saturation classes to add lift tests to a model. NOTE: This is what happens internally of MMM.

import pymc as pm
import pymc.dims as pmd
import pandas as pd

from pymc_marketing.mmm import LogisticSaturation
from pymc_marketing.mmm.lift_test import add_saturation_observations

saturation = LogisticSaturation()

df_base_lift_test = pd.DataFrame(
    {
        "x": [1, 2, 3],
        "delta_x": [1, 2, 3],
        "delta_y": [1, 2, 3],
        "sigma": [0.1, 0.2, 0.3],
    }
)

df_lift_test = df_base_lift_test.assign(
    channel="channel_1",
)

coords = {
    "channel": ["channel_1", "channel_2"],
}
with pm.Model(coords=coords) as model:
    # Usually defined in a larger model.
    # Distributions dont matter here, just the shape
    lam = pmd.HalfNormal("saturation_lam", dims="channel")
    beta = pmd.HalfNormal("saturation_beta", dims="channel")

    add_saturation_observations(
        df_lift_test,
        variable_mapping=saturation.variable_mapping,
        saturation_function=saturation.function,
    )

Add lift tests for channel, geo saturation functions.

import pymc as pm
import pymc.dims as pmd
import pandas as pd

from pymc_marketing.mmm import LogisticSaturation
from pymc_marketing.mmm.lift_test import add_saturation_observations

saturation = LogisticSaturation()

df_base_lift_test = pd.DataFrame(
    {
        "x": [1, 2, 3],
        "delta_x": [1, 2, 3],
        "delta_y": [1, 2, 3],
        "sigma": [0.1, 0.2, 0.3],
    }
)

df_lift_test = df_base_lift_test.assign(
    channel="channel_1",
    geo=["G1", "G2", "G2"],
)

coords = {
    "channel": ["channel_1", "channel_2"],
    "geo": ["G1", "G2", "G3"],
}
with pm.Model(coords=coords) as model:
    # Usually defined in a larger model.
    # Distributions dont matter here, just the shape
    lam = pmd.HalfNormal("saturation_lam", dims=("channel", "geo"))
    beta = pmd.HalfNormal("saturation_beta", dims=("channel", "geo"))

    add_saturation_observations(
        df_lift_test,
        variable_mapping=saturation.variable_mapping,
        saturation_function=saturation.function,
    )