Replies: 4 comments 7 replies
-
Hi @JustStas Yes, we are still working on a forecast functionality that will need some validation as you mention, but we still need some more time. We still don't have it all figured out so any ideas, feedback, or learnings you've already acquired and want to share, are more than welcome. |
Beta Was this translation helpful? Give feedback.
-
I updated the s-curve computation: class Adstock:
def __init__(self, theta):
self.theta = theta
self.decays = []
def compute(self, x_ser, d=0):
try:
if len(x_ser) == 1:
return 0
else:
decay = self.compute(x_ser.iloc[1:], d+1)
self.decays += [decay]
return x_ser.iloc[0] + self.theta * decay
except RecursionError:
print(d)
def transform_gamma(self, gamma):
return np.quantile(self.decays, gamma)
def s_curve(adstocked, alpha, gamma):
return adstocked**alpha / (adstocked**alpha + gamma**alpha)
class Media:
def __init__(self, media_name, alpha, gamma, theta, beta):
self.name = media_name
self.alpha = alpha
self.gamma = gamma
self.theta = theta
self.beta = beta
def transform(self, x_ser):
# TO BE OPTIMIZED (CACHE PRIORS)
# Includes Hill saturation
self.adstock = Adstock(self.theta)
ads_value = self.adstock.compute(x_ser)
self.gamma_transformed = self.adstock.transform_gamma(self.gamma)
adstocked = adstock(x_ser, self.theta)
s_curved = s_curve(adstocked, self.alpha, self.gamma_transformed)
# print('\nbeta', self.beta)
# print('alpha', self.alpha)
# print('gamma', self.gamma)
# print('gamma_transformed', self.gamma_transformed)
# print('decay', adstocked)
# print('decay alpha', adstocked**self.alpha)
# print('s curve', s_curved)
# print('return', self.beta * s_curved)
return self.beta * s_curved
class Context:
def __init__(self, context_name, beta):
self.name = context_name
self.beta = beta
def transform(self, x):
return self.beta * x
class ProphetPredictor:
def __init__(self, wd_betas, trend_beta, trend_base_date, trend_base_value):
self.wd_betas = wd_betas
self.trend_beta = trend_beta
self.trend_base_date = pd.to_datetime(trend_base_date)
self.trend_base_value = trend_base_value
def date_diff(self, ds):
return (pd.to_datetime(ds) - self.trend_base_date).days
def wd_forecast(self, ds):
wd = pd.to_datetime(ds).day_of_week
return self.wd_betas[wd]
def trend_forecast(self, ds):
days_from_base = self.date_diff(ds)
return self.trend_base_value + days_from_base*self.trend_beta
def full_forecast(self, ds):
return self.wd_forecast(ds) + self.trend_forecast(ds) with a transformation of Gamma (as I understand it). Unfortunately, the s-curve results still don't match the corresponding components of the model... |
Beta Was this translation helpful? Give feedback.
-
All right, I think I made it work: class Adstock:
def __init__(self, theta):
self.theta = theta
self.decays = []
def compute(self, x_ser, d=0):
try:
if len(x_ser) == 1:
return 0
else:
decay = self.compute(x_ser.iloc[1:], d+1)
self.decays += [decay]
return x_ser.iloc[0] + self.theta * decay
except RecursionError:
print(d)
def transform_gamma(self, gamma):
min_d = np.min(self.decays)
max_d = np.max(self.decays)
lin_sp = np.linspace(min_d, max_d, 100)
# return np.quantile(self.decays[:100], gamma)
return round(np.quantile(lin_sp, gamma), 4)
def s_curve(adstocked, alpha, gamma):
return adstocked**alpha / (adstocked**alpha + gamma**alpha)
class Media:
def __init__(self, media_name, alpha, gamma, theta, beta):
self.name = media_name
self.alpha = alpha
self.gamma = gamma
self.theta = theta
self.beta = beta
def transform(self, x_ser):
# TO BE OPTIMIZED (CACHE PRIORS)
# Includes Hill saturation
self.adstock = Adstock(self.theta)
ads_value = self.adstock.compute(x_ser)
self.gamma_transformed = self.adstock.transform_gamma(self.gamma)
# adstocked = self.adstock(x_ser, self.theta)
s_curved = s_curve(ads_value, self.alpha, self.gamma_transformed)
# print('\nbeta', self.beta)
# print('alpha', self.alpha)
# print('gamma', self.gamma)
# print('gamma_transformed', self.gamma_transformed)
# print('decay', adstocked)
# print('decay alpha', adstocked**self.alpha)
# print('s curve', s_curved)
# print('return', self.beta * s_curved)
return self.beta * s_curved
class Context:
def __init__(self, context_name, beta):
self.name = context_name
self.beta = beta
def transform(self, x):
return self.beta * x
class ProphetPredictor:
def __init__(self, wd_betas, trend_beta, trend_base_date, trend_base_value):
self.wd_betas = wd_betas
self.trend_beta = trend_beta
self.trend_base_date = pd.to_datetime(trend_base_date)
self.trend_base_value = trend_base_value
def date_diff(self, ds):
return (pd.to_datetime(ds) - self.trend_base_date).days
def wd_forecast(self, ds):
wd = pd.to_datetime(ds).day_of_week
return self.wd_betas[wd]
def trend_forecast(self, ds):
days_from_base = self.date_diff(ds)
return self.trend_base_value + days_from_base*self.trend_beta
def full_forecast(self, ds):
return self.wd_forecast(ds) + self.trend_forecast(ds) for some reason, the gamma transformation in the initial code looks like this: |
Beta Was this translation helpful? Give feedback.
-
Hey @JustStas I'm Cristian Nozzi from facebook. I haven't had the chance to test this out yet but In the meantime I wanted to share a thought with you. The idea is super cool and I see how this can be really helpful but in your specific case, the model you build with 24 months of data and validate with the 25th month of data will only be good for retro-active analysis. If you provide the 25th month of data using the Refresh function you'll end up with a new set of data. Hope I was clear enough, what do you think about it? |
Beta Was this translation helpful? Give feedback.
-
Hi everyone!
Could you please tell me what is the best way to do out-of-sample validation/testing of Robyn models? I would like to see how a trained model performs on a holdout set.
I saw the refresh functionality, but if I understand correctly, it does not show how the non-updated model performs on the new data...
Is there a way to make predictions based on the CSVs generated by the model?
Beta Was this translation helpful? Give feedback.
All reactions