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)