-
Notifications
You must be signed in to change notification settings - Fork 288
Sensitivity analysis and marginal effects #1673
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
base: main
Are you sure you want to change the base?
Conversation
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1673 +/- ##
==========================================
- Coverage 91.59% 90.54% -1.05%
==========================================
Files 60 61 +1
Lines 6778 6872 +94
==========================================
+ Hits 6208 6222 +14
- Misses 570 650 +80 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Hey @drbenvincent very nice work, I really like the functionality here. Nevertheless, after a first quick scan, a few suggestion thinking in the future:
Doing this hopefully we can aim to a signature like: sweep_values = mmm.counterfactual_sweep(
predictors=["influencer_spend"],
sweep_values=np.linspace(0, 2, 12),
sweep_type="absolute",
)
optimizable_model.plot.marginal_effects(
samples=sweep_values,
); This will be consistent with how other plots and methods are being used, for example, optimizer have very similar way of working. How does that sound? On the other hand, I left a few minor questions! Note I saw an issue with drivers being scaled (Ensure the sweep values are being applied in the original space), if you are using the new MMM class, you can make any variable in the graph original scale. Meaning, you can create the original scale var, and probably change your signature from |
View / edit / reply to this conversation on ReviewNB cetagostini commented on 2025-05-09T08:03:47Z Why the total uplift starts in negative? that confuse me a bit. Regarding the second plot, may be better to just called derivative? drbenvincent commented on 2025-05-09T08:35:50Z The top plot starts negative on the left because we've gone from some influencer spend to zero influencer spend. The y-axis is change in uplift relative to the actual scenario, so it answers the question of "what would have happened to our total sales if we had spent zero on influencers.
Second plot - I think it makes sense to keep the "marginal effects" terminology because that is quite popular in stats circles. There's certainly scope for changing the y-axis label, but I think it makes sense to describe the marginal effects as the derivative when I flesh out the notebook text. |
View / edit / reply to this conversation on ReviewNB cetagostini commented on 2025-05-09T08:03:48Z This confuse me a bit more. Why the derivative looks like this? drbenvincent commented on 2025-05-09T08:37:49Z Good point. Check the y-axis scale. The top plot is essentially linear, so the derivative is actually a constant value. But when the axes are scaled so tightly around the values it magnifies numerical imprecision. I will see what I can do with y-axis scaling to better visually portray that the marginal effect here is flat as a function of the variable being manipulated on the x-axis
drbenvincent commented on 2025-05-15T11:27:17Z Resolved in fb51cc4 |
View / edit / reply to this conversation on ReviewNB cetagostini commented on 2025-05-09T08:03:49Z What happens when you move to multiplicative change? Does this means you change the nature of the model to multiplicative? drbenvincent commented on 2025-05-09T08:39:43Z Ah, so this will be better explained as I flesh out the notebook text. The additive, multiplicative, or absolute options are how we are manipulating the target driver. So setting the sweep values to have a multiplicative effect keeps the time course of their values but literally just scales them up or down. Nothing about the model is changed other than how we are manipulating the target driver. |
The top plot starts negative on the left because we've gone from some influencer spend to zero influencer spend. The y-axis is change in uplift relative to the actual scenario, so it answers the question of "what would have happened to our total sales if we had spent zero on influencers.
Second plot - I think it makes sense to keep the "marginal effects" terminology because that is quite popular in stats circles. There's certainly scope for changing the y-axis label, but I think it makes sense to describe the marginal effects as the derivative when I flesh out the notebook text. View entire conversation on ReviewNB |
Good point. Check the y-axis scale. The top plot is essentially linear, so the derivative is actually a constant value. But when the axes are scaled so tightly around the values it magnifies numerical imprecision. I will see what I can do with y-axis scaling to better visually portray that the marginal effect here is flat as a function of the variable being manipulated on the x-axis
View entire conversation on ReviewNB |
Ah, so this will be better explained as I flesh out the notebook text. The additive, multiplicative, or absolute options are how we are manipulating the target driver. So setting the sweep values to have a multiplicative effect keeps the time course of their values but literally just scales them up or down. Nothing about the model is changed other than how we are manipulating the target driver. View entire conversation on ReviewNB |
So it's very worth having this discussion now. Bear in mind that the counterfactual sweep returns a more complex idata - a set of uplifts but with an additional sweep dimension. At the moment this is stored as it's own thing, but I guess I could store it as a new DataArray in the idata. Happy to be guided by you guys about the best way to incorporate this into the codebase. I think there are a few options:
I'm guessing (1) might be your favourite? Any thoughts from @williambdean on this? Would be ideal to pin down the API and general approach early, to avoid having to do any reimplementation/restructuring. |
Co-authored-by: Will Dean <[email protected]>
@cetagostini I've had luck switching to actual = self.mmm._get_group_predictive_data(
group="posterior_predictive", original_scale=True
)["y"] which does not seem to have made it from the original pymc-marketing/pymc_marketing/mmm/base.py Lines 275 to 295 in 6d71a20
Any chance you could add this add this method in, or otherwise let me know how to easily access the posterior predictions in the original scale? I can see how you can do it in the multidimensional example notebook, but it would be useful if a user doesn't have to remember to manually rescale |
FYI. At this point of my refactor, the API is: results:xr.Dataset = CounterfactualSweep(
mmm=mmm,
predictors=["influencer_spend"],
sweep_values=np.linspace(0, 2, 12), # Set spend directly from 0 to $100k
sweep_type="absolute",
).run_sweep()
CounterfactualSweep.plot_uplift(results);
CounterfactualSweep.plot_marginal_effects(results); The changes are:
This is not the end goal. Just keeping track of progress because I'm only able to work on this in bursts. |
Note to self: next focus should be making the sweep intervention in the original data space. Once this is done, we can verify it is working correctly by checking the uplift for a multiplicative change of 1 is zero (because the counterfactual scenario is equal to the actual scenario). |
API is now: mmm.sensitivity_analysis(
predictors=["influencer_spend"],
sweep_values=np.linspace(0, 2, 12),
sweep_type="multiplicative",
)
mmm.plot.plot_sensitivity_analysis()
mmm.plot.plot_sensitivity_analysis(marginal=True); |
Just a quick ping about this one @cetagostini - just because I'm a little idle across the board waiting on reviews on multiple projects :) |
Description
This PR provides extra functionality to extract insights from MMM's. It adds a new class which allows the MMM to be evaluated at a grid of parameters (in what I'm calling a sweep). We can use this functionality to plot marginal effects (where the sweep defines the absolute value of a driver) or where we run a counterfactual sweep. In this latter case, we can run either a multiplicative or additive sweep. This mode retains the time-varying driver values but modifies them multiplicatively or additively.
Sweep type
Plot type
plot_uplift
- plots the change in total outcome variable (e.g. sales) as a function of the sweep valueplot_marginal_effects
- plots the marginal effects as a function of the sweep value.TODO
MMMPlotSuite
. Requires appending the results intoMMM.idata
.Checklist
pre-commit.ci autofix
to auto-fix.📚 Documentation preview 📚: https://pymc-marketing--1673.org.readthedocs.build/en/1673/