Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New notebook: causal inference with MMM's #1032

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft

Conversation

drbenvincent
Copy link
Contributor

@drbenvincent drbenvincent commented Sep 13, 2024

Description

This PR adds a new docs page which demonstrates ability to do causal inference with MMM's. In particular we showcase an example where we run a campaign with elevated media spend for a period of time. Our goal is to estimate the causal impact of the campaign. This is illustrated with simulated data in order to run a parameter recovery. So we know the true causal impact of the campaign and can therefore evaluate if the estimated causal impact is close to the true causal impact.

Overall, this notebooks showcases the ability to conduct causal inference in MMM's in pymc-marketing.

Related Issue

Checklist

Modules affected

  • MMM
  • CLV

Type of change

  • New feature / enhancement
  • Bug fix
  • Documentation
  • Maintenance
  • Other (please specify):

📚 Documentation preview 📚: https://pymc-marketing--1032.org.readthedocs.build/en/1032/

@drbenvincent drbenvincent added docs Improvements or additions to documentation MMM labels Sep 13, 2024
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@drbenvincent drbenvincent marked this pull request as draft September 13, 2024 16:52
@drbenvincent drbenvincent changed the title initial and crude proof of concept notebook New notebook: causal inference with MMM's Sep 13, 2024
@juanitorduz
Copy link
Collaborator

juanitorduz commented Sep 13, 2024

@drbenvincent What if we do a train test split (before and after intervention) and generate predictions and counterfactuals as https://www.pymc-marketing.io/en/latest/notebooks/mmm/mmm_time_slice_cross_validation.html ? This is how we use it in practice. WDYT?

I fear that if we train on the whole data set, we will leak information (for example, inferring the parameter of the trend component).

@drbenvincent
Copy link
Contributor Author

drbenvincent commented Sep 13, 2024

I will think about it. There are at least 2 scenarios:

  • Using prior to an experiment to help in planning an experiment. For example, guiding you on level of spend increase for different minimum detectable effects.
  • Using after the experiment - to analyse the causal impact of a campaign. In this case I don't think there's a data leakage issue. Or are you thinking about a case where there's an unobserved confounding variable which changed at the same time as the intervention?

EDIT: I'm not sure I understand the issue with data leakage in this particular scenario. But it's been a long week.

I'm also planning on taking about (but maybe not including a worked example) what you'd do in real work situations when you don't have counterfactual spends. That could be approached by using CausalPy & synthetic control to predict what the media spend would have been in the absence of the campaign. Though it might be the case that a company knows what they were going to spend in the absence of a campaign.

@juanitorduz
Copy link
Collaborator

I'm not sure I understand the issue with data leakage in this particular scenario. But it's been a long week.

For example, if you train in the whole data set, then you are already using information on the post-intervention period to infer parameters to generate counterfactuals.

Now that I think about it, according to https://smithamilli.com/blog/causal-ladder/ (and similar references). Your approach is indeed at the last level: counterfactuals (and fitting on the whole data is ok I think). Whereas how I would use it (for scenario planing) is on level 2: interventions. This can actually be a nice discussion to have in the notebook (if we want to)

@drbenvincent
Copy link
Contributor Author

drbenvincent commented Sep 13, 2024

I understand the point now - you could argue that training on the full data may overestimate the trend. But I think that any increase in the outcome should be accounted for by the media + transformations.

Good point about counterfactual versus interventions. I think it is worth talking about that in the notebook.

Copy link

review-notebook-app bot commented Sep 14, 2024

View / edit / reply to this conversation on ReviewNB

cetagostini commented on 2024-09-14T00:09:53Z
----------------------------------------------------------------

I like it, but I think that to be in line with the language usually used in causal inference, I would say that we add an intervention to the synthetic dataset.

Also, I would talk about the intervention, giving the example where we will increase our budget by 40% from period A to B.


drbenvincent commented on 2024-09-16T14:59:45Z
----------------------------------------------------------------

Have slightly modified this text. Let's see how it all reads once the larger scale structure of the notebook has settled down a bit.

cetagostini commented on 2024-09-16T20:08:57Z
----------------------------------------------------------------

🚀

Copy link

review-notebook-app bot commented Sep 14, 2024

View / edit / reply to this conversation on ReviewNB

cetagostini commented on 2024-09-14T00:09:53Z
----------------------------------------------------------------

Just to be more descriptive I would perhaps call it something like simulate_intervention instead of generate_actual_dataset ?


drbenvincent commented on 2024-09-16T14:57:46Z
----------------------------------------------------------------

Done in an upcoming commit

Copy link

review-notebook-app bot commented Sep 14, 2024

View / edit / reply to this conversation on ReviewNB

cetagostini commented on 2024-09-14T00:09:54Z
----------------------------------------------------------------

Line #6.        Based on the counterfactual dataset, apply the intervention on the specified date

You mentioned here counterfactual, may be nice to talk about whats a counterfactual and factual scenario? what do we mean by causal impact?


Copy link

review-notebook-app bot commented Sep 14, 2024

View / edit / reply to this conversation on ReviewNB

cetagostini commented on 2024-09-14T00:09:55Z
----------------------------------------------------------------

Here we start talking about counterfactuals without any previous information, I would add a paragraph before to explain in detail this section.

Mentioning that we have a dataset with the real intervention, but because we know the strength of the real intervention, we can create another time series where it did not occur and estimate the true effect of the 40% increase in spending.

I think all the information is there, but I would make it clearer and with a little more storytelling.


