|
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from scipy.optimize import curve_fit
|
|
from sklearn.preprocessing import MinMaxScaler
|
|
import warnings
|
|
import warnings
|
|
warnings.filterwarnings("ignore")
|
|
import os
|
|
import plotly.graph_objects as go
|
|
from datetime import datetime,timedelta
|
|
from plotly.subplots import make_subplots
|
|
import pandas as pd
|
|
import json
|
|
from numerize.numerize import numerize
|
|
|
|
|
|
|
|
|
|
|
|
df= pd.read_excel('response_curves_input_file.xlsx')
|
|
df.dropna(inplace=True)
|
|
df['Date'] = pd.to_datetime(df['Date'])
|
|
df.reset_index(inplace=True)
|
|
|
|
|
|
spend_cols = ['tv_broadcast_spend',
|
|
'tv_cable_spend',
|
|
'stream_video_spend',
|
|
'olv_spend',
|
|
'disp_prospect_spend',
|
|
'disp_retarget_spend',
|
|
'social_prospect_spend',
|
|
'social_retarget_spend',
|
|
'search_brand_spend',
|
|
'search_nonbrand_spend',
|
|
'cm_spend',
|
|
'audio_spend',
|
|
'email_spend']
|
|
spend_cols2 = ['tv_broadcast_spend',
|
|
'tv_cable_spend',
|
|
'stream_video_spend',
|
|
'olv_spend',
|
|
'disp_prospect_spend',
|
|
'disp_retarget_spend',
|
|
'social_prospect_spend',
|
|
'social_retarget_spend',
|
|
'search_brand_spend',
|
|
'search_nonbrand_spend',
|
|
'cm_spend',
|
|
'audio_spend',
|
|
'email_spend', 'Date']
|
|
metric_cols = ['tv_broadcast_grp',
|
|
'tv_cable_grp',
|
|
'stream_video_imp',
|
|
'olv_imp',
|
|
'disp_prospect_imp',
|
|
'disp_retarget_imp',
|
|
'social_prospect_imp',
|
|
'social_retarget_imp',
|
|
'search_brand_imp',
|
|
'search_nonbrand_imp',
|
|
'cm_spend',
|
|
'audio_imp',
|
|
'email_imp']
|
|
channels = [
|
|
'BROADCAST TV',
|
|
'CABLE TV',
|
|
'CONNECTED & OTT TV',
|
|
'VIDEO',
|
|
'DISPLAY PROSPECTING',
|
|
'DISPLAY RETARGETING',
|
|
'SOCIAL PROSPECTING',
|
|
'SOCIAL RETARGETING',
|
|
'SEARCH BRAND',
|
|
'SEARCH NON-BRAND',
|
|
'DIGITAL PARTNERS',
|
|
'AUDIO',
|
|
'EMAIL']
|
|
channels2 = [
|
|
'BROADCAST TV',
|
|
'CABLE TV',
|
|
'CONNECTED & OTT TV',
|
|
'VIDEO',
|
|
'DISPLAY PROSPECTING',
|
|
'DISPLAY RETARGETING',
|
|
'SOCIAL PROSPECTING',
|
|
'SOCIAL RETARGETING',
|
|
'SEARCH BRAND',
|
|
'SEARCH NON-BRAND',
|
|
'DIGITAL PARTNERS',
|
|
'AUDIO',
|
|
'EMAIL','Date']
|
|
contribution_cols = [
|
|
'Broadcast TV_Prospects',
|
|
'Cable TV_Prospects',
|
|
'Connected & OTT TV_Prospects',
|
|
'Video_Prospects',
|
|
'Display Prospecting_Prospects',
|
|
'Display Retargeting_Prospects',
|
|
'Social Prospecting_Prospects',
|
|
'Social Retargeting_Prospects',
|
|
'Search Brand_Prospects',
|
|
'Search Non-brand_Prospects',
|
|
'Digital Partners_Prospects',
|
|
'Audio_Prospects',
|
|
'Email_Prospects']
|
|
|
|
def get_date_range():
|
|
return df['Date'].min(),df['Date'].max()+ timedelta(days=7)
|
|
|
|
def get_default_dates():
|
|
return df['Date'].max()- timedelta(days=21),df['Date'].max()+ timedelta(days=6)
|
|
|
|
|
|
def pie_charts(start_date,end_date):
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
data1 = pd.DataFrame(cur_data[spend_cols].sum().transpose())
|
|
data2 = pd.DataFrame(cur_data[contribution_cols].sum().transpose())
|
|
|
|
data1.index = channels
|
|
data1.columns = ["p"]
|
|
|
|
data2.index = channels
|
|
data2.columns = ["p"]
|
|
|
|
colors = ['#ff2b2b',
|
|
'#0068c9',
|
|
'#83c9ff',
|
|
|
|
'#ffabab',
|
|
'#29b09d',
|
|
'#7defa1',
|
|
'#ff8700',
|
|
'#ffd16a',
|
|
'#6d3fc0',
|
|
'#d5dae5',
|
|
'#309bff',
|
|
'#e9f5ff',
|
|
'#BEBADA'
|
|
]
|
|
|
|
|
|
|
|
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
|
|
|
|
fig.add_trace(go.Pie(labels=channels,
|
|
values=data1["p"],
|
|
name="t2",
|
|
hoverinfo='label+percent',
|
|
textinfo= 'label+percent',
|
|
showlegend= False,textfont=dict(size =10),
|
|
title="Distribution of Spends",
|
|
texttemplate='%{label}: %{percent:.1%}',
|
|
marker=dict(colors=colors)
|
|
), 1, 1)
|
|
|
|
fig.add_trace(go.Pie(labels=channels,
|
|
values=data2["p"],
|
|
name="t2",
|
|
hoverinfo='label+percent',
|
|
textinfo= 'label+percent',
|
|
showlegend= False,
|
|
textfont=dict(size = 10),
|
|
title = "Distribution of Revenue Contributions", marker=dict(colors=colors),
|
|
texttemplate='%{label}: %{percent:.1%}',
|
|
), 1, 2)
|
|
|
|
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
title={
|
|
'text': "Distribution Of Spends And Revenue",
|
|
'font': {
|
|
'size': 24,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
},
|
|
margin=dict(t=130, b=150),
|
|
height=600,
|
|
)
|
|
|
|
fig.add_annotation(
|
|
text=f"{start_date.strftime('%m-%d-%Y')} to {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=18),
|
|
|
|
)
|
|
|
|
return fig
|
|
|
|
def pie_spend(start_date,end_date):
|
|
colors = ['#ff2b2b',
|
|
'#0068c9',
|
|
'#83c9ff',
|
|
|
|
'#ffabab',
|
|
'#29b09d',
|
|
'#7defa1',
|
|
'#ff8700',
|
|
'#ffd16a',
|
|
'#6d3fc0',
|
|
'#d5dae5',
|
|
'#309bff',
|
|
'#e9f5ff',
|
|
'#BEBADA'
|
|
]
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
data = pd.DataFrame(cur_data[spend_cols].sum().transpose())
|
|
data.index = channels
|
|
data.columns = ["p"]
|
|
|
|
fig = go.Figure(data=[go.Pie(
|
|
labels=channels,
|
|
values=data["p"],
|
|
hoverinfo='label+percent',
|
|
textinfo= 'label+percent',
|
|
showlegend= False,
|
|
textfont=dict(size = 10)
|
|
, marker=dict(colors=colors)
|
|
)])
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
title={
|
|
'text': "Distribution Of Spends",
|
|
'font': {
|
|
'size': 24,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
}
|
|
|
|
)
|
|
|
|
fig.add_annotation(
|
|
text=f"{start_date.strftime('%m-%d-%Y')} to {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=18),
|
|
|
|
)
|
|
|
|
|
|
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def waterfall2(start_date1,end_date1,start_date2,end_date2):
|
|
btn_chart = "Month on Month"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start_date1 = pd.to_datetime(start_date1)
|
|
end_date1 = pd.to_datetime(end_date1)
|
|
start_date2 = pd.to_datetime(start_date2)
|
|
end_date2 = pd.to_datetime(end_date2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if start_date1 < df['Date'].min() :
|
|
return "a"
|
|
|
|
cur_data = df[(df['Date'] >= start_date2) & (df['Date'] <= end_date2)]
|
|
prev_data = df[(df['Date'] >= start_date1) & (df['Date'] <= end_date1)]
|
|
|
|
|
|
|
|
data = [
|
|
{'label': 'Previous Period', 'value': round(prev_data[contribution_cols].values.sum(), 1)},
|
|
{'label': 'Broadcast TV', 'value': round(cur_data['Broadcast TV_Prospects'].sum()-prev_data['Broadcast TV_Prospects'].sum(), 1)},
|
|
{'label': 'Cable TV', 'value': round(cur_data['Cable TV_Prospects'].sum()-prev_data['Cable TV_Prospects'].sum())},
|
|
{'label': 'Connected & OTT TV', 'value': round(cur_data['Connected & OTT TV_Prospects'].sum()-prev_data['Connected & OTT TV_Prospects'].sum(), 1)},
|
|
{'label': 'Video', 'value': round(cur_data['Video_Prospects'].sum()-prev_data['Video_Prospects'].sum(), 1)},
|
|
{'label': 'Display Prospecting', 'value': round(cur_data['Display Prospecting_Prospects'].sum()-prev_data['Display Prospecting_Prospects'].sum(), 1)},
|
|
{'label': 'Display Retargeting', 'value': round(cur_data['Display Retargeting_Prospects'].sum()-prev_data['Display Retargeting_Prospects'].sum(), 1)},
|
|
{'label': 'Social Prospecting', 'value': round(cur_data['Social Prospecting_Prospects'].sum()-prev_data['Social Prospecting_Prospects'].sum(), 1)},
|
|
{'label': 'Social Retargeting', 'value': round(cur_data['Social Retargeting_Prospects'].sum()-prev_data['Social Retargeting_Prospects'].sum(), 1)},
|
|
{'label': 'Search Brand', 'value': round(cur_data['Search Brand_Prospects'].sum()-prev_data['Search Brand_Prospects'].sum(), 1)},
|
|
{'label': 'Search Non-brand', 'value': round(cur_data['Search Non-brand_Prospects'].sum()-prev_data['Search Non-brand_Prospects'].sum(), 1)},
|
|
{'label': 'Digital Partners', 'value': round(cur_data['Digital Partners_Prospects'].sum()-prev_data['Digital Partners_Prospects'].sum(), 1)},
|
|
{'label': 'Audio', 'value': round(cur_data['Audio_Prospects'].sum()-prev_data['Audio_Prospects'].sum(), 1)},
|
|
{'label': 'Email', 'value': round(cur_data['Email_Prospects'].sum()-prev_data['Email_Prospects'].sum(), 1)},
|
|
{'label': 'Current Period', 'value': round(cur_data[contribution_cols].values.sum(), 1)}
|
|
]
|
|
|
|
|
|
cumulative = [0]
|
|
for i in range(len(data)):
|
|
cumulative.append(cumulative[-1] + data[i]['value'])
|
|
|
|
|
|
cumulative[-1] = 0
|
|
|
|
|
|
labels = [item['label'] for item in data]
|
|
values = [item['value'] for item in data]
|
|
|
|
|
|
bars = []
|
|
for i in range(len(data)):
|
|
color = '#4A88D9' if i == 0 or i == len(data) - 1 else '#DC5537'
|
|
hover_text = f"<b>{labels[i]}</b><br>Value: {abs(values[i])}"
|
|
|
|
bars.append(go.Bar(
|
|
x=[labels[i]],
|
|
y=[cumulative[i+1] - cumulative[i]],
|
|
base=[cumulative[i]],
|
|
text=[f"{abs(values[i]):,}"],
|
|
textposition='auto',
|
|
hovertemplate=hover_text,
|
|
marker=dict(color=color),
|
|
showlegend=False
|
|
))
|
|
|
|
|
|
fig = go.Figure(data=bars)
|
|
|
|
|
|
if btn_chart == "Month on Month":
|
|
fig.update_layout(
|
|
title=f"Change In MMM Estimated Revenue Contribution"
|
|
,showlegend=False,
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
|
|
)
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{start_date2.strftime('%m-%d-%Y')} to {end_date2.strftime('%m-%d-%Y')} vs. {start_date1.strftime('%m-%d-%Y')} To {end_date1.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else :
|
|
fig.update_layout(
|
|
showlegend=False,
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
|
|
)
|
|
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{start_date2.strftime('%m-%d-%Y')} to {end_date2.strftime('%m-%d-%Y')} vs. {start_date1.strftime('%m-%d-%Y')} To {end_date1.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
|
|
|
|
|
|
return fig
|
|
|
|
def waterfall(start_date,end_date,btn_chart):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
|
|
if btn_chart == "Month on Month":
|
|
start_date_prev = start_date +timedelta(weeks=-4)
|
|
end_date_prev = start_date +timedelta(days=-1)
|
|
else:
|
|
start_date_prev = start_date +timedelta(weeks=-52)
|
|
end_date_prev = start_date_prev +timedelta(weeks=4) +timedelta(days=-1)
|
|
|
|
|
|
|
|
|
|
|
|
prev_data = df[(df['Date'] >= start_date_prev) & (df['Date'] <= end_date_prev)]
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
|
|
|
|
|
|
data = [
|
|
{'label': 'Previous Period', 'value': round(prev_data[contribution_cols].values.sum())},
|
|
{'label': 'Broadcast TV', 'value': round(cur_data['Broadcast TV_Prospects'].sum()-prev_data['Broadcast TV_Prospects'].sum())},
|
|
{'label': 'Cable TV', 'value': round(cur_data['Cable TV_Prospects'].sum()-prev_data['Cable TV_Prospects'].sum())},
|
|
{'label': 'Connected & OTT TV', 'value': round(cur_data['Connected & OTT TV_Prospects'].sum()-prev_data['Connected & OTT TV_Prospects'].sum())},
|
|
{'label': 'Video', 'value': round(cur_data['Video_Prospects'].sum()-prev_data['Video_Prospects'].sum())},
|
|
{'label': 'Display Prospecting', 'value': round(cur_data['Display Prospecting_Prospects'].sum()-prev_data['Display Prospecting_Prospects'].sum())},
|
|
{'label': 'Display Retargeting', 'value': round(cur_data['Display Retargeting_Prospects'].sum()-prev_data['Display Retargeting_Prospects'].sum())},
|
|
{'label': 'Social Prospecting', 'value': round(cur_data['Social Prospecting_Prospects'].sum()-prev_data['Social Prospecting_Prospects'].sum())},
|
|
{'label': 'Social Retargeting', 'value': round(cur_data['Social Retargeting_Prospects'].sum()-prev_data['Social Retargeting_Prospects'].sum())},
|
|
{'label': 'Search Brand', 'value': round(cur_data['Search Brand_Prospects'].sum()-prev_data['Search Brand_Prospects'].sum())},
|
|
{'label': 'Search Non-brand', 'value': round(cur_data['Search Non-brand_Prospects'].sum()-prev_data['Search Non-brand_Prospects'].sum())},
|
|
{'label': 'Digital Partners', 'value': round(cur_data['Digital Partners_Prospects'].sum()-prev_data['Digital Partners_Prospects'].sum())},
|
|
{'label': 'Audio', 'value': round(cur_data['Audio_Prospects'].sum()-prev_data['Audio_Prospects'].sum())},
|
|
{'label': 'Email', 'value': round(cur_data['Email_Prospects'].sum()-prev_data['Email_Prospects'].sum())},
|
|
{'label': 'Current Period', 'value': round(cur_data[contribution_cols].values.sum())}
|
|
]
|
|
|
|
|
|
cumulative = [0]
|
|
for i in range(len(data)):
|
|
cumulative.append(cumulative[-1] + data[i]['value'])
|
|
|
|
|
|
cumulative[-1] = 0
|
|
|
|
|
|
labels = [item['label'] for item in data]
|
|
values = [item['value'] for item in data]
|
|
|
|
|
|
bars = []
|
|
for i in range(len(data)):
|
|
color = '#4A88D9' if i == 0 or i == len(data) - 1 else '#DC5537'
|
|
hover_text = f"<b>{labels[i]}</b><br>Value: {abs(values[i])}"
|
|
|
|
bars.append(go.Bar(
|
|
x=[labels[i]],
|
|
y=[cumulative[i+1] - cumulative[i]],
|
|
base=[cumulative[i]],
|
|
text=[f"{abs(values[i]):,}"],
|
|
textposition='auto',
|
|
hovertemplate=hover_text,
|
|
marker=dict(color=color),
|
|
showlegend=False
|
|
))
|
|
|
|
|
|
fig = go.Figure(data=bars)
|
|
|
|
|
|
if btn_chart == "Month on Month":
|
|
fig.update_layout(
|
|
title=f"Change In MMM Estimated Revenue Contribution"
|
|
,showlegend=False,
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
|
|
)
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{start_date_prev.strftime('%m-%d-%Y')} to {end_date_prev.strftime('%m-%d-%Y')} vs. {start_date.strftime('%m-%d-%Y')} To {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else :
|
|
fig.update_layout(
|
|
showlegend=False,
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
|
|
)
|
|
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{start_date_prev.strftime('%m-%d-%Y')} to {end_date_prev.strftime('%m-%d-%Y')} vs. {start_date.strftime('%m-%d-%Y')} To {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
|
|
|
|
|
|
return fig
|
|
|
|
def shares_df_func(start_date,end_date):
|
|
|
|
|
|
|
|
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
|
|
start_date_prev = start_date +timedelta(weeks=-4)
|
|
end_date_prev = start_date +timedelta(days=-1)
|
|
|
|
prev_data = df[(df['Date'] >= start_date_prev) & (df['Date'] <= end_date_prev)]
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
cur_df1 = pd.DataFrame(cur_data[spend_cols].sum()).reset_index()
|
|
cur_df2 = pd.DataFrame(cur_data[metric_cols].sum()).reset_index()
|
|
cur_df3 = pd.DataFrame(cur_data[contribution_cols].sum()).reset_index()
|
|
|
|
cur_df1.columns = ["channels","cur_total_spend"]
|
|
cur_df2.columns = ["channels","cur_total_support"]
|
|
cur_df3.columns = ["channels","cur_total_contributions"]
|
|
cur_df1["channels"] = channels
|
|
cur_df2["channels"] = channels
|
|
cur_df3["channels"] = channels
|
|
|
|
cur_df1["cur_spend_share"] = (cur_df1["cur_total_spend"]/cur_df1["cur_total_spend"].sum())*100
|
|
cur_df2["cur_support_share"] = (cur_df2["cur_total_support"]/cur_df2["cur_total_support"].sum())*100
|
|
cur_df3["cur_contributions_share"] = (cur_df3["cur_total_contributions"]/cur_df3["cur_total_contributions"].sum())*100
|
|
|
|
prev_df1 = pd.DataFrame(prev_data[spend_cols].sum()).reset_index()
|
|
prev_df2 = pd.DataFrame(prev_data[metric_cols].sum()).reset_index()
|
|
prev_df3 = pd.DataFrame(prev_data[contribution_cols].sum()).reset_index()
|
|
|
|
prev_df1.columns = ["channels","prev_total_spend"]
|
|
prev_df2.columns = ["channels","prev_total_support"]
|
|
prev_df3.columns = ["channels","prev_total_contributions"]
|
|
|
|
prev_df1["channels"] = channels
|
|
prev_df2["channels"] = channels
|
|
prev_df3["channels"] = channels
|
|
|
|
prev_df1["prev_spend_share"] = (prev_df1["prev_total_spend"]/prev_df1["prev_total_spend"].sum())*100
|
|
prev_df2["prev_support_share"] = (prev_df2["prev_total_support"]/prev_df2["prev_total_support"].sum())*100
|
|
prev_df3["prev_contributions_share"] = (prev_df3["prev_total_contributions"]/prev_df3["prev_total_contributions"].sum())*100
|
|
|
|
cur_df = cur_df1.merge(cur_df2,on="channels",how = "inner")
|
|
cur_df = cur_df.merge(cur_df3,on="channels",how = "inner")
|
|
|
|
prev_df = prev_df1.merge(prev_df2,on="channels",how = "inner")
|
|
prev_df = prev_df.merge(prev_df3,on="channels",how = "inner")
|
|
|
|
shares_df = cur_df.merge(prev_df,on = "channels",how = "inner")
|
|
shares_df["Contribution Change"] = (-shares_df["prev_contributions_share"]+shares_df["cur_contributions_share"])/shares_df["prev_contributions_share"]
|
|
shares_df["Support Change"] = (-shares_df["prev_support_share"]+shares_df["cur_support_share"])/shares_df["prev_support_share"]
|
|
shares_df["Spend Change"] = (-shares_df["prev_spend_share"]+shares_df["cur_spend_share"])/shares_df["prev_spend_share"]
|
|
shares_df["Efficiency Index"] = shares_df["cur_contributions_share"]/shares_df["cur_spend_share"]
|
|
shares_df["Effectiveness Index"] = shares_df["cur_support_share"]/shares_df["cur_spend_share"]
|
|
return shares_df
|
|
|
|
def waterfall_table_func(shares_df):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
waterfall_delta_df = shares_df[["channels","Contribution Change","Support Change","Spend Change"]]
|
|
waterfall_delta_df = waterfall_delta_df.rename(columns = {"channels":"METRIC"})
|
|
waterfall_delta_df.index = waterfall_delta_df["METRIC"]
|
|
waterfall_delta_df = waterfall_delta_df.round(2)
|
|
return (waterfall_delta_df[["Contribution Change","Support Change","Spend Change"]].transpose())
|
|
|
|
|
|
def channel_contribution(start_date,end_date):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
|
|
channel_df = pd.DataFrame(cur_data[contribution_cols].sum()).reset_index()
|
|
channel_df.columns = ["channels","contributions"]
|
|
channel_df["channels"] = channels
|
|
|
|
|
|
total_contributions = channel_df['contributions'].sum()
|
|
channel_df['percentage'] = (channel_df['contributions'] / total_contributions) * 100
|
|
|
|
|
|
fig = go.Figure(data=[go.Bar(
|
|
x=channel_df['channels'],
|
|
y=round(channel_df['contributions']),
|
|
marker=dict(color='rgb(74, 136, 217)'),
|
|
text=channel_df.apply(
|
|
lambda row: f"{numerize(row['contributions'])}" + f"\n({row['percentage']:.1f}%)" if row['contributions'] > 1000
|
|
else f"{int(row['contributions']):,}" + f"\n({row['percentage']:.1f}%)",
|
|
axis=1
|
|
),
|
|
textposition='outside'
|
|
)])
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
|
|
|
|
|
|
|
|
title=
|
|
{
|
|
'text': "Media Contribution",
|
|
'font': {
|
|
'size': 28,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
},
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
)
|
|
)
|
|
|
|
|
|
fig.add_annotation(
|
|
text=f"{start_date.strftime('%m-%d-%Y')} to {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
|
|
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def chanel_spends(start_date, end_date):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
|
|
channel_df = pd.DataFrame(cur_data[spend_cols].sum()).reset_index()
|
|
channel_df.columns = ["channels", "spends"]
|
|
channel_df["channels"] = channels
|
|
|
|
|
|
total_spends = channel_df["spends"].sum()
|
|
channel_df["percentage"] = (channel_df["spends"] / total_spends) * 100
|
|
|
|
|
|
fig = go.Figure(data=[go.Bar(
|
|
x=channel_df['channels'],
|
|
y=round(channel_df['spends']),
|
|
marker=dict(color='rgb(74, 136, 217)'),
|
|
text=channel_df.apply(
|
|
lambda row: f"{numerize(row['spends'])}" + f"\n({row['percentage']:.1f}%)" if row['spends'] > 1000 else f"{int(row['spends']):,}"
|
|
+ f"\n({row['percentage']:.1f}%)", axis=1
|
|
),
|
|
|
|
textposition='outside'
|
|
)])
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
|
|
|
|
|
|
title=
|
|
{
|
|
'text': "Media Spends",
|
|
'font': {
|
|
'size': 28,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
},
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Spends ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
)
|
|
)
|
|
|
|
fig.add_annotation(
|
|
text=f"{start_date.strftime('%m-%d-%Y')} to {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
|
|
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def shares_table_func(shares_df):
|
|
|
|
shares_table_df = shares_df[["channels","cur_spend_share","cur_support_share","cur_contributions_share"]].copy()
|
|
|
|
|
|
shares_table_df["ROI"] = (shares_df["cur_total_contributions"] / shares_df["cur_total_spend"]).round(1)
|
|
|
|
|
|
shares_table_df["Effectiveness"] = (shares_df["cur_contributions_share"] * 1000 / shares_df["cur_support_share"]).round(1)
|
|
|
|
shares_table_df = shares_table_df.rename(columns = {"channels":"METRIC",
|
|
"cur_spend_share":"Spend Share",
|
|
"cur_support_share":"Support Share",
|
|
"cur_contributions_share":"Contribution Share"})
|
|
|
|
shares_table_df.index = shares_table_df["METRIC"]
|
|
|
|
for c in ["Spend Share", "Support Share", "Contribution Share"]:
|
|
shares_table_df[c] = shares_table_df[c].astype(int)
|
|
shares_table_df[c] = shares_table_df[c].astype(str) + '%'
|
|
|
|
shares_table_df = shares_table_df[["Spend Share", "Support Share", "Contribution Share", "ROI", "Effectiveness"]].transpose()
|
|
|
|
return shares_table_df
|
|
|
|
def eff_table_func(shares_df):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
media_df = shares_df[['channels', 'cur_total_spend',"cur_total_support", "cur_total_contributions" ,'cur_spend_share',
|
|
'cur_support_share', 'cur_contributions_share', 'Efficiency Index', 'Effectiveness Index']]
|
|
media_df = media_df.rename(columns = {"channels":"MEDIA",
|
|
"cur_total_spend":"TOTAL SPEND",
|
|
"cur_total_support":"TOTAL SUPPORT",
|
|
"cur_total_contributions":"TOTAL CONTRIBUTION",
|
|
|
|
"cur_spend_share":"SPEND SHARE",
|
|
"cur_support_share":"SUPPORT SHARE",
|
|
"cur_contributions_share":"CONTRIBUTION SHARE",
|
|
'Efficiency Index':'EFFICIENCY INDEX',
|
|
'Effectiveness Index' :'EFFECTIVENESS INDEX'
|
|
})
|
|
|
|
media_df.index = media_df["MEDIA"]
|
|
media_df.drop(columns = ["MEDIA"],inplace = True)
|
|
for c in ["TOTAL SPEND","TOTAL SUPPORT","TOTAL CONTRIBUTION"]:
|
|
media_df[c] = media_df[c].astype(int)
|
|
for c in ["SPEND SHARE","SUPPORT SHARE","CONTRIBUTION SHARE"]:
|
|
media_df[c] = media_df[c].astype(int)
|
|
media_df[c] = media_df[c].astype(str)+'%'
|
|
for c in ['EFFICIENCY INDEX','EFFECTIVENESS INDEX']:
|
|
media_df[c] = media_df[c].round(2).astype(str)
|
|
return (media_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cpp(start_date, end_date):
|
|
|
|
|
|
|
|
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
cur_data = cur_data.sort_values(by='Date', ascending=True)
|
|
|
|
fig = go.Figure()
|
|
colors = [
|
|
'rgba(74, 136, 217, 0.8)',
|
|
'rgba(220, 85, 55, 0.8)',
|
|
'rgba(67, 150, 80, 0.8)',
|
|
'rgba(237, 151, 35, 0.8)',
|
|
'rgba(145, 68, 255, 0.8)',
|
|
'rgba(128, 128, 128, 0.8)',
|
|
'rgba(255, 165, 0, 0.8)',
|
|
'rgba(255, 192, 203, 0.8)',
|
|
'rgba(0, 191, 255, 0.8)',
|
|
'rgba(127, 255, 0, 0.8)',
|
|
'rgba(255, 69, 0, 0.8)',
|
|
'rgba(75, 0, 130, 0.8)',
|
|
'rgba(240, 230, 140, 0.8)',
|
|
'rgba(218, 112, 214, 0.8)'
|
|
]
|
|
colors = [
|
|
'#ff2b2b',
|
|
'#0068c9',
|
|
'#83c9ff',
|
|
'#ffabab',
|
|
'#29b09d',
|
|
'#7defa1',
|
|
'#ff8700',
|
|
'#ffd16a',
|
|
'#6d3fc0',
|
|
'#d5dae5',
|
|
'#309bff',
|
|
'#e9f5ff',
|
|
'#BEBADA'
|
|
]
|
|
|
|
for i in range(0, 13):
|
|
spend_col = spend_cols[i]
|
|
metric_col = str(metric_cols[i]).replace("_spend", "_imp")
|
|
|
|
if spend_col == "cm_spend":
|
|
continue
|
|
|
|
cpp_df = cur_data[['Date', spend_col, metric_col]]
|
|
cpp_df[channels[i] + "_cpm"] = cpp_df[spend_col] / cpp_df[metric_col]
|
|
|
|
fig.add_trace(go.Scatter(x=cpp_df['Date'], y=cpp_df[channels[i] + "_cpm"], mode='lines', name=channels[i], line=dict(color=colors[i])))
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
|
|
title={
|
|
'text': "Cost per Support Distribution (CPM)",
|
|
'font': {
|
|
'size': 28,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
},
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="CPM",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
),
|
|
hovermode='x'
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{start_date.strftime('%m-%d-%Y')} to {end_date.strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
return fig
|
|
|
|
|
|
def base_decomp():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
base_decomp_df = df[['Date','Unemployment', 'Competition','Trend','Seasonality','Base_0']]
|
|
base_decomp_df = base_decomp_df.sort_values(by='Date', ascending=True)
|
|
|
|
fig = go.Figure()
|
|
colors = ['#ff2b2b',
|
|
'#0068c9',
|
|
'#83c9ff',
|
|
]
|
|
|
|
fig.add_trace(go.Scatter(x=base_decomp_df['Date'], y=base_decomp_df['Base_0'], mode='lines', name='Trend and Seasonality',line=dict(color=colors[0])))
|
|
fig.add_trace(go.Scatter(x=base_decomp_df['Date'], y=base_decomp_df['Unemployment'], mode='lines', name='Unemployment',line=dict(color=colors[1])))
|
|
fig.add_trace(go.Scatter(x=base_decomp_df['Date'], y=base_decomp_df['Competition'], mode='lines', name='Competition',line=dict(color=colors[2])))
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
title=
|
|
{
|
|
'text': "Base Decomposition",
|
|
'font': {
|
|
'size': 28,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
},
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=True,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
),
|
|
hovermode='x'
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{base_decomp_df['Date'].min().strftime('%m-%d-%Y')} to {(base_decomp_df['Date'].max()+timedelta(days=6)).strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
return fig
|
|
|
|
def media_decomp():
|
|
|
|
|
|
|
|
|
|
|
|
df['base'] = df[ 'Base_0']+df['Unemployment']+df['Competition']
|
|
cols = ['Date',
|
|
'base',
|
|
'Broadcast TV_Prospects',
|
|
'Cable TV_Prospects',
|
|
'Connected & OTT TV_Prospects',
|
|
'Video_Prospects',
|
|
'Display Prospecting_Prospects',
|
|
'Display Retargeting_Prospects',
|
|
'Social Prospecting_Prospects',
|
|
'Social Retargeting_Prospects',
|
|
'Search Brand_Prospects',
|
|
'Search Non-brand_Prospects',
|
|
'Digital Partners_Prospects',
|
|
'Audio_Prospects',
|
|
'Email_Prospects',
|
|
]
|
|
media_decomp_df = df[cols]
|
|
|
|
media_decomp_df = media_decomp_df.sort_values(by='Date', ascending=True)
|
|
|
|
|
|
cumulative_df = media_decomp_df.copy()
|
|
|
|
|
|
|
|
media_cols = media_decomp_df.columns
|
|
for i in range(2,len(media_cols)):
|
|
|
|
cumulative_df[media_cols[i]] = cumulative_df[media_cols[i]] + cumulative_df[media_cols[i-1]]
|
|
|
|
|
|
|
|
fig = go.Figure()
|
|
|
|
colors =colors = [
|
|
'rgba(74, 136, 217, 0.8)',
|
|
'rgba(220, 85, 55, 0.8)',
|
|
'rgba(67, 150, 80, 0.8)',
|
|
'rgba(237, 151, 35, 0.8)',
|
|
'rgba(145, 68, 255, 0.8)',
|
|
'rgba(128, 128, 128, 0.8)',
|
|
'rgba(255, 165, 0, 0.8)',
|
|
'rgba(255, 192, 203, 0.8)',
|
|
'rgba(0, 191, 255, 0.8)',
|
|
'rgba(127, 255, 0, 0.8)',
|
|
'rgba(255, 69, 0, 0.8)',
|
|
'rgba(75, 0, 130, 0.8)',
|
|
'rgba(240, 230, 140, 0.8)',
|
|
'rgba(218, 112, 214, 0.8)'
|
|
]
|
|
|
|
for idx, channel in enumerate(media_decomp_df.columns[1:]):
|
|
fig.add_trace(go.Scatter(
|
|
x=media_decomp_df['Date'],
|
|
y=cumulative_df[channel],
|
|
fill='tonexty' if idx > 0 else 'tozeroy',
|
|
mode='none',
|
|
name=str.split(channel,'_')[0],
|
|
text=media_decomp_df[channel],
|
|
hoverinfo='x+y+text',
|
|
fillcolor=colors[idx]
|
|
))
|
|
|
|
|
|
fig.update_layout(
|
|
|
|
title=
|
|
{
|
|
'text': "Media Decomposition",
|
|
'font': {
|
|
'size': 28,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
},
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
)
|
|
)
|
|
fig.add_annotation(
|
|
text=f"{media_decomp_df['Date'].min().strftime('%m-%d-%Y')} to {(media_decomp_df['Date'].max()+timedelta(days=6)).strftime('%m-%d-%Y')}",
|
|
x=0,
|
|
y=1.15,
|
|
xref="x domain",
|
|
yref="y domain",
|
|
showarrow=False,
|
|
font=dict(size=16),
|
|
|
|
)
|
|
return fig
|
|
|
|
def mmm_model_quality():
|
|
base_df = df[['Date',"Y_hat","Y"]]
|
|
fig = go.Figure()
|
|
|
|
|
|
fig.add_trace(go.Scatter(x=base_df['Date'], y=base_df['Y_hat'], mode='lines', name='Predicted',line=dict(color='#CC5500') ))
|
|
fig.add_trace(go.Scatter(x=base_df['Date'], y=base_df['Y'], mode='lines', name='Actual (Revenue)',line=dict(color='#4B88FF')))
|
|
|
|
|
|
|
|
fig.update_layout(
|
|
title={
|
|
'text': "Model Predicted v/s Actual Revenue",
|
|
'font': {
|
|
'size': 24,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
}
|
|
|
|
,
|
|
|
|
|
|
|
|
xaxis=dict(
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
title="Revenue ($)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
),
|
|
hovermode='x'
|
|
)
|
|
|
|
return(fig)
|
|
|
|
def media_data():
|
|
|
|
json_file_path = "all_solutions_2024-05-09.json"
|
|
|
|
with open(json_file_path, 'r') as file:
|
|
json_data = json.load(file)
|
|
|
|
|
|
extracted_data = []
|
|
|
|
|
|
for params_type in ["control_params","other_params","media_params"]:
|
|
for media, params in json_data['solution_0']['solution'][params_type].items():
|
|
try:
|
|
extracted_data.append({
|
|
'category': media,
|
|
'half_life': params['half_life'],
|
|
'coeff': params['coeff']
|
|
})
|
|
except:
|
|
extracted_data.append({
|
|
'category':media,
|
|
'half_life': None,
|
|
'coeff': params['coeff']
|
|
})
|
|
|
|
media_df = pd.DataFrame(extracted_data)
|
|
return media_df
|
|
|
|
def elasticity_and_media(media_df):
|
|
|
|
fig = make_subplots(rows=1, cols=2, subplot_titles=("Chart 1", "Chart 2"))
|
|
fig.add_trace(
|
|
go.Bar(
|
|
x=media_df['coeff'],
|
|
y=media_df['category'],
|
|
orientation='h',
|
|
marker_color='rgba(75, 136, 257, 1)',
|
|
text= media_df['coeff'].round(2),
|
|
textposition="outside"
|
|
),row=1, col=1
|
|
)
|
|
|
|
fig.add_trace(
|
|
go.Bar(
|
|
x=media_df[media_df['half_life'].isnull()==False]['half_life'],
|
|
y=media_df[media_df['half_life'].isnull()==False]['category'],
|
|
orientation='h',
|
|
marker_color='rgba(75, 136, 257, 1)',
|
|
|
|
textposition="outside"
|
|
),row=1, col=2
|
|
)
|
|
fig.update_layout(
|
|
margin=dict(l=40, r=40, t=40, b=40),
|
|
)
|
|
|
|
return fig
|
|
|
|
def elasticity(media_df):
|
|
fig = go.Figure()
|
|
|
|
fig.add_trace(go.Bar(
|
|
|
|
x=media_df['coeff'],
|
|
y=media_df['category'],
|
|
orientation='h',
|
|
marker_color='rgba(75, 136, 257, 1)',
|
|
text= media_df['coeff'].round(2),
|
|
textposition="outside"
|
|
))
|
|
|
|
|
|
fig.update_layout(
|
|
title={
|
|
'text': "Media And Baseline Elasticity",
|
|
'font': {
|
|
'size': 24,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
}
|
|
|
|
,
|
|
|
|
xaxis=dict(
|
|
title="Elasticity (coefficient)",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=False,
|
|
),
|
|
margin=dict(r=10)
|
|
|
|
|
|
|
|
)
|
|
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def half_life(media_df):
|
|
fig = go.Figure()
|
|
|
|
fig.add_trace(go.Bar(
|
|
|
|
x=media_df[media_df['half_life'].isnull()==False]['half_life'],
|
|
y=media_df[media_df['half_life'].isnull()==False]['category'],
|
|
orientation='h',
|
|
marker_color='rgba(75, 136, 257, 1)',
|
|
text= media_df[media_df['half_life'].isnull()==False]['half_life'].round(2),
|
|
textposition="outside"
|
|
))
|
|
|
|
|
|
fig.update_layout(
|
|
title={
|
|
'text': "Media Half-life",
|
|
'font': {
|
|
'size': 24,
|
|
'family': 'Arial',
|
|
'color': 'black',
|
|
|
|
}
|
|
}
|
|
|
|
,
|
|
xaxis=dict(
|
|
title="Weeks",
|
|
showgrid=True,
|
|
gridcolor='lightgray',
|
|
griddash='dot',
|
|
zeroline=False,
|
|
),
|
|
yaxis=dict(
|
|
|
|
showgrid=False,
|
|
gridcolor='gray',
|
|
zeroline=False,
|
|
),margin=dict(l=20)
|
|
|
|
|
|
|
|
)
|
|
return fig
|
|
|
|
|
|
|
|
n = 104
|
|
k = 18
|
|
|
|
def calculate_aic(y, y_hat):
|
|
n = len(y)
|
|
sse = np.sum((y - y_hat) ** 2)
|
|
aic = n * np.log(sse / n) + 2 * k
|
|
return aic
|
|
|
|
def calculate_bic(y, y_hat):
|
|
n = len(y)
|
|
sse = np.sum((y - y_hat) ** 2)
|
|
bic = n * np.log(sse / n) + k * np.log(n)
|
|
return bic
|
|
def calculate_r_squared(y, y_hat):
|
|
ss_total = np.sum((y - np.mean(y)) ** 2)
|
|
ss_residual = np.sum((y - y_hat) ** 2)
|
|
r_squared = 1 - (ss_residual / ss_total)
|
|
return r_squared
|
|
|
|
|
|
def calculate_adjusted_r_squared(y, y_hat):
|
|
n = len(y)
|
|
r_squared = calculate_r_squared(y, y_hat)
|
|
adjusted_r_squared = 1 - ((1 - r_squared) * (n - 1) / (n - k - 1))
|
|
return adjusted_r_squared
|
|
|
|
|
|
def calculate_mape(y, y_hat):
|
|
mape = np.mean(np.abs((y - y_hat) / y)) * 100
|
|
return mape
|
|
|
|
def model_metrics_table_func():
|
|
model_metrics_df = pd.DataFrame([calculate_r_squared(df["Y"], df["Y_hat"]),
|
|
calculate_adjusted_r_squared(df["Y"], df["Y_hat"]),
|
|
calculate_mape(df["Y"], df["Y_hat"]),
|
|
calculate_aic(df["Y"], df["Y_hat"]),
|
|
calculate_bic(df["Y"], df["Y_hat"])])
|
|
model_metrics_df.index = ["R-squared","Adjusted R-squared","MAPE","AIC","BIC"]
|
|
model_metrics_df = model_metrics_df.transpose()
|
|
|
|
|
|
model_metrics_df2 = pd.DataFrame(model_metrics_df.values,columns=["R-squared","Adjusted R-squared","MAPE","AIC","BIC"] )
|
|
|
|
|
|
model_metrics_df2["R-squared"] = model_metrics_df2["R-squared"].apply(lambda x: "{:.2%}".format(x))
|
|
model_metrics_df2["Adjusted R-squared"] = model_metrics_df2["Adjusted R-squared"].apply(lambda x: "{:.2%}".format(x))
|
|
model_metrics_df2["MAPE"] = (model_metrics_df2["MAPE"]/100).apply(lambda x: "{:.2%}".format(x))
|
|
model_metrics_df2["AIC"] = model_metrics_df2["AIC"].round(0)
|
|
model_metrics_df2["BIC"] = model_metrics_df2["BIC"].round(0)
|
|
model_metrics_df2.index = [" "]
|
|
|
|
return model_metrics_df2
|
|
|
|
def get_month_name(month_number):
|
|
|
|
months = ["January", "February", "March", "April", "May", "June",
|
|
"July", "August", "September", "October", "November", "December"]
|
|
if 1 <= month_number <= 12:
|
|
return months[month_number - 1]
|
|
else:
|
|
return "Invalid month number"
|
|
|
|
|
|
|
|
def scenario_spend_forecasting(delta_df,start_date,end_date):
|
|
|
|
key_df = pd.DataFrame()
|
|
key_df["Channel_name"] = ["Email",
|
|
"DisplayRetargeting",
|
|
"\xa0Video",
|
|
"BroadcastTV",
|
|
"SocialRetargeting",
|
|
"Connected&OTTTV",
|
|
"SearchBrand",
|
|
"Audio",
|
|
"SocialProspecting",
|
|
"CableTV",
|
|
"DisplayProspecting",
|
|
"SearchNon-brand",
|
|
"DigitalPartners"]
|
|
key_df["Channels"] = [
|
|
"EMAIL",
|
|
"DISPLAY RETARGETING",
|
|
"VIDEO",
|
|
"BROADCAST TV",
|
|
"SOCIAL RETARGETING",
|
|
"CONNECTED & OTT TV",
|
|
"SEARCH BRAND",
|
|
"AUDIO",
|
|
"SOCIAL PROSPECTING",
|
|
"CABLE TV",
|
|
"DISPLAY PROSPECTING",
|
|
"SEARCH NON-BRAND",
|
|
"DIGITAL PARTNERS"
|
|
]
|
|
|
|
|
|
start_date = pd.to_datetime(start_date)
|
|
end_date = pd.to_datetime(end_date)
|
|
|
|
cur_data = df[(df['Date'] >= start_date) & (df['Date'] <= end_date)]
|
|
cur_data["Month"] = cur_data["Date"].dt.month
|
|
|
|
cur_data["Month year"] = cur_data["Month"].apply(get_month_name) + ' ' +(cur_data["Date"].dt.year+1).astype(str)
|
|
grp_cols = ['tv_broadcast_spend',
|
|
'tv_cable_spend',
|
|
'stream_video_spend',
|
|
'olv_spend',
|
|
'disp_prospect_spend',
|
|
'disp_retarget_spend',
|
|
'social_prospect_spend',
|
|
'social_retarget_spend',
|
|
'search_brand_spend',
|
|
'search_nonbrand_spend',
|
|
'cm_spend',
|
|
'audio_spend',
|
|
'email_spend',
|
|
"Month",
|
|
"Month year"]
|
|
|
|
data2 = cur_data[grp_cols].groupby("Month year").sum()
|
|
|
|
data2.columns = [
|
|
'BROADCAST TV',
|
|
'CABLE TV',
|
|
'CONNECTED & OTT TV',
|
|
'VIDEO',
|
|
'DISPLAY PROSPECTING',
|
|
'DISPLAY RETARGETING',
|
|
'SOCIAL PROSPECTING',
|
|
'SOCIAL RETARGETING',
|
|
'SEARCH BRAND',
|
|
'SEARCH NON-BRAND',
|
|
'DIGITAL PARTNERS',
|
|
'AUDIO',
|
|
'EMAIL',
|
|
"Month"]
|
|
data2 = data2.sort_values("Month")
|
|
data2.drop(columns = ["Month"], inplace = True)
|
|
|
|
key_df = pd.DataFrame()
|
|
key_df["Channel_name"] = ["Email","DisplayRetargeting","\xa0Video","BroadcastTV","SocialRetargeting","Connected&OTTTV","SearchBrand","Audio","SocialProspecting","CableTV","DisplayProspecting","SearchNon-brand","DigitalPartners"]
|
|
key_df["Channels"] = ["EMAIL","DISPLAY RETARGETING","VIDEO","BROADCAST TV","SOCIAL RETARGETING","CONNECTED & OTT TV","SEARCH BRAND","AUDIO","SOCIAL PROSPECTING","CABLE TV","DISPLAY PROSPECTING","SEARCH NON-BRAND","DIGITAL PARTNERS"]
|
|
delta_df = delta_df.merge(key_df,on = "Channel_name",how = "inner")
|
|
|
|
|
|
data3 = data2.copy()
|
|
for channel in delta_df["Channels"]:
|
|
|
|
delta_percent = delta_df[delta_df["Channels"]==channel]["Delta_percent"].iloc[0]
|
|
|
|
data3[channel] = data3[channel]*(1+delta_percent/100)
|
|
|
|
|
|
|
|
|
|
|
|
output_df2 = data3.copy()
|
|
|
|
|
|
delta_df2 = pd.DataFrame(data = delta_df["Delta_percent"].values,index = delta_df["Channels"])
|
|
|
|
output_df1 = (pd.DataFrame(data2.sum()).transpose()).append(pd.DataFrame(data3.sum()).transpose()).append(delta_df2.transpose())
|
|
output_df1.index = ["Last Year Spends", "Forecasted Spends","Spends Change"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return output_df1,output_df2
|
|
|
|
def scenario_spend_forecasting2(delta_df,start_date,end_date):
|
|
|
|
key_df = pd.DataFrame()
|
|
key_df["Channel_name"] = ["Email",
|
|
"DisplayRetargeting",
|
|
"\xa0Video",
|
|
"BroadcastTV",
|
|
"SocialRetargeting",
|
|
"Connected&OTTTV",
|
|
"SearchBrand",
|
|
"Audio",
|
|
"SocialProspecting",
|
|
"CableTV",
|
|
"DisplayProspecting",
|
|
"SearchNon-brand",
|
|
"DigitalPartners"]
|
|
key_df["Channels"] = [
|
|
"EMAIL",
|
|
"DISPLAY RETARGETING",
|
|
"VIDEO",
|
|
"BROADCAST TV",
|
|
"SOCIAL RETARGETING",
|
|
"CONNECTED & OTT TV",
|
|
"SEARCH BRAND",
|
|
"AUDIO",
|
|
"SOCIAL PROSPECTING",
|
|
"CABLE TV",
|
|
"DISPLAY PROSPECTING",
|
|
"SEARCH NON-BRAND",
|
|
"DIGITAL PARTNERS"
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return key_df
|
|
|
|
|