Negative marginal CACs

Has anyone tried looking at marginal CACs when doing MMM in PyMC3?

I’ve been working off Michael Johns & Zhenyu Wang’s Bayesian Approach to Media Mix Modeling from a couple of years back and was surprised to find that the reach / saturation function they used often yields discontinuities in marginal CAC. Curious if anyone else has had this issue - and found a better reach function to use.

Rough illustration:

  • Spend level 1 $900
    CAC @ spend 1 $55.33
    Acquisitions @ spend 1 16.27

  • Spend level 2 $1000
    CAC @ spend 2 $65.80
    Acquisitions @ spend 2 15.20

Fewer acquisitions in spite of higher spend means the marginal CAC is negative.

Might want to check against the default implementation in MMM Example Notebook — pymc-marketing 0.1.1 documentation

Thank you! That’s actually where i got my original code from :]

The CAC curve looks fine, the issue only becomes apparent if you look at marginals. I noticed that this was not done in any of the publicly available notebooks using this function.

Quick addendum - i tried plugging in a couple of other saturation functions and time and again have gotten the error:

“Length of Elemwise{true_div,no_inplace}.0 cannot be determined”

For example, this happens when i copy-paste the exact code for tanh_saturation() from pymc_marketing/mmm/transformers.py; it has also come up for a Hill function as well as a couple of other alternatives i tried throwing at this problem. Any tips you may have for solving this would be immensely appreciated.

Quick update here in case someone else runs into the Elemwise issue:

I dug up a really old doc which mentioned that we can often use plain numpy functions in place of theano tensor functions. Switching tt. to np. whenever possible solved my issue. (Of course some theano functions don’t have numpy equivalents.)

Curiously my ad-stock function continues to work fine with tt. :slightly_smiling_face:

That will only call the tt versions under the hood. It’s better to do it yourself explicitly because for some functions it won’t work (like np.dot) and you will end up more surprised.