Simulator-UOPX / pages /2_Scenario_Planner.py
Pragya Jatav
m1
f7bb281
raw
history blame
83.5 kB
import streamlit as st
from numerize.numerize import numerize
import numpy as np
from functools import partial
from collections import OrderedDict
from plotly.subplots import make_subplots
import plotly.graph_objects as go
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import time
import Streamlit_functions as sf
from utilities import (
format_numbers,format_numbers_f,
load_local_css,
set_header,
initialize_data,
load_authenticator,
send_email,
channel_name_formating,
)
from io import BytesIO
# import xlsxwriter
import warnings
# Suppress specific warnings if necessary
warnings.filterwarnings("ignore")
warnings.filterwarnings("ignore", category=UserWarning, message="The widget with key")
# for i in :
# warnings.filterwarnings("ignore",)
from classes import class_from_dict, class_to_dict
import pickle
import streamlit_authenticator as stauth
import yaml
from yaml import SafeLoader
import re
import pandas as pd
import plotly.express as px
import response_curves_model_quality as rc
st.set_page_config(layout="wide")
load_local_css("styles.css")
set_header()
for k, v in st.session_state.items():
if k not in ["logout", "login", "config"] and not k.startswith("FormSubmitter"):
st.session_state[k] = v
# ======================================================== #
# ======================= Functions ====================== #
# ======================================================== #
def optimize(key, status_placeholder):
"""
Optimize the spends for the sales
"""
channel_list = [
key for key, value in st.session_state["optimization_channels"].items() if value
]
if len(channel_list) > 0:
scenario = st.session_state["scenario"]
if key.lower() == "media spends":
with status_placeholder:
with st.spinner("Optimizing"):
# # print(channel_list)
# # print(st.session_state["total_spends_change"])
result = st.session_state["scenario"].optimize(
st.session_state["total_spends_change"], channel_list
# result = st.session_state["scenario"].spends_optimisation(
# st.session_state["total_spends_change"], channel_list
)
# elif key.lower() == "revenue":
else:
with status_placeholder:
with st.spinner("Optimizing"):
result = st.session_state["scenario"].optimize_spends(
st.session_state["total_sales_change"], channel_list
)
for channel_name, modified_spends in result:
st.session_state[channel_name] = numerize(
modified_spends * scenario.channels[channel_name].conversion_rate,
1,
)
prev_spends = (
st.session_state["scenario"].channels[channel_name].actual_total_spends
)
st.session_state[f"{channel_name}_change"] = round(
100 * (modified_spends - prev_spends) / prev_spends, 2
)
def save_scenario(scenario_name):
"""
Save the current scenario with the mentioned name in the session state
Parameters
----------
scenario_name
Name of the scenario to be saved
"""
if "saved_scenarios" not in st.session_state:
st.session_state = OrderedDict()
# st.session_state['saved_scenarios'][scenario_name] = st.session_state['scenario'].save()
st.session_state["saved_scenarios"][scenario_name] = class_to_dict(
st.session_state["scenario"]
)
st.session_state["scenario_input"] = ""
# # print(type(st.session_state['saved_scenarios']))
with open("../saved_scenarios.pkl", "wb") as f:
pickle.dump(st.session_state["saved_scenarios"], f)
if "allow_spends_update" not in st.session_state:
st.session_state["allow_spends_update"] = True
if "allow_sales_update" not in st.session_state:
st.session_state["allow_sales_update"] = True
def update_sales_abs_slider():
actual_sales = _scenario.actual_total_sales
if validate_input(st.session_state["total_sales_change_abs_slider"]):
modified_sales = extract_number_for_string(
st.session_state["total_sales_change_abs_slider"]
)
st.session_state["total_sales_change"] = round(
((modified_sales / actual_sales) - 1) * 100
)
st.session_state["total_sales_change_abs"] = numerize(modified_sales, 1)
def update_sales_abs():
if (
st.session_state["total_sales_change_abs"]
in st.session_state["total_sales_change_abs_slider_options"]
):
st.session_state["allow_sales_update"] = True
else:
st.session_state["allow_sales_update"] = False
actual_sales = _scenario.actual_total_sales
if (
validate_input(st.session_state["total_sales_change_abs"])
and st.session_state["allow_sales_update"]
):
modified_sales = extract_number_for_string(
st.session_state["total_sales_change_abs"]
)
st.session_state["total_sales_change"] = round(
((modified_sales / actual_sales) - 1) * 100
)
st.session_state["total_sales_change_abs_slider"] = numerize(modified_sales, 1)
def update_sales():
st.session_state["total_sales_change_abs"] = numerize(
(1 + st.session_state["total_sales_change"] / 100)
* _scenario.actual_total_sales,
1,
)
st.session_state["total_sales_change_abs_slider"] = numerize(
(1 + st.session_state["total_sales_change"] / 100)
* _scenario.actual_total_sales,
1,
)
# def update_all_spends_abs_slider():
# actual_spends = _scenario.actual_total_spends
# if validate_input(st.session_state["total_spends_change_abs_slider"]):
# modified_spends = extract_number_for_string(
# st.session_state["total_spends_change_abs_slider"]
# )
# st.session_state["total_spends_change"] = round(
# ((modified_spends / actual_spends) - 1) * 100
# )
# st.session_state["total_spends_change_abs"] = numerize(modified_spends, 1)
# update_all_spends()
# def update_all_spends_abs_slider():
# actual_spends = _scenario.actual_total_spends
# if validate_input(st.session_state["total_spends_change_abs_slider"]):
# # print("#" * 100)
# # print(st.session_state["total_spends_change_abs_slider"])C:\Users\PragyaJatav\Downloads\Untitled Folder 2\simulatorAldi\pages\8_Scenario_Planner.py
# # print("#" * 100)
# modified_spends = extract_number_for_string(
# st.session_state["total_spends_change_abs_slider"]
# )
# st.session_state["total_spends_change"] = (
# (modified_spends / actual_spends) - 1
# ) * 100
# st.session_state["total_spends_change_abs"] = st.session_state[
# "total_spends_change_abs_slider"
# ]
# update_all_spends()
def update_all_spends_abs():
if (
st.session_state["total_spends_change_abs"]
in st.session_state["total_spends_change_abs_slider_options"]
):
st.session_state["allow_spends_update"] = True
else:
st.session_state["allow_spends_update"] = False
actual_spends = _scenario.actual_total_spends
if (
validate_input(st.session_state["total_spends_change_abs"])
and st.session_state["allow_spends_update"]
):
modified_spends = extract_number_for_string(
st.session_state["total_spends_change_abs"]
)
st.session_state["total_spends_change"] = (
(modified_spends / actual_spends) - 1
) * 100
st.session_state["total_spends_change_abs_slider"] = st.session_state[
"total_spends_change_abs"
]
update_all_spends()
def update_spends():
st.session_state["total_spends_change_abs"] = numerize(
(1 + st.session_state["total_spends_change"] / 100)
* _scenario.actual_total_spends,
1,
)
st.session_state["total_spends_change_abs_slider"] = numerize(
(1 + st.session_state["total_spends_change"] / 100)
* _scenario.actual_total_spends,
1,
)
update_all_spends()
def update_all_spends():
"""
Updates spends for all the channels with the given overall spends change
"""
percent_change = st.session_state["total_spends_change"]
for channel_name in st.session_state["channels_list"]:
channel = st.session_state["scenario"].channels[channel_name]
current_spends = channel.actual_total_spends
modified_spends = (1 + percent_change / 100) * current_spends
st.session_state["scenario"].update(channel_name, modified_spends)
st.session_state[channel_name] = numerize(
modified_spends * channel.conversion_rate, 1
)
st.session_state[f"{channel_name}_change"] = percent_change
def extract_number_for_string(string_input):
string_input = string_input.upper()
if string_input.endswith("K"):
return float(string_input[:-1]) * 10**3
elif string_input.endswith("M"):
return float(string_input[:-1]) * 10**6
elif string_input.endswith("B"):
return float(string_input[:-1]) * 10**9
def validate_input(string_input):
pattern = r"\d+\.?\d*[K|M|B]$"
match = re.match(pattern, string_input)
if match is None:
return False
return True
def update_data_bound_min(channel_name):
"""
Updates the bounds for the given channel
"""
modified_bounds = st.session_state[f"{channel_name}_lower_bound"]
# st.session_state['scenario']['channels'][channel_name].channel_bounds_min = st.session_state[f"{channel_name}_lower_bound"]
st.session_state['scenario'].update_bounds_min(channel_name,modified_bounds)
st.write(st.session_state["scenario"].channels[channel_name].channel_bounds_min)
# st.write(st.session_state["scenario"].channels[channel_name])
def update_data_bound_max(channel_name):
"""
Updates the bounds for the given channel
"""
modified_bounds = st.session_state[f"{channel_name}_upper_bound"]
# st.session_state['scenario']['channels'][channel_name].channel_bounds_min = st.session_state[f"{channel_name}_lower_bound"]
st.session_state['scenario'].update_bounds_max(channel_name,modified_bounds)
# st.write(st.session_state["scenario"].channels[channel_name].channel_bounds_max)
# st.write(st.session_state["scenario"].channels[channel_name])
def update_data_by_percent(channel_name):
prev_spends = (
st.session_state["scenario"].channels[channel_name].actual_total_spends
* st.session_state["scenario"].channels[channel_name].conversion_rate
)
modified_spends = prev_spends * (
1 + st.session_state[f"{channel_name}_change"] / 100
)
st.session_state[channel_name] = numerize(modified_spends, 1)
st.session_state["scenario"].update(
channel_name,
modified_spends
/ st.session_state["scenario"].channels[channel_name].conversion_rate,
)
def update_data(channel_name):
"""
Updates the spends for the given channel
"""
if validate_input(st.session_state[channel_name]):
modified_spends = extract_number_for_string(st.session_state[channel_name])
prev_spends = (
st.session_state["scenario"].channels[channel_name].actual_total_spends
* st.session_state["scenario"].channels[channel_name].conversion_rate
)
st.session_state[f"{channel_name}_change"] = round(
100 * (modified_spends - prev_spends) / prev_spends, 2
)
st.session_state["scenario"].update(
channel_name,
modified_spends
/ st.session_state["scenario"].channels[channel_name].conversion_rate,
)
# st.write(hasattr(st.session_state["scenario"], 'update_bounds_min'))
# st.session_state['scenario'].update(channel_name, modified_spends)
# else:
# try:
# modified_spends = float(st.session_state[channel_name])
# prev_spends = st.session_state['scenario'].channels[channel_name].actual_total_spends * st.session_state['scenario'].channels[channel_name].conversion_rate
# st.session_state[f'{channel_name}_change'] = round(100*(modified_spends - prev_spends) / prev_spends,2)
# st.session_state['scenario'].update(channel_name, modified_spends/st.session_state['scenario'].channels[channel_name].conversion_rate)
# st.session_state[f'{channel_name}'] = numerize(modified_spends,1)
# except ValueError:
# st.write('Invalid input')
def select_channel_for_optimization(channel_name):
"""
Marks the given channel for optimization
"""
st.session_state["optimization_channels"][channel_name] = st.session_state[
f"{channel_name}_selected"
]
# if not all(st.session_state["optimization_channels"].values()):
# st.session_state["optimize_all_channels"] = False
def select_all_channels_for_optimization():
"""
Marks all the channel for optimization
"""
for channel_name in st.session_state["optimization_channels"].keys():
st.session_state[f"{channel_name}_selected"] = st.session_state[
"optimze_all_channels"
]
st.session_state["optimization_channels"][channel_name] = st.session_state[
"optimze_all_channels"
]
def update_penalty():
"""
Updates the penalty flag for sales calculation
"""
st.session_state["scenario"].update_penalty(st.session_state["apply_penalty"])
def reset_scenario(panel_selected, file_selected, updated_rcs):
# ## print(st.session_state['default_scenario_dict'])
# st.session_state['scenario'] = class_from_dict(st.session_state['default_scenario_dict'])
# for channel in st.session_state['scenario'].channels.values():
# st.session_state[channel.name] = float(channel.actual_total_spends * channel.conversion_rate)
# initialize_data()
if panel_selected == "Total Market":
initialize_data(
target_file=file_selected,
panel=panel_selected,
updated_rcs=updated_rcs,
metrics=metrics_selected,
)
panel = None
else:
initialize_data(
target_file=file_selected,
panel=panel_selected,
updated_rcs=updated_rcs,
metrics=metrics_selected,
)
for channel_name in st.session_state["channels_list"]:
st.session_state[f"{channel_name}_selected"] = False
st.session_state[f"{channel_name}_change"] = 0
st.session_state["optimze_all_channels"] = False
st.session_state["total_sales_change"] = 0
update_spends()
update_sales()
reset_inputs()
# st.rerun()
def format_number(num):
if num >= 1_000_000:
return f"{num / 1_000_000:.2f}M"
elif num >= 1_000:
return f"{num / 1_000:.0f}K"
else:
return f"{num:.2f}"
def summary_plot(data, x, y, title, text_column):
fig = px.bar(
data,
x=x,
y=y,
orientation="h",
title=title,
text=text_column,
color="Channel_name",
)
# Convert text_column to numeric values
data[text_column] = pd.to_numeric(data[text_column], errors="coerce")
# Update the format of the displayed text based on magnitude
fig.update_traces(
texttemplate="%{text:.2s}",
textposition="outside",
hovertemplate="%{x:.2s}",
)
fig.update_layout(xaxis_title=x, yaxis_title="Channel Name", showlegend=False)
return fig
def s_curve(x, K, b, a, x0):
return K / (1 + b * np.exp(-a * (x - x0)))
def find_segment_value(x, roi, mroi):
start_value = x[0]
end_value = x[len(x) - 1]
# Condition for green region: Both MROI and ROI > 1
green_condition = (roi > 1) & (mroi > 1)
left_indices = np.where(green_condition)[0]
left_value = x[left_indices[0]] if left_indices.size > 0 else x[0]
right_indices = np.where(green_condition)[0]
right_value = x[right_indices[-1]] if right_indices.size > 0 else x[0]
return start_value, end_value, left_value, right_value
def calculate_rgba(
start_value, end_value, left_value, right_value, current_channel_spends
):
# Initialize alpha to None for clarity
alpha = None
# Determine the color and calculate relative_position and alpha based on the point's position
if start_value <= current_channel_spends <= left_value:
color = "yellow"
relative_position = (current_channel_spends - start_value) / (
left_value - start_value
)
alpha = 0.8 - (0.6 * relative_position) # Alpha decreases from start to end
elif left_value < current_channel_spends <= right_value:
color = "green"
relative_position = (current_channel_spends - left_value) / (
right_value - left_value
)
alpha = 0.8 - (0.6 * relative_position) # Alpha decreases from start to end
elif right_value < current_channel_spends <= end_value:
color = "red"
relative_position = (current_channel_spends - right_value) / (
end_value - right_value
)
alpha = 0.2 + (0.6 * relative_position) # Alpha increases from start to end
else:
# Default case, if the spends are outside the defined ranges
return "rgba(136, 136, 136, 0.5)" # Grey for values outside the range
# Ensure alpha is within the intended range in case of any calculation overshoot
alpha = max(0.2, min(alpha, 0.8))
# Define color codes for RGBA
color_codes = {
"yellow": "255, 255, 0", # RGB for yellow
"green": "0, 128, 0", # RGB for green
"red": "255, 0, 0", # RGB for red
}
rgba = f"rgba({color_codes[color]}, {alpha})"
return rgba
def debug_temp(x_test, power, K, b, a, x0):
# # print("*" * 100)
# Calculate the count of bins
count_lower_bin = sum(1 for x in x_test if x <= 2524)
count_center_bin = sum(1 for x in x_test if x > 2524 and x <= 3377)
count_ = sum(1 for x in x_test if x > 3377)
# # print(
# f"""
# lower : {count_lower_bin}
# center : {count_center_bin}
# upper : {count_}
# """
# )
# @st.cache
def plot_response_curves(summary_df_sorted):
# rows = (
# len(channels_list) // cols
# if len(channels_list) % cols == 0
# else len(channels_list) // cols + 1
# )
# rcs = st.session_state["rcs"]
# shapes = []
# fig = make_subplots(rows=rows, cols=cols, subplot_titles=channels_list)
channel_cols = [
'BroadcastTV',
'CableTV',
'Connected&OTTTV',
'DisplayProspecting',
'DisplayRetargeting',
'Video',
'SocialProspecting',
'SocialRetargeting',
'SearchBrand',
'SearchNon-brand',
'DigitalPartners',
'Audio',
'Email']
summary_df_sorted.index = summary_df_sorted["Channel_name"]
figures = [rc.response_curves(channels_list[i], summary_df_sorted["Optimized_spend"][channels_list[i]]/104, summary_df_sorted["New_sales"][channels_list[i]]/104) for i in range(13)]
# for i in range()
# Display figures in a grid layout
cols = st.columns(3) # 4 columns for the grid
for idx, fig in enumerate(figures):
col = cols[idx % 3]
with col:
st.plotly_chart(fig, use_container_width=True)
# ======================================================== #
# ==================== HTML Components =================== #
# ======================================================== #
def generate_spending_header(heading):
return st.markdown(
f"""<h4 class="spends-header"><bold>{heading}<bold></h4>""", unsafe_allow_html=True
)
# ======================================================== #
# =================== Session variables ================== #
# ======================================================== #
with open("config.yaml") as file:
config = yaml.load(file, Loader=SafeLoader)
st.session_state["config"] = config
authenticator = stauth.Authenticate(
config["credentials"],
config["cookie"]["name"],
config["cookie"]["key"],
config["cookie"]["expiry_days"],
config["preauthorized"],
)
st.session_state["authenticator"] = authenticator
name, authentication_status, username = authenticator.login("Login", "main")
auth_status = st.session_state.get("authentication_status")
import os
import glob
def upload_file_prospects_calc(df):
df["Prospects"] = 0
params = pd.read_excel(r"response_curves_parameters.xlsx",index_col = "channel")
param_dicts = {col: params[col].to_dict() for col in params.columns}
df.index = df.channel
# # print(param_dicts)
for col in df.channel:
x = (df["Spends"][col]/104 - param_dicts["x_min"][col])/(param_dicts["x_max"][col]-param_dicts["x_min"][col])
x_out = x**param_dicts["n"][col]/(param_dicts["Kd"][col]**param_dicts["n"][col]+ x**param_dicts["n"][col])
x_out_inv = (x_out*(param_dicts["y_max"][col]-param_dicts["y_min"][col])+param_dicts["y_min"][col])*104
df["Prospects"][col] = x_out_inv
# # print(df)
return df
def upload_file_format(df):
df1 = df.transpose()
df1.reset_index(inplace = True)
df1.columns = df1.iloc[0]
df1 = df1[1:]
df1["channel"] = pd.to_datetime('1999-08-06').date()
df1.rename(columns = {"channel":"Date"},inplace = True)
df2 = df1.rename(columns = {"Date":"Week"})
df3 =upload_file_prospects_calc(df)
df3 = df3[["Prospects"]].transpose().reset_index()
df3["index"] = pd.to_datetime('1999-08-06').date()
df3.rename(columns = {"index":"Date"},inplace = True)
df3.insert(1, 'const', [0])
# Create a buffer to hold the Excel file
import io
output = io.BytesIO()
# Write the dataframes to an Excel file
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df1.to_excel(writer, index=False, sheet_name='RAW DATA MMM')
df2.to_excel(writer, index=False, sheet_name='SPEND INPUT')
df3.to_excel(writer, index=False, sheet_name='CONTRIBUTION MMM')
# Seek to the beginning of the stream
output.seek(0)
with open('Overview_data_uploaded.xlsx', 'wb') as f:
f.write(output.getvalue())
return
def get_excel_names(directory):
# Create a list to hold the final parts of the filenames
last_portions = []
# Patterns to match Excel files (.xlsx and .xls) that contain @#
patterns = [
os.path.join(directory, "*@#*.xlsx"),
os.path.join(directory, "*@#*.xls"),
]
# Process each pattern
for pattern in patterns:
files = glob.glob(pattern)
# Extracting the last portion after @# for each file
for file in files:
base_name = os.path.basename(file)
last_portion = base_name.split("@#")[-1]
last_portion = last_portion.replace(".xlsx", "").replace(
".xls", ""
) # Removing extensions
last_portions.append(last_portion)
return last_portions
def name_formating(channel_name):
# Replace underscores with spaces
name_mod = channel_name.replace("_", " ")
# Capitalize the first letter of each word
name_mod = name_mod.title()
return name_mod
@st.cache_data(show_spinner=False)
def panel_fetch(file_selected):
raw_data_mmm_df = pd.read_excel(file_selected, sheet_name="RAW DATA MMM")
# if "Panel" in raw_data_mmm_df.columns:
# panel = list(set(raw_data_mmm_df["Panel"]))
# else:
# raw_data_mmm_df = None
# panel = None
# raw_data_mmm_df = None
panel = None
return panel
def reset_inputs():
if "total_spends_change_abs" in st.session_state:
del st.session_state.total_spends_change_abs
if "total_spends_change" in st.session_state:
del st.session_state.total_spends_change
if "total_spends_change_abs_slider" in st.session_state:
del st.session_state.total_spends_change_abs_slider
if "total_sales_change_abs" in st.session_state:
del st.session_state.total_sales_change_abs
if "total_sales_change" in st.session_state:
del st.session_state.total_sales_change
if "total_sales_change_abs_slider" in st.session_state:
del st.session_state.total_sales_change_abs_slider
st.session_state["initialized"] = False
def scenario_planner_plots():
with st.expander('Optimized Spends Overview'):
# if st.button('Refresh'):
# st.experimental_rerun()
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Define light colors for bars
import plotly.graph_objects as go
from plotly.subplots import make_subplots
st.empty()
#st.header('Model Result Analysis')
spends_data=pd.read_excel('Overview_data_test.xlsx')
with open('summary_df.pkl', 'rb') as file:
summary_df_sorted = pickle.load(file)
#st.write(summary_df_sorted)
# selected_scenario= st.selectbox('Select Saved Scenarios',['S1','S2'])
summary_df_sorted=summary_df_sorted.sort_values(by=['Optimized_spend'],ascending=False)
summary_df_sorted['old_efficiency']=(summary_df_sorted['Old_sales']/summary_df_sorted['Old_sales'].sum())/(summary_df_sorted['Actual_spend']/summary_df_sorted['Actual_spend'].sum())
summary_df_sorted['new_efficiency']=(summary_df_sorted['New_sales']/summary_df_sorted['New_sales'].sum())/(summary_df_sorted['Optimized_spend']/summary_df_sorted['Optimized_spend'].sum())
summary_df_sorted['old_roi']=summary_df_sorted['Old_sales']/summary_df_sorted['Actual_spend']
summary_df_sorted['new_roi']=summary_df_sorted['New_sales']/summary_df_sorted['Optimized_spend']
total_actual_spend = summary_df_sorted['Actual_spend'].sum()
total_optimized_spend = summary_df_sorted['Optimized_spend'].sum()
actual_spend_percentage = (summary_df_sorted['Actual_spend'] / total_actual_spend) * 100
optimized_spend_percentage = (summary_df_sorted['Optimized_spend'] / total_optimized_spend) * 100
light_blue = 'rgba(0, 31, 120, 0.7)'
light_orange = 'rgba(0, 181, 219, 0.7)'
light_green = 'rgba(240, 61, 20, 0.7)'
light_red = 'rgba(250, 110, 10, 0.7)'
light_purple = 'rgba(255, 191, 69, 0.7)'
fig = go.Figure()
# Add actual vs optimized spend bars
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['Actual_spend'], name='Actual',
text=summary_df_sorted['Actual_spend'].apply(format_number) + ' '
# +
# ' '+
# '</br> (' + actual_spend_percentage.astype(int).astype(str) + '%)'
,textposition='outside',#textfont=dict(size=30),
marker_color=light_blue))
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['Optimized_spend'], name='Optimized',
text=summary_df_sorted['Optimized_spend'].apply(format_number) + ' '
# +
# '</br> (' + optimized_spend_percentage.astype(int).astype(str) + '%)'
,textposition='outside',#textfont=dict(size=30),
marker_color=light_orange))
fig.update_xaxes(title_text="Channels")
fig.update_yaxes(title_text="Spends ($)")
fig.update_layout(
title = "Actual vs. Optimized Spends",
margin=dict(t=40, b=40, l=40, r=40)
)
st.plotly_chart(fig,use_container_width=True)
# Add actual vs optimized Contribution
fig = go.Figure()
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['Old_sales'],
name='Actual Contribution',text=summary_df_sorted['Old_sales'].apply(format_number),textposition='outside',
marker_color=light_blue,showlegend=True))
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['New_sales'],
name='Optimized Contribution',text=summary_df_sorted['New_sales'].apply(format_number),textposition='outside',
marker_color=light_orange, showlegend=True))
fig.update_yaxes(title_text="Contribution")
fig.update_xaxes(title_text="Channels")
fig.update_layout(
title = "Actual vs. Optimized Contributions",
margin=dict(t=40, b=40, l=40, r=40)
# yaxis=dict(range=[0, 0.002]),
)
st.plotly_chart(fig,use_container_width=True)
# Add actual vs optimized Efficiency bars
fig = go.Figure()
summary_df_sorted_p = summary_df_sorted[summary_df_sorted['Channel_name']!="Panel"]
fig.add_trace(go.Bar(x=summary_df_sorted_p['Channel_name'].apply(channel_name_formating), y=summary_df_sorted_p['old_efficiency'],
name='Actual Efficiency', text=summary_df_sorted_p['old_efficiency'].apply(format_number) ,textposition='outside',
marker_color=light_blue,showlegend=True))
fig.add_trace(go.Bar(x=summary_df_sorted_p['Channel_name'].apply(channel_name_formating), y=summary_df_sorted_p['new_efficiency'],
name='Optimized Efficiency',text=summary_df_sorted_p['new_efficiency'].apply(format_number),textposition='outside' ,
marker_color=light_orange,showlegend=True))
fig.update_xaxes(title_text="Channels")
fig.update_yaxes(title_text="Efficiency")
fig.update_layout(
title = "Actual vs. Optimized Efficiency",
margin=dict(t=40, b=40, l=40, r=40),
# yaxis=dict(range=[0, 0.002]),
)
st.plotly_chart(fig,use_container_width=True)
if auth_status == True:
authenticator.logout("Logout", "main")
st.header("Scenario Planner")
data_selected = st.selectbox(
"Select base data for optimisation", options=["Optimise Actual Spends", "Optimise Uploaded Spends"],
key = "data_upload_key",index = 1)
# st.text_input("")
# Response Metrics
directory = "metrics_level_data"
metrics_list = get_excel_names(directory)
# metrics_selected = col1.selectbox(
# "Response Metrics",
# metrics_list,
# format_func=name_formating,
# index=0,
# on_change=reset_inputs,
# )
metrics_selected='prospects'
# Target
target = name_formating(metrics_selected)
# file_selected = (
# f"Overview_data_test_panel@#{metrics_selected}.xlsx"
# )
file_selected = None
if data_selected == "Optimise Uploaded Spends":
st.write("Select a file to upload")
uploaded_file = st.file_uploader("Choose an Excel file", type=["xlsx", "xls"])
if uploaded_file:
try:
# Read the Excel file using pandas
df = pd.read_excel(uploaded_file, engine='openpyxl')
upload_file_format(df)
file_selected = "Overview_data_uploaded.xlsx"
# initialize_data(
# target_file=file_selected,
# panel="Total Market",
# updated_rcs=None,
# metrics=metrics_selected,
# )
except Exception as e:
st.error(f"Error reading the file: {e}")
elif data_selected == "Optimise Actual Spends":
file_selected = (
f"Overview_data_test_panel@#{metrics_selected}.xlsx"
)
# initialize_data(
# target_file=file_selected,
# panel="Total Market",
# updated_rcs=None,
# metrics=metrics_selected,
# )
else :
st.write("")
if file_selected:
st.session_state['file_selected']=file_selected
# Panel List
panel_list = panel_fetch(file_selected)
# # Panel Selected
# panel_selected = st.selectbox(
# "Markets",
# ["Total Market"] + panel_list,
# index=0,
# on_change=reset_inputs,
# )
# st.write(panel_selected)
panel_selected = "Total Market"
st.session_state['selected_markets']=panel_selected
if "update_rcs" in st.session_state:
updated_rcs = st.session_state["update_rcs"]
else:
updated_rcs = None
if "first_time" not in st.session_state:
st.session_state["first_time"] = True
# Check if state is initiaized
is_state_initiaized = st.session_state.get("initialized", False)
if not is_state_initiaized or st.session_state["first_time"]:
initialize_data(
target_file=file_selected,
panel=panel_selected,
updated_rcs=updated_rcs,
metrics=metrics_selected,
)
st.session_state["initialized"] = True
st.session_state["first_time"] = False
# initialize_data(
# panel=panel_selected,
# target_file=file_selected,
# updated_rcs=updated_rcs,
# metrics=metrics_selected,
# )
# st.session_state["initialized"] = True
# st.session_state["first_time"] = False
# Channels List
channels_list = st.session_state["channels_list"]
# ======================================================== #
# ========================== UI ========================== #
# ======================================================== #
# # print(list(st.session_state.keys()))
main_header = st.columns((2, 2))
sub_header = st.columns((1, 1, 1, 1))
_scenario = st.session_state["scenario"]
if "total_spends_change" not in st.session_state:
st.session_state.total_spends_change = 0
if "total_sales_change" not in st.session_state:
st.session_state.total_sales_change = 0
if "total_spends_change_abs" not in st.session_state:
st.session_state["total_spends_change_abs"] = numerize(
_scenario.actual_total_spends, 1
)
# st.write(_scenario.actual_total_sales)
if "total_sales_change_abs" not in st.session_state:
st.session_state["total_sales_change_abs"] = numerize(
_scenario.actual_total_sales, 1
)
if "total_spends_change_abs_slider" not in st.session_state:
st.session_state.total_spends_change_abs_slider = numerize(
_scenario.actual_total_spends, 1
)
if "total_sales_change_abs_slider" not in st.session_state:
st.session_state.total_sales_change_abs_slider = numerize(
_scenario.actual_total_sales, 1
)
if "lower_bound_key" not in st.session_state:
st.session_state["lower_bound_key"] = 10
if "upper_bound_key" not in st.session_state:
st.session_state["upper_bound_key"] = 10
# st.write(_scenario.modified_total_sales)
header_df = pd.DataFrame(index=["Actual","Simulated","Change","Percent Change"],columns=["Spends","Prospects"])
header_df["Spends"]["Actual"] = format_numbers(_scenario.actual_total_spends)
header_df["Spends"]["Simulated"] = format_numbers(_scenario.modified_total_spends)
header_df["Spends"]["Change"] = format_numbers(_scenario.delta_spends) #_scenario.modified_total_spends -_scenario.actual_total_spends
header_df["Spends"]["Percent Change"] = numerize(100*(_scenario.delta_spends/_scenario.actual_total_spends))+'%'
header_df["Prospects"]["Actual"] = format_numbers_f(float(_scenario.actual_total_sales))
header_df["Prospects"]["Simulated"] = format_numbers_f(float(_scenario.modified_total_sales))
header_df["Prospects"]["Change"] = format_numbers_f(_scenario.delta_sales)
header_df["Prospects"]["Percent Change"] = numerize(100*(_scenario.delta_sales/_scenario.actual_total_sales),1)+'%'
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
_columns = st.columns((1, 1, 1, 1, 1))
st.markdown(
"""
<style>
.custom-text_head {
font-size: 24px; /* Adjust font size */
color: 'blue' ; /* Adjust text color */
font-weight: bold;
}
</style>
""",
unsafe_allow_html=True
)
with _columns[0]:
st.markdown(f'<p class="custom-text_head">{"Metrics"}</p>', unsafe_allow_html=True)
# generate_spending_header("Metric")
with _columns[1]:
st.markdown(f'<p class="custom-text_head">{"Actual"}</p>', unsafe_allow_html=True)
# generate_spending_header("Actual")
with _columns[2]:
st.markdown(f'<p class="custom-text_head">{"Simulated"}</p>', unsafe_allow_html=True)
# generate_spending_header("Optimised")
with _columns[3]:
st.markdown(f'<p class="custom-text_head">{"Change"}</p>', unsafe_allow_html=True)
# generate_spending_header("Change")
with _columns[4]:
st.markdown(f'<p class="custom-text_head">{"Change Percent"}</p>', unsafe_allow_html=True)
# generate_spending_header("Change Percent")
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
_columns = st.columns((1, 1, 1, 1, 1))
with _columns[0]:
st.markdown("""<h4> Spends</h4>""",unsafe_allow_html=True)
# st.write("Spends")
with _columns[1]:
st.markdown(f"""<h4>{header_df["Spends"]["Actual"]}</h4>""",unsafe_allow_html=True)
# st.metric(label="", value=header_df["Spends"]["Actual"])
with _columns[2]:
st.markdown(f"""<h4>{header_df["Spends"]["Simulated"]}</h4>""",unsafe_allow_html=True)
if _scenario.delta_spends < 0:
st.markdown(
"""
<style>
.custom-text {
font-size: 24px; /* Adjust font size */
color: #6bbf6b ; /* Adjust text color */
}
</style>
""",
unsafe_allow_html=True
)
else:
st.markdown(
"""
<style>
.custom-text {
font-size: 24px; /* Adjust font size */
color: #ff6868; /* Adjust text color */
}
</style>
""",
unsafe_allow_html=True
)
# st.metric(label="", value=header_df["Spends"]["Simulated"])
with _columns[3]:
# Apply custom styles to text
st.markdown(f'<h4 class="custom-text">{header_df["Spends"]["Change"]}</h4>', unsafe_allow_html=True)
with _columns[4]:
# Apply custom styles to text
# st.markdown(f'<p></hr></p>', unsafe_allow_html=True)
st.markdown(f'<h4 class="custom-text">{header_df["Spends"]["Percent Change"]}</h4>', unsafe_allow_html=True)
st.markdown(
"""<hr class="spends-child-seperator">""",
unsafe_allow_html=True,
)
_columns = st.columns((1, 1, 1, 1, 1))
with _columns[0]:
# st.header("Prospects")
st.markdown("""<h4> Prospects</h4>""",unsafe_allow_html=True)
with _columns[1]:
st.markdown(f"""<h4>{header_df["Prospects"]["Actual"]}</h4>""",unsafe_allow_html=True)
# st.metric(label="", value=header_df["Prospects"]["Actual"])
with _columns[2]:
st.markdown(f"""<h4>{header_df["Prospects"]["Simulated"]}</h4>""",unsafe_allow_html=True)
# st.metric(label="", value=header_df["Prospects"]["Simulated"])
if _scenario.delta_sales >= 0:
st.markdown(
"""
<style>
.custom-text {
font-size: 24px; /* Adjust font size */
color:#6bbf6b ; /* Adjust text color */
}
</style>
""",
unsafe_allow_html=True
)
else:
st.markdown("""<style>.custom-text {font-size: 24px; /* Adjust font size */color: #ff6868; /* Adjust text color */}</style>""",unsafe_allow_html=True)
with _columns[3]:
# Apply custom styles to text
st.markdown(f'<h4 class="custom-text">{header_df["Prospects"]["Change"]}</h4>', unsafe_allow_html=True)
# st.markdown(f'<p style="color: red;">{st.metric(label="", value=header_df["Prospects"]["Change"])}</p>', unsafe_allow_html=True)
# st.markdown(f'<p style="color: red;">{header_df["Prospects"]["Change"]}</p>', unsafe_allow_html=True)
with _columns[4]:
# st.markdown(f'<p></hr></p>', unsafe_allow_html=True)
# Apply custom styles to text
st.markdown(f'<h4 class="custom-text">{header_df["Prospects"]["Percent Change"]}</h4>', unsafe_allow_html=True)
st.markdown(
"""<hr class="spends-child-seperator">""",
unsafe_allow_html=True,
)
_columns = st.columns((1, 1, 1, 1, 1))
ef1 = (_scenario.actual_total_spends/_scenario.actual_total_sales)
ef2 = (_scenario.modified_total_spends/_scenario.modified_total_sales)
with _columns[0]:
st.markdown("""<h4>Cost Per Prospect</h4>""",unsafe_allow_html=True)
# st.header("Cost Per Prospect")
with _columns[1]:
st.markdown(f"""<h4>{'$ '+numerize(ef1,0)}</h4>""",unsafe_allow_html=True)
# st.metric(label="", value='$ '+numerize(ef1,0))
with _columns[2]:
st.markdown(f"""<h4>{'$ '+numerize(ef2,0)}</h4>""",unsafe_allow_html=True)
# st.metric(label="", value='$ '+numerize(ef2,0))
if ef2 <= ef1:
st.markdown(
"""
<style>
.custom-text1 {
font-size: 24px; /* Adjust font size */
color:#6bbf6b ; /* Adjust text color */
}
</style>
""",
unsafe_allow_html=True
)
else:
st.markdown(
"""
<style>
.custom-text1 {
font-size: 24px; /* Adjust font size */
color: #ff6868; /* Adjust text color */
}
</style>
""",
unsafe_allow_html=True
)
with _columns[3]:
# Apply custom styles to text
st.markdown(f'<h4 class="custom-text1">{"$ "+numerize(ef2-ef1,0)}</h4>', unsafe_allow_html=True)
# st.markdown(f'<p style="color: red;">{st.metric(label="", value=header_df["Prospects"]["Change"])}</p>', unsafe_allow_html=True)
# st.markdown(f'<p style="color: red;">{header_df["Prospects"]["Change"]}</p>', unsafe_allow_html=True)
with _columns[4]:
# st.markdown(f'<p></hr></p>', unsafe_allow_html=True)
# Apply custom styles to text
st.markdown(f'<h4 class="custom-text1">{round((ef2-ef1)/ef1*100,2)}%</h4>', unsafe_allow_html=True)
st.markdown("""<hr class="spends-child-seperator">""",unsafe_allow_html=True)
# st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
# header_df.reset_index(inplace=True)
# # Function to color the index
# def highlight_index(s):
# return ['background-color: lightblue' for _ in s]
# # Function to color the header
# def highlight_header(s):
# return ['background-color: lightgreen' for _ in s]
# # Applying the styles
# styled_df = header_df.style \
# .apply(highlight_index, axis=0, subset=pd.IndexSlice[:, :]) \
# .set_table_styles({
# 'A': [{'selector': 'th', 'props': [('background-color', 'lightgreen')]}],
# 'B': [{'selector': 'th', 'props': [('background-color', 'lightgreen')]}],
# 'C': [{'selector': 'th', 'props': [('background-color', 'lightgreen')]}]
# })
# # Function to apply arrows based on value
# def format_arrows(val):
# if val > 0:
# return '<span style="color: green;">&#9650;</span>' # Green up arrow
# elif val < 0:
# return '<span style="color: red;">&#9660;</span>' # Red down arrow
# return '' # No arrow for zero
# # Function to format specific rows and exclude the first column
# def apply_row_formatting(df, rows):
# def format_cell(val, row_idx, col_idx):
# if row_idx in rows and col_idx > 0: # Exclude the first column (col_idx > 0)
# return format_arrows(val)
# return '' # No formatting for other cells
# return df.style.apply(lambda x: [format_cell(val, i, col) for i, (val, col) in enumerate(zip(x, range(len(x))))], axis=1)
# # Apply formatting to 3rd and 4th rows (index 2 and 3)
# styled_df = apply_row_formatting(header_df, [2, 3])
# st.markdown(styled_df.to_html(escape=False), unsafe_allow_html=True)
# st.markdown(header_df.style.set_table_styles
# ([{'selector': 'th',
# 'props': [('background-color', '#D3D3D3'),
# ('font-size', '25px')]},
# {
# 'selector' : 'td:first-child',
# 'props' : [('background-color', '#D3D3D3'),
# ('font-size', '25px')]
# }
# ,
# {'selector': 'tbody td',
# 'props': [('font-size', '20px')]}
# ]).to_html(),unsafe_allow_html=True)
# styled_df = header_df.style.apply(highlight_first_col, axis=1)
# st.table(styled_df)
# with main_header[0]:
# st.subheader("Actual")
# with main_header[-1]:
# st.subheader("Simulated")
# with sub_header[0]:
# st.metric(label="Spends", value=format_numbers(_scenario.actual_total_spends))
# with sub_header[1]:
# st.metric(
# label=target,
# value=format_numbers_f(
# float(_scenario.actual_total_sales)
# ),
# )
# with sub_header[2]:
# st.metric(
# label="Spends",
# value=format_numbers(_scenario.modified_total_spends),
# delta=numerize(_scenario.delta_spends, 1),
# )
# with sub_header[3]:
# st.metric(
# label=target,
# value=format_numbers_f(
# float(_scenario.modified_total_sales)
# ),
# delta=numerize(_scenario.delta_sales, 1),
# )
with st.expander("Channel Spends Simulator", expanded=True):
_columns1 = st.columns((1.5, 1.5, 1,1))
with _columns1[0]:
optimization_selection = st.selectbox(
"Optimize", options=["Media Spends", target], key="optimization_key"
)
with _columns1[1]:
# st.markdown("#")
# if st.checkbox(
# label="Optimize all Channels",
# key="optimze_all_channels",
# value=False,
# # on_change=select_all_channels_for_optimization,
# ):
# select_all_channels_for_optimization()
st.checkbox(
label="Optimize all Channels",
key="optimze_all_channels",
value=False,
on_change=select_all_channels_for_optimization,
)
with _columns1[2]:
# st.button(
# "Optimize",
# on_click=optimize,
# args=(st.session_state["optimization_key"]),
# use_container_width=True,
# )
optimize_placeholder = st.empty()
with _columns1[3]:
# st.markdown("#")
st.button(
"Reset",
on_click=reset_scenario,
args=(panel_selected, file_selected, updated_rcs),
# use_container_width=True,
)
# st.write(target)
_columns2 = st.columns((2, 2, 2,2))
if st.session_state["optimization_key"] == "Media Spends":
with _columns2[0]:
spend_input = st.text_input(
"Absolute",
key="total_spends_change_abs",
# label_visibility="collapsed",
on_change=update_all_spends_abs,
)
with _columns2[1]:
st.number_input(
"Percent Change",
key="total_spends_change",
min_value=-50.00,
max_value=50.00,
step=1.00,
value=0.00,
on_change=update_spends,
)
with _columns2[2]:
overall_lower_bound = st.number_input(
"Overall Lower Bound for Spends",
value = 50
# key = overall_lower_bound,
# on_change=partial(update_data_bound_min_overall)
)
with _columns2[3]:
overall_upper_bound = st.number_input(
"Overall Upper Bound for Spends",
value = 50
# key = overall_upper_bound,
# on_change=partial(update_data_bound_max_overall)
)
min_value = round(_scenario.actual_total_spends * (1-overall_lower_bound/100))
max_value = round(_scenario.actual_total_spends * (1-overall_upper_bound/100))
st.session_state["total_spends_change_abs_slider_options"] = [
numerize(value, 1)
for value in range(min_value, max_value + 1, int(1e4))
]
# st.select_slider(
# "Absolute Slider",
# options=st.session_state["total_spends_change_abs_slider_options"],
# key="total_spends_change_abs_slider",
# on_change=update_all_spends_abs_slider,
# )
elif st.session_state["optimization_key"] == target:
# st.write(target)
with _columns2[0]:
sales_input = st.text_input(
"Absolute",
key="total_sales_change_abs",
on_change=update_sales_abs,
)
with _columns2[1]:
st.number_input(
"Percent Change",
key="total_sales_change",
min_value=-50.00,
max_value=50.00,
step=1.00,
value=0.00,
on_change=update_sales,
)
with _columns2[2]:
overall_lower_bound = st.number_input(
"Overall Lower Bound for Spends",
value = 50
)
with _columns2[3]:
overall_upper_bound = st.number_input(
"Overall Upper Bound for Spends",
value = 50
)
min_value = round(_scenario.actual_total_sales * (1-overall_lower_bound/100))
max_value = round(_scenario.actual_total_sales * (1+overall_upper_bound/100))
# st.write(min_value)
# st.write(max_value)
# for value in range(min_value, max_value + 1, int(100)):
# st.write(numerize(value, 1))
# st.session_state["total_sales_change_abs_slider_options"] = [
# numerize(value, 1)
# for value in range(min_value, max_value + 1, int(100))
# ]
# st.select_slider(
# "Absolute Slider",
# options=st.session_state["total_sales_change_abs_slider_options"],
# key="total_sales_change_abs_slider",
# on_change=update_sales_abs_slider,
# # value=numerize(min_value, 1)
# )
if (
not st.session_state["allow_sales_update"]
and optimization_selection == target
):
st.warning("Invalid Input")
if (
not st.session_state["allow_spends_update"]
and optimization_selection == "Media Spends"
):
st.warning("Invalid Input")
status_placeholder = st.empty()
# if optimize_placeholder.button("Optimize", use_container_width=True):
# optimize(st.session_state["optimization_key"], status_placeholder)
# st.rerun()
optimize_placeholder.button(
"Optimize",
on_click=optimize,
args=(st.session_state["optimization_key"], status_placeholder),
# use_container_width=True,
)
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
_columns = st.columns((1.5,2.5,2,2, 1))
with _columns[0]:
generate_spending_header("Channel")
with _columns[1]:
generate_spending_header("Spends Input")
with _columns[2]:
generate_spending_header("Spends")
with _columns[3]:
generate_spending_header(target)
with _columns[4]:
generate_spending_header("Optimize")
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
if "acutual_predicted" not in st.session_state:
st.session_state["acutual_predicted"] = {
"Channel_name": [],
"Actual_spend": [],
"Optimized_spend": [],
"Delta": [],
"New_sales":[],
"Old_sales":[]
}
for i, channel_name in enumerate(channels_list):
# st.write(channel_name)
_channel_class = st.session_state["scenario"].channels[channel_name]
# st.write(st.session_state["scenario"].channels[channel_name])
# st.write(st.session_state["scenario"].channels[channel_name].actual_total_sales)
# st.write(st.session_state["scenario"].channels[channel_name].actual_total_spends)
# st.write(st.session_state["scenario"].channels[channel_name].modified_total_sales)
# st.write(st.session_state["scenario"].channels[channel_name].modified_total_spends)
# st.write(st.session_state["scenario"].channels[channel_name].bounds)
# st.write(st.session_state["scenario"].channels[channel_name].channel_bounds_min)
_columns = st.columns((1.5,2.5,2,2, 1))
response_curve_params = pd.read_excel("response_curves_parameters.xlsx",index_col = "channel")
param_dicts = {col: response_curve_params[col].to_dict() for col in response_curve_params.columns}
with _columns[0]:
st.write(channel_name_formating(channel_name))
bin_placeholder = st.container()
with _columns[1]:
channel_bounds = _channel_class.bounds
# st.write(channel_bounds)
channel_spends = float(_channel_class.actual_total_spends)
channel_bounds_min = float(_channel_class.channel_bounds_min)
channel_bounds_max = float(_channel_class.channel_bounds_max)
min_value = float((1 - channel_bounds_min / 100) * channel_spends)
max_value = float((1 + channel_bounds_max / 100) * channel_spends)
# st.write(channel_spends)
# st.write(min_value)
# st.write(max_value)
### print(st.session_state[channel_name])
_columns_min = st.columns(2)
with _columns_min[0]:
spend_input = st.text_input(
"Absolute",
key=channel_name,
# label_visibility="collapsed",
on_change=partial(update_data, channel_name),
)
channel_name_lower_bound = f"{channel_name}_lower_bound"
if channel_name_lower_bound not in st.session_state:
st.session_state[channel_name_lower_bound] = str(round(param_dicts["x_min"][channel_name]*10400/param_dicts["current_spends"][channel_name]))
# st.write(st.session_state[channel_name_lower_bound])
channel_bounds_min = st.text_input(
"Lower Bound Percentage",
key = channel_name_lower_bound,
on_change=partial(update_data_bound_min,channel_name)
)
# st.write(st.session_state[channel_name_lower_bound])
if not validate_input(spend_input):
st.error("Invalid input")
channel_name_current = f"{channel_name}_change"
with _columns_min[1]:
st.number_input(
"Percent Change",
key=channel_name_current,
step=1.00,value=0.00,
on_change=partial(update_data_by_percent, channel_name),
)
channel_name_upper_bound = f"{channel_name}_upper_bound"
if channel_name_upper_bound not in st.session_state:
st.session_state[channel_name_upper_bound] = str(10)
channel_bounds_max = st.text_input(
"Upper Bound Percentage",
key = channel_name_upper_bound,
on_change=partial(update_data_bound_max,channel_name)
)
with _columns[2]:
# spends
current_channel_spends = float(
_channel_class.modified_total_spends
* _channel_class.conversion_rate
)
actual_channel_spends = float(
_channel_class.actual_total_spends * _channel_class.conversion_rate
)
spends_delta = float(
_channel_class.delta_spends * _channel_class.conversion_rate
)
st.session_state["acutual_predicted"]["Channel_name"].append(
channel_name
)
st.session_state["acutual_predicted"]["Actual_spend"].append(
actual_channel_spends
)
st.session_state["acutual_predicted"]["Optimized_spend"].append(
current_channel_spends
)
st.session_state["acutual_predicted"]["Delta"].append(spends_delta)
_spend_cols = st.columns(2)
with _spend_cols[0]:
# st.write("Actual")
st.markdown(f'<p> Actual <h5>{format_numbers(actual_channel_spends)}</h5> </p>', unsafe_allow_html=True)
# st.metric(
# label="Actual Spends",
# value=format_numbers(actual_channel_spends),
# # delta=numerize(spends_delta, 1),
# # label_visibility="collapsed",
# )
# st.write("Actual")
st.markdown(f'<p> Change <h5>{format_numbers(spends_delta)}</h5> </p>', unsafe_allow_html=True)
# st.markdown(f'<h4 class="custom-text1">{format_numbers(spends_delta)}%</h4>', unsafe_allow_html=True)
# st.metric(
# label="Change",
# value= format_numbers_f(spends_delta),
# delta=numerize(spends_delta, 1),
# # label_visibility="collapsed",
# )
with _spend_cols[1]:
st.markdown(f'<p> Simulated <h5>{format_numbers(current_channel_spends)}</h5> </p>', unsafe_allow_html=True)
st.markdown(f'<p>Percent<h5>{numerize(( spends_delta/actual_channel_spends)*100,0) +"%"}</h5> </p>', unsafe_allow_html=True)
# st.metric(
# label="Simulated Spends",
# value=format_numbers(current_channel_spends),
# # delta=numerize(spends_delta, 1),
# # label_visibility="collapsed",
# )
# st.metric(
# label="Percent Change",
# value= numerize(( spends_delta/actual_channel_spends)*100,0) +"%",
# delta=numerize(spends_delta, 1),
# # label_visibility="collapsed",
# )
with _columns[3]:
# sales
current_channel_sales = float(_channel_class.modified_total_sales)
actual_channel_sales = float(_channel_class.actual_total_sales)
sales_delta = float(_channel_class.delta_sales)
st.session_state["acutual_predicted"]["Old_sales"].append(actual_channel_sales)
st.session_state["acutual_predicted"]["New_sales"].append(current_channel_sales)
#st.write(actual_channel_sales)
_prospect_cols = st.columns(2)
with _prospect_cols[0]:
# st.write("Actual")
st.markdown(f'<p> Actual <h5>{format_numbers(actual_channel_sales)}</h5> </p>', unsafe_allow_html=True)
st.markdown(f'<p> Change <h5>{format_numbers(sales_delta)}</h5> </p>', unsafe_allow_html=True)
# st.metric(
# # target,
# label="Actual Prospects",
# value= format_numbers_f(actual_channel_sales),
# # delta=numerize(sales_delta, 1),
# # label_visibility="collapsed",
# )
# st.metric(
# label="Change",
# value= format_numbers_f(_channel_class.delta_sales),
# delta=numerize(sales_delta, 1),
# # label_visibility="collapsed",
# )
with _prospect_cols[1]:
st.markdown(f'<p> Simulated <h5>{format_numbers(current_channel_sales)}</h5> </p>', unsafe_allow_html=True)
st.markdown(f'<p>Percent<h5>{numerize(( _channel_class.delta_sales/actual_channel_sales)*100,0) +"%"}</h5> </p>', unsafe_allow_html=True)
# st.metric(
# label="Simulated Prospects",
# value= format_numbers_f(current_channel_sales),
# # delta=numerize(sales_delta, 1),
# # label_visibility="collapsed",
# )
# st.metric(
# label="Percent Change",
# value= numerize((_channel_class.delta_sales/actual_channel_sales)*100,0) +"%",
# delta=numerize(sales_delta, 1),
# # label_visibility="collapsed",
# )
with _columns[4]:
# if st.checkbox(
# label="select for optimization",
# key=f"{channel_name}_selected",
# value=False,
# # on_change=partial(select_channel_for_optimization, channel_name),
# label_visibility="collapsed",
# ):
# select_channel_for_optimization(channel_name)
st.checkbox(
label="select for optimization",
key=f"{channel_name}_selected",
value=False,
on_change=partial(select_channel_for_optimization, channel_name),
label_visibility="collapsed",
)
st.markdown(
"""<hr class="spends-child-seperator">""",
unsafe_allow_html=True,
)
# Bins
col = channels_list[i]
x_actual = st.session_state["scenario"].channels[col].actual_spends
x_modified = st.session_state["scenario"].channels[col].modified_spends
# x_modified_total = 0
# for c in channels_list:
# # st.write(c)
# # st.write(st.session_state["scenario"].channels[c].modified_spends)
# x_modified_total = x_modified_total + st.session_state["scenario"].channels[c].modified_spends.sum()
# st.write(x_modified_total)
x_total = x_modified.sum()
power = np.ceil(np.log(x_actual.max()) / np.log(10)) - 3
updated_rcs_key = f"{metrics_selected}#@{panel_selected}#@{channel_name}"
# if updated_rcs and updated_rcs_key in list(updated_rcs.keys()):
# K = updated_rcs[updated_rcs_key]["K"]
# b = updated_rcs[updated_rcs_key]["b"]
# a = updated_rcs[updated_rcs_key]["a"]
# x0 = updated_rcs[updated_rcs_key]["x0"]
# else:
# K = st.session_state["rcs"][col]["K"]
# b = st.session_state["rcs"][col]["b"]
# a = st.session_state["rcs"][col]["a"]
# x0 = st.session_state["rcs"][col]["x0"]
# x_plot = np.linspace(0, 5 * x_actual.sum(), 200)
# # Append current_channel_spends to the end of x_plot
# x_plot = np.append(x_plot, current_channel_spends)
# x, y, marginal_roi = [], [], []
# for x_p in x_plot:
# x.append(x_p * x_actual / x_actual.sum())
# for index in range(len(x_plot)):
# y.append(s_curve(x[index] / 10**power, K, b, a, x0))
# for index in range(len(x_plot)):
# marginal_roi.append(
# a * y[index] * (1 - y[index] / np.maximum(K, np.finfo(float).eps))
# )
# x = (
# np.sum(x, axis=1)
# * st.session_state["scenario"].channels[col].conversion_rate
# )
# y = np.sum(y, axis=1)
# marginal_roi = (
# np.average(marginal_roi, axis=1)
# / st.session_state["scenario"].channels[col].conversion_rate
# )
# roi = y / np.maximum(x, np.finfo(float).eps)
# # roi = (y/np.sum(y))/(x/np.sum(x))
# # st.write(x)
# # st.write(y)
# # st.write(roi)
# # st.write(roi[-1])
# roi_current, marginal_roi_current = roi[-1], marginal_roi[-1]
# x, y, roi, marginal_roi = (
# x[:-1],
# y[:-1],
# roi[:-1],
# marginal_roi[:-1],
# ) # Drop data for current spends
# # roi_current =
# start_value, end_value, left_value, right_value = find_segment_value(
# x,
# roi,
# marginal_roi,
# )
#st.write(roi_current)
# rgba = calculate_rgba(
# start_value,
# end_value,
# left_value,
# right_value,
# current_channel_spends,
# )
# # print(st.session_state["acutual_predicted"])
summary_df = pd.DataFrame(st.session_state["acutual_predicted"])
# st.dataframe(summary_df)
summary_df.drop_duplicates(subset="Channel_name", keep="last", inplace=True)
# st.dataframe(summary_df)
summary_df_sorted = summary_df.sort_values(by="Delta", ascending=False)
summary_df_sorted["Delta_percent"] = np.round(
((summary_df_sorted["Optimized_spend"] / summary_df_sorted["Actual_spend"]) - 1)
* 100,
2,
)
summary_df_sorted=summary_df_sorted.sort_values(by=['Optimized_spend'],ascending=False)
summary_df_sorted['old_efficiency']=(summary_df_sorted['Old_sales']/summary_df_sorted['Old_sales'].sum())/(summary_df_sorted['Actual_spend']/summary_df_sorted['Actual_spend'].sum())
summary_df_sorted['new_efficiency']=(summary_df_sorted['New_sales']/summary_df_sorted['New_sales'].sum())/(summary_df_sorted['Optimized_spend']/summary_df_sorted['Optimized_spend'].sum())
a = (summary_df_sorted[summary_df_sorted['Channel_name']== col]).reset_index()['new_efficiency'][0]
b = (summary_df_sorted[summary_df_sorted['Channel_name']== col]).reset_index()['old_efficiency'][0]
# st.write(a)
with bin_placeholder:
if a> 1:
fill_color_box = "#6bbf6b"
elif a <1:
fill_color_box = "#ff6868"
else:
fill_color_box = "#ff6868"
st.markdown(
f"""
<div style="
border-radius: 12px;
background-color: {fill_color_box};
padding: 10px;
text-align: center;
color: {'black'};
">
<p style="margin: 0; font-size: 16px;">Simulated Efficiency: {round(a,2)} </br> Actual Efficiency: {round(b,2)} </p>
<!--<p style="margin: 0; font-size: 16px;">Marginal ROI: {round(b,1)}</p>-->
</div>
""",
unsafe_allow_html=True,
)
# <p style="margin: 0; font-size: 16px;">Simulated Efficiency: {round(a,2)} </br> Actual Efficiency: {round(b,2)} </p>
# <!--<p style="margin: 0; font-size: 16px;">Marginal ROI: {round(b,1)}</p>-->
with st.expander("See Response Curves", expanded=True):
fig = plot_response_curves(summary_df_sorted)
# st.plotly_chart(rc.response_curves(col))
# st.plotly_chart(fig, use_container_width=True)
summary_df = pd.DataFrame(st.session_state["acutual_predicted"])
# st.dataframe(summary_df)
summary_df.drop_duplicates(subset="Channel_name", keep="last", inplace=True)
# st.dataframe(summary_df)
summary_df_sorted = summary_df.sort_values(by="Delta", ascending=False)
summary_df_sorted["Delta_percent"] = np.round(
((summary_df_sorted["Optimized_spend"] / summary_df_sorted["Actual_spend"]) - 1)
* 100,
2,
)
with open("summary_df.pkl", "wb") as f:
pickle.dump(summary_df_sorted, f)
# st.dataframe(summary_df_sorted)
# ___columns=st.columns(3)
# with ___columns[2]:
# fig=summary_plot(summary_df_sorted, x='Delta_percent', y='Channel_name', title='Delta', text_column='Delta_percent')
# st.plotly_chart(fig,use_container_width=True)
# with ___columns[0]:
# fig=summary_plot(summary_df_sorted, x='Actual_spend', y='Channel_name', title='Actual Spend', text_column='Actual_spend')
# st.plotly_chart(fig,use_container_width=True)
# with ___columns[1]:
# fig=summary_plot(summary_df_sorted, x='Optimized_spend', y='Channel_name', title='Planned Spend', text_column='Optimized_spend')
# st.plotly_chart(fig,use_container_width=True)
scenario_planner_plots()
with st.expander ("View Forecasted spends"):
col1, col2, col3 = st.columns([1,1,0.75])
# List of 12 months
months_start = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]
years_start = range(2022,2100)
months_end = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]
years_end = range(2022,2100)
with col1:
from datetime import date
st.write ("Select Start Time Period")
sc1,sc2 = st.columns([1,1])
with sc1:
# Create a dropdown (selectbox) for months
start_date_mon = st.selectbox("Select Start Month:", months_start)
with sc2:
start_date_year = st.selectbox("Select Start Year:", years_start,index=2)
start_date1 = date(start_date_year, months_start.index(start_date_mon)+1, 1)- relativedelta(years=1)
# st.write(start_date1)
# default_Month = "January"
# start_date_mon = st.text_input("Select Start Month: ",value=default_Month)
# default_Year = 2024
# start_date_year = st.number_input("Select Start Year: ",value=default_Year)
with col2:
st.write ("Select End Time Period")
ec1,ec2 = st.columns([1,1])
with ec1:
end_date_mon = st.selectbox("Select End Month:", months_end,index=1)
with ec2:
end_date_year = st.selectbox("Select End Year:", years_end,index=2)
end_date1 = date(end_date_year, months_end.index(end_date_mon)+2, 1)- relativedelta(years=1)
# st.write(end_date1)
# default_Month = "February"
# end_date_mon = st.text_input("Select End Month: ",value=default_Month)
# default_Year = 2024
# end_date_year = st.number_input("Select End Year: ",value=default_Year)
# end_date1 = st.date_input("Select End Date: ",value=default_date) - relativedelta(years=1)
with col3:
current_date = datetime.now()
c1, c2, c3 = st.columns(3)
with c1:
if st.button('Next Month'):
start_date1 = current_date- relativedelta(years=1)
end_date1 = current_date + relativedelta(months=1)- relativedelta(years=1)
with c2:
if st.button('Next Quarter'):
start_date1 = current_date- relativedelta(years=1)
end_date1 = current_date + relativedelta(months = 3)- relativedelta(years=1)
with c3:
if st.button('Next Year'):
start_date1 = current_date- relativedelta(years=1)
end_date1 = current_date + relativedelta(months = 12)- relativedelta(years=1)
forecasted_table_df = sf.scenario_spend_forecasting(summary_df_sorted,start_date1,end_date1)
st.dataframe(forecasted_table_df)
forecasted_table_df2 = sf.scenario_spend_forecasting2(summary_df_sorted,start_date1,end_date1)
st.dataframe(forecasted_table_df2)
st.markdown("""
<style>
.yellow-container {
background-color: #FFFF99;
border: 1px solid #FFD700;
padding: 10px;
border-radius: 5px;
margin-bottom: 10px;
}
</style>
""", unsafe_allow_html=True)
def save_report_forecast(forecasted_table_df,forecasted_table_df2):
# Convert the DataFrame to an Excel file in memory
excel_file = BytesIO()
with pd.ExcelWriter(excel_file, engine='openpyxl') as writer:
forecasted_table_df.to_excel(writer, index=True, sheet_name='Forecasted Spends')
forecasted_table_df2.to_excel(writer, sheet_name='Monthly Breakdown',index = True)
# Seek to the beginning of the BytesIO buffer
excel_file.seek(0)
return excel_file
st.subheader("Download Report")
report_name = st.text_input(
"Report name",
key="report_input",
placeholder="Report name",
label_visibility="collapsed",
)
st.download_button(
"Download Report",
data = save_report_forecast(forecasted_table_df,forecasted_table_df2),
file_name = report_name+".xlsx",
mime="application/vnd.ms-excel",
# on_click=lambda: save_report_forecast(forecasted_table_df,report_name),
disabled=len(st.session_state["report_input"]) == 0,#use_container_width=True
)
# filename = st.text_input("Save Report: ",placeholder="Report name")
# if st.button("Download Report",disabled= (filename != "Report name")):
# excel_file_path = filename+ '.xlsx'
# forecasted_table_df.to_excel(excel_file_path, index=False)
# message_container = st.empty()
# with message_container:
# st.write(f'<div class="yellow-container">{"Report Saved!"}</div>', unsafe_allow_html=True)
# time.sleep(0.5)
# st.empty()
# on_click=lambda: save_scenario(scenario_name),
# disabled=len(st.session_state["scenario_input"]) == 0,#use_container_width=True
_columns = st.columns(2)
# with _columns[0]:
st.subheader("Save Scenario")
scenario_name = st.text_input(
"Scenario name",
key="scenario_input",
placeholder="Scenario name",
label_visibility="collapsed",
)
st.button(
"Save",
on_click=lambda: save_scenario(scenario_name),
disabled=len(st.session_state["scenario_input"]) == 0,#use_container_width=True
)
elif auth_status == False:
st.error("Username/Password is incorrect")
if auth_status != True:
try:
username_forgot_pw, email_forgot_password, random_password = (
authenticator.forgot_password("Forgot password")
)
if username_forgot_pw:
st.session_state["config"]["credentials"]["usernames"][username_forgot_pw][
"password"
] = stauth.Hasher([random_password]).generate()[0]
send_email(email_forgot_password, random_password)
st.success("New password sent securely")
# Random password to be transferred to user securely
elif username_forgot_pw == False:
st.error("Username not found")
except Exception as e:
st.error(e)