drbenvincent commented on 2024-09-16T15:01:29Z
----------------------------------------------------------------

Yep - so this initial commit was very preliminary - I've expanded on the text and narrative of the notebook much more now

cetagostini commented on 2024-09-16T20:09:48Z
----------------------------------------------------------------

🚀

Copy link

review-notebook-app bot commented Sep 14, 2024

View / edit / reply to this conversation on ReviewNB

cetagostini commented on 2024-09-14T00:09:55Z
----------------------------------------------------------------

Are these priors doing anything? We can leave the default ones we use. Otherwise, we could extend and show how changing priors changes estimated impact. (?)


drbenvincent commented on 2024-09-16T15:01:46Z
----------------------------------------------------------------

Good point. I've removed these and we now use default priors

@drbenvincent
Copy link
Contributor Author

Just to say... this notebook is going to get substantial modification / TLC. But the input already is great - looks like this topic is interesting to people 🙂

@drbenvincent
Copy link
Contributor Author

Very excited about plans I have for this post. Though changes will have to wait until Monday. Watch this space...

Copy link
Contributor

@wd60622 wd60622 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool.

Do you think we can do away with the large functions and duplication in the notebook? It seems the API should expose:

  • parameter freezing via pm.do instead redefining the forward_pass
    • Someone can initialize model without data (that defines the structure) and call something like: params = {"intercept: 1, ...}; y = mmm.generate_target(X, params=params, target_scaler=...). This opens door to X_counterfactual = some_modification(X); y_counterfactual = mmm.generate_target(X_counterfactual, params=params, ...)
  • The counterfactual comparision plot. We just need two targets, right? which we can store in xarray to have the date dim
    • plot_counterfactual(y, y_counterfactual, along="date", intervention_date="...")

WDYT?

@drbenvincent
Copy link
Contributor Author

Yep - so this is the first very crude implementation. I'm trying to move away from generating synthetic data from separate numpy code and doing it with the DAG, sampling for the prior. I didn't know about mmm.generate_target so this is very useful. I'll see how far I get with that approach and maybe ask for some guidance. I know how to do it with base pymc, but I'm still unfamiliar with much of the pymc-marketing functionality/methods.

And I'll move towards getting counterfactual samples into the idata.predictions field. Is that what you're suggesting?

This notebook is going to change quite a lot before I flag as ready for review 👍🏻

@wd60622
Copy link
Contributor

wd60622 commented Sep 14, 2024

Yep - so this is the first very crude implementation. I'm trying to move away from generating synthetic data from separate numpy code and doing it with the DAG, sampling for the prior. I didn't know about mmm.generate_target so this is very useful. I'll see how far I get with that approach and maybe ask for some guidance. I know how to do it with base pymc, but I'm still unfamiliar with much of the pymc-marketing functionality/methods.

That doesn't exist but just some ideas are API. I think we should make whatever is useful to us

And I'll move towards getting counterfactual samples into the idata.predictions field. Is that what you're suggesting?

The idata for the instance won't exist until it being fit. However, xarray objects could be generated. Similar to the sample_prior, sample_curve workflow for many of the components as they relay on pymc functions

This notebook is going to change quite a lot before I flag as ready for review 👍🏻

👍

Copy link
Contributor Author

Done in an upcoming commit


View entire conversation on ReviewNB

Copy link
Contributor Author

Have slightly modified this text. Let's see how it all reads once the larger scale structure of the notebook has settled down a bit.


View entire conversation on ReviewNB

Copy link
Contributor Author

Yep - so this initial commit was very preliminary - I've expanded on the text and narrative of the notebook much more now


View entire conversation on ReviewNB

Copy link
Contributor Author

Good point. I've removed these and we now use default priors


View entire conversation on ReviewNB

@drbenvincent
Copy link
Contributor Author

Sorry, took me a moment to understand that this was a proposal @wd60622.

  • parameter freezing via pm.do instead redefining the forward_pass

    • Someone can initialize model without data (that defines the structure) and call something like: params = {"intercept: 1, ...}; y = mmm.generate_target(X, params=params, target_scaler=...). This opens door to X_counterfactual = some_modification(X); y_counterfactual = mmm.generate_target(X_counterfactual, params=params, ...)

Yep - I will have a go at defending these functions (quickly and crudely) in the notebook. Then they can be built out either in this PR or a different one.

  • The counterfactual comparision plot. We just need two targets, right? which we can store in xarray to have the date dim

    • plot_counterfactual(y, y_counterfactual, along="date", intervention_date="...")

Yes, but I think we would need a way to set the predictions=True flag in the kwargs of pm.sample_posterior_predictive. Is that possible now, or would that be new functionality.

I have to shift focus for a day or so, but I'll see what I can do code-wise in this notebook to inspire some new functionality.

Copy link
Contributor

🚀


View entire conversation on ReviewNB

Copy link
Contributor

🚀


View entire conversation on ReviewNB

@wd60622
Copy link
Contributor

wd60622 commented Sep 17, 2024

Feel free to make some issues if you don't touch in this PR

Yes, but I think we would need a way to set the predictions=True flag in the kwargs of pm.sample_posterior_predictive. Is that possible now, or would that be new functionality.

I have to shift focus for a day or so, but I'll see what I can do code-wise in this notebook to inspire some new functionality.

The sample_posterior_predictive method allows for passing sample_posterior_predictive kwargs.
My idea was to just pass the xarray.DataArray objects for a function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to documentation MMM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants