|
import streamlit as st |
|
from classes import numerize |
|
import pandas as pd |
|
from utilities import ( |
|
format_numbers, |
|
load_local_css, |
|
set_header, |
|
name_formating, |
|
project_selection |
|
) |
|
import pickle |
|
import yaml |
|
from yaml import SafeLoader |
|
from classes import class_from_dict |
|
import plotly.express as px |
|
import numpy as np |
|
import plotly.graph_objects as go |
|
import pandas as pd |
|
from plotly.subplots import make_subplots |
|
import sqlite3 |
|
from utilities import update_db |
|
from collections import OrderedDict |
|
import os |
|
|
|
st.set_page_config(layout="wide") |
|
load_local_css("styles.css") |
|
set_header() |
|
|
|
st.empty() |
|
st.header("Model Result Analysis") |
|
|
|
def get_saved_scenarios_dict(): |
|
|
|
saved_scenarios_dict_path = os.path.join( |
|
st.session_state["project_path"], "saved_scenarios.pkl" |
|
) |
|
|
|
|
|
if os.path.exists(saved_scenarios_dict_path): |
|
with open(saved_scenarios_dict_path, "rb") as f: |
|
saved_scenarios_dict = pickle.load(f) |
|
else: |
|
saved_scenarios_dict = OrderedDict() |
|
|
|
return saved_scenarios_dict |
|
|
|
|
|
|
|
def format_value(value): |
|
return round(value, 4) if value < 1 else round(value, 1) |
|
|
|
|
|
|
|
def convert_to_serializable(obj): |
|
if isinstance(obj, np.ndarray): |
|
return obj.tolist() |
|
elif isinstance(obj, dict): |
|
return {key: convert_to_serializable(value) for key, value in obj.items()} |
|
elif isinstance(obj, list): |
|
return [convert_to_serializable(element) for element in obj] |
|
elif isinstance(obj, (int, float, str, bool, type(None))): |
|
return obj |
|
else: |
|
|
|
return str(obj) |
|
|
|
|
|
if 'username' not in st.session_state: |
|
st.session_state['username']=None |
|
|
|
if "project_name" not in st.session_state: |
|
st.session_state["project_name"] = None |
|
|
|
if "project_dct" not in st.session_state: |
|
project_selection() |
|
|
|
if "project_path" not in st.session_state: |
|
st.stop() |
|
if 'username' in st.session_state and st.session_state['username'] is not None: |
|
|
|
data_path = os.path.join(st.session_state["project_path"], "data_import.pkl") |
|
|
|
try: |
|
with open(data_path, "rb") as f: |
|
data = pickle.load(f) |
|
except Exception as e: |
|
st.error(f"Please import data from the Data Import Page") |
|
st.stop() |
|
|
|
saved_scenarios_dict = get_saved_scenarios_dict() |
|
scenarios_list = list(saved_scenarios_dict.keys()) |
|
|
|
|
|
|
|
if len(scenarios_list) == 0: |
|
|
|
st.warning("No scenarios saved. Please save a scenario to load.", icon="⚠️") |
|
st.stop() |
|
|
|
|
|
selected_scenario = st.selectbox( |
|
"Pick a Scenario", sorted(scenarios_list), key="selected_scenario" |
|
) |
|
selected_scenario_data = saved_scenarios_dict[selected_scenario] |
|
|
|
|
|
metrics_name = selected_scenario_data["metrics_selected"] |
|
panel_name = selected_scenario_data["panel_selected"] |
|
optimization_name = selected_scenario_data["optimization"] |
|
|
|
|
|
|
|
|
|
download_col, delete_col = st.columns(2) |
|
|
|
|
|
channels_list = list(selected_scenario_data["channels"].keys()) |
|
|
|
|
|
channels_data = [] |
|
|
|
|
|
for channel in channels_list: |
|
channel_conversion_rate = selected_scenario_data["channels"][channel][ |
|
"conversion_rate" |
|
] |
|
channel_actual_spends = ( |
|
selected_scenario_data["channels"][channel]["actual_total_spends"] |
|
* channel_conversion_rate |
|
) |
|
channel_optimized_spends = ( |
|
selected_scenario_data["channels"][channel]["modified_total_spends"] |
|
* channel_conversion_rate |
|
) |
|
|
|
channel_actual_metrics = selected_scenario_data["channels"][channel][ |
|
"actual_total_sales" |
|
] |
|
channel_optimized_metrics = selected_scenario_data["channels"][channel][ |
|
"modified_total_sales" |
|
] |
|
|
|
channel_roi_mroi_data = selected_scenario_data["channel_roi_mroi"][channel] |
|
|
|
|
|
actual_roi = channel_roi_mroi_data["actual_roi"] |
|
optimized_roi = channel_roi_mroi_data["optimized_roi"] |
|
actual_mroi = channel_roi_mroi_data["actual_mroi"] |
|
optimized_mroi = channel_roi_mroi_data["optimized_mroi"] |
|
|
|
|
|
spends_per_metrics_actual = channel_actual_spends / channel_actual_metrics |
|
spends_per_metrics_optimized = channel_optimized_spends / channel_optimized_metrics |
|
|
|
|
|
channels_data.append( |
|
{ |
|
"Channel Name": channel, |
|
"Spends Actual": channel_actual_spends, |
|
"Spends Optimized": channel_optimized_spends, |
|
f"{metrics_name} Actual": channel_actual_metrics, |
|
f"{name_formating(metrics_name)} Optimized": numerize( |
|
channel_optimized_metrics |
|
), |
|
"ROI Actual": format_value(actual_roi), |
|
"ROI Optimized": format_value(optimized_roi), |
|
"MROI Actual": format_value(actual_mroi), |
|
"MROI Optimized": format_value(optimized_mroi), |
|
f"Spends per {name_formating(metrics_name)} Actual": numerize( |
|
spends_per_metrics_actual |
|
), |
|
f"Spends per {name_formating(metrics_name)} Optimized": numerize( |
|
spends_per_metrics_optimized |
|
), |
|
} |
|
) |
|
|
|
|
|
summary_df_sorted = pd.DataFrame(channels_data).sort_values(by=['Spends Optimized']) |
|
|
|
|
|
summary_df_sorted['Delta']=summary_df_sorted['Spends Actual']-summary_df_sorted['Spends Optimized'] |
|
|
|
|
|
summary_df_sorted['Delta_percent']= np.round((summary_df_sorted['Delta']) / summary_df_sorted['Spends Actual'] * 100,2) |
|
|
|
|
|
|
|
|
|
st.header("Optimized Spends Overview") |
|
|
|
channel_colors = px.colors.qualitative.Plotly |
|
|
|
fig = make_subplots( |
|
rows=1, |
|
cols=3, |
|
subplot_titles=("Actual Spend", "Spends Optimized", "Delta"), |
|
horizontal_spacing=0.05, |
|
) |
|
|
|
for i, channel in enumerate(summary_df_sorted["Channel Name"].unique()): |
|
channel_df = summary_df_sorted[ |
|
summary_df_sorted["Channel Name"] == channel |
|
] |
|
channel_color = channel_colors[i % len(channel_colors)] |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Spends Actual"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Spends Actual"].apply(format_numbers), |
|
marker_color=channel_color, |
|
orientation="h", |
|
), |
|
row=1, |
|
col=1, |
|
) |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Spends Optimized"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Spends Optimized"].apply(format_numbers), |
|
marker_color=channel_color, |
|
orientation="h", |
|
showlegend=False, |
|
), |
|
row=1, |
|
col=2, |
|
) |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Delta_percent"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Delta_percent"].apply(lambda x: f"{x:.0f}%"), |
|
marker_color=channel_color, |
|
orientation="h", |
|
showlegend=False, |
|
), |
|
row=1, |
|
col=3, |
|
) |
|
fig.update_layout(height=600, width=900, title="", showlegend=False) |
|
|
|
fig.update_yaxes(showticklabels=False, row=1, col=2) |
|
fig.update_yaxes(showticklabels=False, row=1, col=3) |
|
|
|
fig.update_xaxes(showticklabels=False, row=1, col=1) |
|
fig.update_xaxes(showticklabels=False, row=1, col=2) |
|
fig.update_xaxes(showticklabels=False, row=1, col=3) |
|
|
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
|
|
summary_df_sorted["Perc_alloted"] = np.round( |
|
summary_df_sorted["Spends Optimized"] |
|
/ summary_df_sorted["Spends Optimized"].sum(), |
|
2, |
|
) |
|
st.header(" Budget Allocation") |
|
|
|
fig = make_subplots( |
|
rows=1, |
|
cols=2, |
|
subplot_titles=("Spends Optimized", "% Split"), |
|
horizontal_spacing=0.05, |
|
) |
|
|
|
for i, channel in enumerate(summary_df_sorted["Channel Name"].unique()): |
|
channel_df = summary_df_sorted[ |
|
summary_df_sorted["Channel Name"] == channel |
|
] |
|
channel_color = channel_colors[i % len(channel_colors)] |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Spends Optimized"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Spends Optimized"].apply(format_numbers), |
|
marker_color=channel_color, |
|
orientation="h", |
|
), |
|
row=1, |
|
col=1, |
|
) |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Perc_alloted"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Perc_alloted"].apply(lambda x: f"{100*x:.0f}%"), |
|
marker_color=channel_color, |
|
orientation="h", |
|
showlegend=False, |
|
), |
|
row=1, |
|
col=2, |
|
) |
|
|
|
fig.update_layout(height=600, width=900, title="", showlegend=False) |
|
|
|
fig.update_yaxes(showticklabels=False, row=1, col=2) |
|
fig.update_yaxes(showticklabels=False, row=1, col=3) |
|
|
|
fig.update_xaxes(showticklabels=False, row=1, col=1) |
|
fig.update_xaxes(showticklabels=False, row=1, col=2) |
|
fig.update_xaxes(showticklabels=False, row=1, col=3) |
|
|
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.session_state["cleaned_data"] = data["final_df"] |
|
st.session_state["category_dict"] = data["bin_dict"] |
|
|
|
effectiveness_overall=pd.DataFrame() |
|
|
|
response_metrics=list( |
|
*[ |
|
st.session_state["category_dict"][key] |
|
for key in st.session_state["category_dict"].keys() |
|
if key == "Response Metrics" |
|
] |
|
) |
|
|
|
effectiveness_overall=st.session_state["cleaned_data"][response_metrics].sum().reset_index() |
|
|
|
effectiveness_overall.columns=['ResponseMetricName','ResponseMetricValue'] |
|
|
|
|
|
effectiveness_overall["Efficiency"] = effectiveness_overall[ |
|
"ResponseMetricValue" |
|
].map(lambda x: x / summary_df_sorted["Spends Optimized"].sum()) |
|
|
|
|
|
columns6 = st.columns(3) |
|
|
|
effectiveness_overall.sort_values( |
|
by=["ResponseMetricValue"], ascending=False, inplace=True |
|
) |
|
effectiveness_overall = np.round(effectiveness_overall, 2) |
|
|
|
columns4 = st.columns([0.55, 0.45]) |
|
|
|
effectiveness_overall=effectiveness_overall.sort_values(by=['ResponseMetricValue']) |
|
|
|
with columns4[0]: |
|
fig = px.funnel( |
|
effectiveness_overall, |
|
x="ResponseMetricValue", |
|
y="ResponseMetricName", |
|
color="ResponseMetricName", |
|
title="Effectiveness", |
|
) |
|
fig.update_layout( |
|
showlegend=False, |
|
yaxis=dict( |
|
tickmode="array" |
|
), |
|
) |
|
fig.update_traces( |
|
textinfo="value", |
|
textposition="inside", |
|
texttemplate="%{x:.2s} ", |
|
hoverinfo="y+x+percent initial", |
|
) |
|
fig.update_traces( |
|
marker=dict(line=dict(color="black", width=2)), |
|
selector=dict(marker=dict(color="blue")), |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
with columns4[1]: |
|
fig1 = px.bar( |
|
effectiveness_overall.sort_values(by=['ResponseMetricValue'],ascending=False), |
|
x="Efficiency", |
|
y="ResponseMetricName", |
|
color="ResponseMetricName", |
|
text_auto=True, |
|
title="Efficiency", |
|
) |
|
|
|
|
|
fig1.update_traces( |
|
customdata=effectiveness_overall["Efficiency"], textposition="auto" |
|
) |
|
fig1.update_layout(showlegend=False) |
|
fig1.update_yaxes(title="", showticklabels=False) |
|
fig1.update_xaxes(title="", showticklabels=False) |
|
fig1.update_xaxes(tickfont=dict(size=20)) |
|
fig1.update_yaxes(tickfont=dict(size=20)) |
|
st.plotly_chart(fig1, use_container_width=True) |
|
|
|
|
|
|
|
|
|
st.header("Return Forecast by Media Channel") |
|
|
|
with st.expander("Return Forecast by Media Channel"): |
|
|
|
metric_data = [metrics_name] |
|
|
|
metric = st.selectbox("Select Metric", metric_data, index=0) |
|
|
|
metric=metric+ " " + "Actual" |
|
|
|
effectiveness = summary_df_sorted[metric] |
|
|
|
summary_df_sorted['Efficiency']= summary_df_sorted[metric]/summary_df_sorted['Spends Optimized'] |
|
|
|
|
|
channel_colors = px.colors.qualitative.Plotly |
|
|
|
fig = make_subplots( |
|
rows=1, |
|
cols=3, |
|
subplot_titles=("Optimized Spends", "Effectiveness", "Efficiency"), |
|
horizontal_spacing=0.05, |
|
) |
|
|
|
for i, channel in enumerate(summary_df_sorted["Channel Name"].unique()): |
|
channel_df = summary_df_sorted[ |
|
summary_df_sorted["Channel Name"] == channel |
|
] |
|
channel_color = channel_colors[i % len(channel_colors)] |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Spends Optimized"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Spends Optimized"].apply(format_numbers), |
|
marker_color=channel_color, |
|
orientation="h", |
|
), |
|
row=1, |
|
col=1, |
|
) |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df[metric], |
|
y=channel_df["Channel Name"], |
|
text=channel_df[metric].apply(format_numbers), |
|
marker_color=channel_color, |
|
orientation="h", |
|
showlegend=False, |
|
), |
|
row=1, |
|
col=2, |
|
) |
|
|
|
fig.add_trace( |
|
go.Bar( |
|
x=channel_df["Efficiency"], |
|
y=channel_df["Channel Name"], |
|
text=channel_df["Efficiency"].apply(lambda x: f"{x:.2f}"), |
|
marker_color=channel_color, |
|
orientation="h", |
|
showlegend=False, |
|
), |
|
row=1, |
|
col=3, |
|
) |
|
|
|
fig.update_layout( |
|
height=600, |
|
width=900, |
|
title="Media Channel Performance", |
|
showlegend=False, |
|
) |
|
|
|
fig.update_yaxes(showticklabels=False, row=1, col=2) |
|
fig.update_yaxes(showticklabels=False, row=1, col=3) |
|
|
|
fig.update_xaxes(showticklabels=False, row=1, col=1) |
|
fig.update_xaxes(showticklabels=False, row=1, col=2) |
|
fig.update_xaxes(showticklabels=False, row=1, col=3) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|