-
Notifications
You must be signed in to change notification settings - Fork 331
User-defined media transformations and custom ordering #632
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
User-defined media transformations and custom ordering #632
Conversation
Co-Authored-By: Carlos Trujillo <59846724+cetagostini@users.noreply.github.com>
Co-Authored-By: Carlos Trujillo <59846724+cetagostini@users.noreply.github.com>
|
Now is possible to pass an string to the class or a custom component class, e.g: mmm = DelayedSaturatedMMM(
model_config = my_model_config,
sampler_config = my_sampler_config,
adstock = "geometric",
saturation = LogisticSaturationComponent,
date_column="date_week",
channel_columns=["x1", "x2"],
control_columns=[
"event_1",
"event_2",
"t",
],
adstock_max_lag=8,
yearly_seasonality=2,
)If the user wants to allow for a custom function (Component) then he must create a class which contains a variable mapping property and a default config property. In order to allow other functions as lift test integration work. |
|
I have still the issue of: Traceback (most recent call last):
File "/home/docs/checkouts/readthedocs.org/user_builds/pymc-marketing/envs/632/lib/python3.10/site-packages/sphinx/config.py", line 358, in eval_config_file
exec(code, namespace) # NoQA: S102
File "/home/docs/checkouts/readthedocs.org/user_builds/pymc-marketing/checkouts/632/docs/source/conf.py", line 5, in <module>
import pymc_marketing # isort:skip
File "/home/docs/checkouts/readthedocs.org/user_builds/pymc-marketing/envs/632/lib/python3.10/site-packages/pymc_marketing/__init__.py", line 1, in <module>
from pymc_marketing import clv, mmm
File "/home/docs/checkouts/readthedocs.org/user_builds/pymc-marketing/envs/632/lib/python3.10/site-packages/pymc_marketing/mmm/__init__.py", line 1, in <module>
from pymc_marketing.mmm import base, delayed_saturated_mmm, preprocessing, validating
File "/home/docs/checkouts/readthedocs.org/user_builds/pymc-marketing/envs/632/lib/python3.10/site-packages/pymc_marketing/mmm/delayed_saturated_mmm.py", line 22, in <module>
from pymc_marketing.mmm.models.components.lagging import _get_lagging_function
ModuleNotFoundError: No module named 'pymc_marketing.mmm.models.components'@juanitorduz @wd60622 If you don't find the separation into folders something practical then I'm going to leave everything in the main to avoid it. If you want to modify, feel free. Anything to move quickly. I only do it because I would like to maintain some order and possibly have a folder with different models like in the CLV model. |
|
The lift tests will need something like this: variable_mapping = {
"lam": "saturation_lambda",
"beta": "saturation_beta",
}
def saturation_function(x, beta, lam):
return beta * logistic_saturation(x, lam)
add_lift_measurements_to_likelihood(
df_lift_test,
variable_mapping,
saturation_function=saturation_function,
model=model,
dist=dist,
name=name,
)The EDIT: Opening up an PR into your branch carlos: cetagostini-wise#1 |
I have not experienced this while working in my branch / locally. This is in the CI/CD right? |
…ests Lift test support with generalized saturation function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some small items and suggestions.
Checking out the notebooks now
|
+1 View entire conversation on ReviewNB |
|
+1 View entire conversation on ReviewNB |
|
View / edit / reply to this conversation on ReviewNB wd60622 commented on 2024-06-08T06:57:33Z These formulas are not rendering well here or on the page: https://pymc-marketing--632.org.readthedocs.build/en/632/notebooks/mmm/mmm_budget_allocation_example.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave a quick review again. Very minor comments (I will check the notebooks tonight). I believe we will merge this one on Monday :D
| constraints = custom_constraints | ||
|
|
||
| num_channels = len(self.parameters.keys()) | ||
| initial_guess = [total_budget // num_channels] * num_channels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to double check: this is the floor division and then multiplying it back?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the multiplication is just to repeat the same number for all channels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why floor division over normal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| initial_guess = [total_budget // num_channels] * num_channels | |
| initial_guess = np.ones(num_channels) * total_budget / num_channels |
| method="SLSQP", | ||
| options={"ftol": 1e-9, "maxiter": 1000}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we allow the users to add this? We can add a **minimize_kwargs argument to allocate_budget and pass them to minimize?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can keep these defaults.
Also, this is not a blocker for this PR. Feel free to open an issue, and we can work it out later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make the issue and address it in other PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created #729
| for group_name, params in param_groups.items(): | ||
| # Build dictionary for the current group of parameters | ||
| param_dict = { | ||
| param.replace(group_name[:-7] + "_", ""): self.fit_result[param] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we do something more transparent like using param.split ? Say
>>> word = "my_word"
>>> word.split("my_")
['', 'word']
>>> word.split("my_")[-1]
'word'| budget_bounds: dict[str, list[Any]] | None = None, | ||
| custom_constraints: dict[str, float] | None = None, | ||
| quantile: float = 0.5, | ||
| ): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing return type hint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think it looks good!
Will just merge in suggestions from @juanitorduz
Any idea what is up with the code coverage decrease? Is that a fluke?
| constraints = custom_constraints | ||
|
|
||
| num_channels = len(self.parameters.keys()) | ||
| initial_guess = [total_budget // num_channels] * num_channels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| initial_guess = [total_budget // num_channels] * num_channels | |
| initial_guess = np.ones(num_channels) * total_budget / num_channels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cetagostini @wd60622 this was a massive effort and work! Amazing!
Description
This PR introduces:
MMMas replacement toDelayedSaturatedMMMclass.MMMalong to specify desired transformationsDelayedSaturatedMMMis specific instance ofMMMbut with "geometric" and "logistic" transformationsadstock_firstparameterCurrently we could choose the function in the following way:
Alternative to the string, there are new classes that can be passed instead
This has out-of-the-box media transformations for:
a) Adstock:
b) Saturation:
This allows for 4 (adstocks) x 5 (saturations) x 2 (orderings) = 40 out-of-the-box models!
One can also make a custom media transformation by inheriting from the
AdstockTransformationorSaturationTransformationbase class which can be used in the MMM as well. For instance,EDIT: Changed description with latest changes
Related Issue
Checklist
Modules affected
Type of change
📚 Documentation preview 📚: https://pymc-marketing--632.org.readthedocs.build/en/632/