RFI / pages /11_Model_Optimized_Recommendation.py
Manoj
latest
fde220d
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():
# Path to the saved scenarios file
saved_scenarios_dict_path = os.path.join(
st.session_state["project_path"], "saved_scenarios.pkl"
)
# Load existing scenarios if the file exists
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
# Function to format values based on their size
def format_value(value):
return round(value, 4) if value < 1 else round(value, 1)
# Function to recursively convert non-serializable types to serializable ones
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:
# Fallback: convert the object to a string
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()
# Get saved scenarios dictionary and scenario name list
saved_scenarios_dict = get_saved_scenarios_dict()
scenarios_list = list(saved_scenarios_dict.keys())
#st.write(saved_scenarios_dict)
# Check if the list of saved scenarios is empty
if len(scenarios_list) == 0:
# Display a warning message if no scenarios are saved
st.warning("No scenarios saved. Please save a scenario to load.", icon="⚠️")
st.stop()
# Display a dropdown saved scenario list
selected_scenario = st.selectbox(
"Pick a Scenario", sorted(scenarios_list), key="selected_scenario"
)
selected_scenario_data = saved_scenarios_dict[selected_scenario]
# Scenarios Name
metrics_name = selected_scenario_data["metrics_selected"]
panel_name = selected_scenario_data["panel_selected"]
optimization_name = selected_scenario_data["optimization"]
# Display the scenario details with bold "Metric," "Panel," and "Optimization"
# Create columns for download and delete buttons
download_col, delete_col = st.columns(2)
channels_list = list(selected_scenario_data["channels"].keys())
# List to hold data for all channels
channels_data = []
# Iterate through each channel and gather required 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]
# Extract the ROI and MROI data
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"]
# Calculate spends per metric
spends_per_metrics_actual = channel_actual_spends / channel_actual_metrics
spends_per_metrics_optimized = channel_optimized_spends / channel_optimized_metrics
# Append the collected data as a dictionary to the list
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
),
}
)
# Create a DataFrame from the collected data
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)
# spends_data = pd.read_excel("Overview_data_test.xlsx")
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",
)
# Update layout and traces
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)