Spaces:
Sleeping
Sleeping
| from scenario import numerize | |
| import streamlit as st | |
| import pandas as pd | |
| import json | |
| from scenario import Channel, Scenario | |
| import numpy as np | |
| from plotly.subplots import make_subplots | |
| import plotly.graph_objects as go | |
| from scenario import class_to_dict | |
| from collections import OrderedDict | |
| import io | |
| import plotly | |
| from pathlib import Path | |
| import pickle | |
| import yaml | |
| from yaml import SafeLoader | |
| from streamlit.components.v1 import html | |
| import smtplib | |
| from scipy.optimize import curve_fit | |
| from sklearn.metrics import r2_score | |
| from scenario import class_from_dict | |
| from utilities import retrieve_pkl_object | |
| import os | |
| import base64 | |
| # # schema = db_cred["schema"] | |
| color_palette = [ | |
| "#F3F3F0", | |
| "#5E7D7E", | |
| "#2FA1FF", | |
| "#00EDED", | |
| "#00EAE4", | |
| "#304550", | |
| "#EDEBEB", | |
| "#7FBEFD", | |
| "#003059", | |
| "#A2F3F3", | |
| "#E1D6E2", | |
| "#B6B6B6", | |
| ] | |
| CURRENCY_INDICATOR = "$" | |
| if "project_dct" not in st.session_state or "project_number" not in st.session_state: | |
| st.error( | |
| "No tuned model available. Please build and tune a model to generate response curves." | |
| ) | |
| st.stop() | |
| tuned_model = retrieve_pkl_object( | |
| st.session_state["project_number"], "Model_Tuning", "tuned_model" | |
| ) | |
| if tuned_model is None: | |
| st.error( | |
| "No tuned model available. Please build and tune a model to generate response curves." | |
| ) | |
| st.stop() | |
| def load_authenticator(): | |
| 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 | |
| return authenticator | |
| def nav_page(page_name, timeout_secs=3): | |
| nav_script = """ | |
| <script type="text/javascript"> | |
| function attempt_nav_page(page_name, start_time, timeout_secs) { | |
| var links = window.parent.document.getElementsByTagName("a"); | |
| for (var i = 0; i < links.length; i++) { | |
| if (links[i].href.toLowerCase().endsWith("/" + page_name.toLowerCase())) { | |
| links[i].click(); | |
| return; | |
| } | |
| } | |
| var elasped = new Date() - start_time; | |
| if (elasped < timeout_secs * 1000) { | |
| setTimeout(attempt_nav_page, 100, page_name, start_time, timeout_secs); | |
| } else { | |
| alert("Unable to navigate to page '" + page_name + "' after " + timeout_secs + " second(s)."); | |
| } | |
| } | |
| window.addEventListener("load", function() { | |
| attempt_nav_page("?", new Date(), %d); | |
| }); | |
| </script> | |
| """ % ( | |
| page_name, | |
| timeout_secs, | |
| ) | |
| html(nav_script) | |
| # def load_local_css(file_name): | |
| # with open(file_name) as f: | |
| # st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True) | |
| # def set_header(): | |
| # return st.markdown(f"""<div class='main-header'> | |
| # <h1>MMM LiME</h1> | |
| # <img src="https://assets-global.website-files.com/64c8fffb0e95cbc525815b79/64df84637f83a891c1473c51_Vector%20(Stroke).svg "> | |
| # </div>""", unsafe_allow_html=True) | |
| path = os.path.dirname(__file__) | |
| file_ = open(f"{path}/logo.png", "rb") | |
| contents = file_.read() | |
| data_url = base64.b64encode(contents).decode("utf-8") | |
| file_.close() | |
| DATA_PATH = "./data" | |
| IMAGES_PATH = "./data/images_224_224" | |
| # is_panel = True if len(panel_col) > 0 else False | |
| # manoj | |
| is_panel = False | |
| date_col = "Date" | |
| # is_panel = False # flag if set to true - do panel level response curves | |
| def load_local_css(file_name): | |
| with open(file_name) as f: | |
| st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True) | |
| # def set_header(): | |
| # return st.markdown(f"""<div class='main-header'> | |
| # <h1>H & M Recommendations</h1> | |
| # <img src="data:image;base64,{data_url}", alt="Logo"> | |
| # </div>""", unsafe_allow_html=True) | |
| path1 = os.path.dirname(__file__) | |
| # file_1 = open(f"{path}/willbank.png", "rb") | |
| # contents1 = file_1.read() | |
| # data_url1 = base64.b64encode(contents1).decode("utf-8") | |
| # file_1.close() | |
| DATA_PATH1 = "./data" | |
| IMAGES_PATH1 = "./data/images_224_224" | |
| def set_header(): | |
| return st.markdown( | |
| f"""<div class='main-header'> | |
| <!-- <h1></h1> --> | |
| <div > | |
| <img class='blend-logo' src="data:image;base64,{data_url}", alt="Logo"> | |
| </div> | |
| <img class='blend-logo' src="data:image;base64,{data_url}", alt="Logo"> | |
| </div>""", | |
| unsafe_allow_html=True, | |
| ) | |
| # def set_header(): | |
| # logo_path = "./path/to/your/local/LIME_logo.png" # Replace with the actual file path | |
| # text = "LiME" | |
| # return st.markdown(f"""<div class='main-header'> | |
| # <img src="data:image/png;base64,{data_url}" alt="Logo" style="float: left; margin-right: 10px; width: 100px; height: auto;"> | |
| # <h1>{text}</h1> | |
| # </div>""", unsafe_allow_html=True) | |
| def s_curve(x, K, b, a, x0): | |
| return K / (1 + b * np.exp(-a * (x - x0))) | |
| def overview_test_data_prep_panel(X, df, spends_X, date_col, panel_col, target_col): | |
| """ | |
| function to create the data which is used in initialize data fn | |
| X : X test with contributions | |
| df : originally uploaded data (media data) which has raw vars | |
| spends_X : spends of dates in X test | |
| """ | |
| channels = st.session_state["channels"] | |
| channel_list = channels.keys() | |
| # map transformed variable to raw variable name & channel name | |
| # mapping eg : paid_search_clicks_lag_2 (transformed var) --> paid_search_clicks (raw var) --> paid_search (channel) | |
| variables = {} | |
| channel_and_variables = {} | |
| new_variables = {} | |
| new_channels_and_variables = {} | |
| for transformed_var in [ | |
| col | |
| for col in X.drop(columns=[date_col, panel_col, target_col, "pred"]).columns | |
| if "_contr" not in col | |
| ]: | |
| if len([col for col in df.columns if col in transformed_var]) == 1: | |
| raw_var = [col for col in df.columns if col in transformed_var][0] | |
| variables[transformed_var] = raw_var | |
| # Check if the list comprehension result is not empty before accessing the first element | |
| channels_list = [ | |
| channel for channel, raw_vars in channels.items() if raw_var in raw_vars | |
| ] | |
| if channels_list: | |
| channel_and_variables[raw_var] = channels_list[0] | |
| else: | |
| # Handle the case where channels_list is empty | |
| # You might want to set a default value or handle it according to your use case | |
| channel_and_variables[raw_var] = None | |
| else: | |
| new_variables[transformed_var] = transformed_var | |
| new_channels_and_variables[transformed_var] = "base" | |
| # Raw DF | |
| raw_X = pd.merge( | |
| X[[date_col, panel_col]], | |
| df[[date_col, panel_col] + list(variables.values())], | |
| how="left", | |
| on=[date_col, panel_col], | |
| ) | |
| assert len(raw_X) == len(X) | |
| raw_X_cols = [] | |
| for i in raw_X.columns: | |
| if i in channel_and_variables.keys(): | |
| raw_X_cols.append(channel_and_variables[i]) | |
| else: | |
| raw_X_cols.append(i) | |
| raw_X.columns = raw_X_cols | |
| # Contribution DF | |
| contr_X = X[ | |
| [date_col, panel_col] | |
| + [col for col in X.columns if "_contr" in col and "sum_" not in col] | |
| ].copy() | |
| # if "base_contr" in contr_X.columns: | |
| # contr_X.rename(columns={'base_contr':'const_contr'},inplace=True) | |
| # # new_variables = [ | |
| # col | |
| # for col in contr_X.columns | |
| # if "_flag" in col.lower() or "trend" in col.lower() or "sine" in col.lower() | |
| # ] | |
| # if len(new_variables) > 0: | |
| # contr_X["const"] = contr_X[["panel_effect"] + new_variables].sum(axis=1) | |
| # contr_X.drop(columns=["panel_effect"], inplace=True) | |
| # contr_X.drop(columns=new_variables, inplace=True) | |
| # else: | |
| # contr_X.rename(columns={"panel_effect": "const"}, inplace=True) | |
| new_contr_X_cols = [] | |
| for col in contr_X.columns: | |
| col_clean = col.replace("_contr", "") | |
| new_contr_X_cols.append(col_clean) | |
| contr_X.columns = new_contr_X_cols | |
| contr_X_cols = [] | |
| for i in contr_X.columns: | |
| if i in variables.keys(): | |
| contr_X_cols.append(channel_and_variables[variables[i]]) | |
| else: | |
| contr_X_cols.append(i) | |
| contr_X.columns = contr_X_cols | |
| # Spends DF | |
| spends_X.columns = [col.replace("_cost", "") for col in spends_X.columns] | |
| raw_X.rename(columns={"date": "Date"}, inplace=True) | |
| contr_X.rename(columns={"date": "Date"}, inplace=True) | |
| spends_X.rename(columns={"date": "Week"}, inplace=True) | |
| spends_X.columns = [ | |
| col.replace("spends_", "") if col.startswith("spends_") else col | |
| for col in spends_X.columns | |
| ] | |
| # Rename column to 'Date' | |
| spends_X.rename(columns={"Week": "Date"}, inplace=True) | |
| # Remove "response_metric_" from the start and "_total" from the end | |
| if str(target_col).startswith("response_metric_"): | |
| target_col = target_col.replace("response_metric_", "", 1) | |
| # Remove the last 6 characters (length of "_total") | |
| if str(target_col).endswith("_total"): | |
| target_col = target_col[:-6] | |
| # Rename column to 'Date' | |
| spends_X.rename(columns={"Week": "Date"}, inplace=True) | |
| # Save raw, spends and contribution data | |
| st.session_state["project_dct"]["current_media_performance"]["model_outputs"][ | |
| target_col | |
| ] = { | |
| "raw_data": raw_X, | |
| "contribution_data": contr_X, | |
| "spends_data": spends_X, | |
| } | |
| # Clear page metadata | |
| st.session_state["project_dct"]["scenario_planner"]["original_metadata_file"] = None | |
| st.session_state["project_dct"]["response_curves"]["original_metadata_file"] = None | |
| # st.session_state["project_dct"]["scenario_planner"]["modified_metadata_file"] = None | |
| # st.session_state["project_dct"]["response_curves"]["modified_metadata_file"] = None | |
| def overview_test_data_prep_nonpanel(X, df, spends_X, date_col, target_col): | |
| """ | |
| function to create the data which is used in initialize data fn | |
| """ | |
| # with open( | |
| # os.path.join(st.session_state["project_path"], "channel_groups.pkl"), "rb" | |
| # ) as f: | |
| # channels = pickle.load(f) | |
| # channel_list = list(channels.keys()) | |
| channels = st.session_state["channels"] | |
| channel_list = channels.keys() | |
| # map transformed variable to raw variable name & channel name | |
| # mapping eg : paid_search_clicks_lag_2 (transformed var) --> paid_search_clicks (raw var) --> paid_search (channel) | |
| variables = {} | |
| channel_and_variables = {} | |
| new_variables = {} | |
| new_channels_and_variables = {} | |
| cols_to_del = list( | |
| set([date_col, target_col, "pred"]).intersection((set(X.columns))) | |
| ) | |
| # remove exog cols from RAW data (exog cols are part of base, raw data needs media vars only) | |
| all_exog_vars = st.session_state["bin_dict"]["Exogenous"] | |
| all_exog_vars = [ | |
| var.lower() | |
| .replace(".", "_") | |
| .replace("@", "_") | |
| .replace(" ", "_") | |
| .replace("-", "") | |
| .replace(":", "") | |
| .replace("__", "_") | |
| for var in all_exog_vars | |
| ] | |
| exog_cols = [] | |
| if len(all_exog_vars) > 0: | |
| for col in X.columns: | |
| if len([exog_var for exog_var in all_exog_vars if exog_var in col]) > 0: | |
| exog_cols.append(col) | |
| cols_to_del = cols_to_del + exog_cols | |
| for transformed_var in [ | |
| col for col in X.drop(columns=cols_to_del).columns if "_contr" not in col | |
| ]: # also has 'const' | |
| if ( | |
| len([col for col in df.columns if col in transformed_var]) == 1 | |
| ): # col is raw var | |
| raw_var = [col for col in df.columns if col in transformed_var][0] | |
| variables[transformed_var] = raw_var | |
| channel_and_variables[raw_var] = [ | |
| channel for channel, raw_vars in channels.items() if raw_var in raw_vars | |
| ][0] | |
| else: # when no corresponding raw var then base | |
| new_variables[transformed_var] = transformed_var | |
| new_channels_and_variables[transformed_var] = "base" | |
| # Raw DF | |
| raw_X = pd.merge( | |
| X[[date_col]], | |
| df[[date_col] + list(variables.values())], | |
| how="left", | |
| on=[date_col], | |
| ) | |
| assert len(raw_X) == len(X) | |
| raw_X_cols = [] | |
| for i in raw_X.columns: | |
| if i in channel_and_variables.keys(): | |
| raw_X_cols.append(channel_and_variables[i]) | |
| else: | |
| raw_X_cols.append(i) | |
| raw_X.columns = raw_X_cols | |
| # Contribution DF | |
| contr_X = X[ | |
| [date_col] + [col for col in X.columns if "_contr" in col and "sum_" not in col] | |
| ].copy() | |
| new_variables = [ | |
| col | |
| for col in contr_X.columns | |
| if "_flag" in col.lower() or "trend" in col.lower() or "sine" in col.lower() | |
| ] | |
| if ( | |
| len(new_variables) > 0 | |
| ): # if new vars are available, their contributions should be added to base (called const) | |
| contr_X["const_contr"] = contr_X[["const_contr"] + new_variables].sum(axis=1) | |
| contr_X.drop(columns=new_variables, inplace=True) | |
| new_contr_X_cols = [] | |
| for col in contr_X.columns: | |
| col_clean = col.replace("_contr", "") | |
| new_contr_X_cols.append(col_clean) | |
| contr_X.columns = new_contr_X_cols | |
| contr_X_cols = [] | |
| for i in contr_X.columns: | |
| if i in variables.keys(): | |
| contr_X_cols.append(channel_and_variables[variables[i]]) | |
| else: | |
| contr_X_cols.append(i) | |
| contr_X.columns = contr_X_cols | |
| # Spends DF | |
| # spends_X.columns = [ | |
| # col.replace("_cost", "").replace("_spends", "").replace("_spend", "") | |
| # for col in spends_X.columns | |
| # ] | |
| spends_X_col_map = { | |
| col: bucket | |
| for col in spends_X.columns | |
| for bucket in channels.keys() | |
| if col in channels[bucket] | |
| } | |
| spends_X.rename(columns=spends_X_col_map, inplace=True) | |
| raw_X.rename(columns={"date": "Date"}, inplace=True) | |
| contr_X.rename(columns={"date": "Date"}, inplace=True) | |
| spends_X.rename(columns={"date": "Week"}, inplace=True) | |
| spends_X.columns = [ | |
| col.replace("spends_", "") if col.startswith("spends_") else col | |
| for col in spends_X.columns | |
| ] | |
| # Rename column to 'Date' | |
| spends_X.rename(columns={"Week": "Date"}, inplace=True) | |
| # Remove "response_metric_" from the start and "_total" from the end | |
| if str(target_col).startswith("response_metric_"): | |
| target_col = target_col.replace("response_metric_", "", 1) | |
| # Remove the last 6 characters (length of "_total") | |
| if str(target_col).endswith("_total"): | |
| target_col = target_col[:-6] | |
| # Rename column to 'Date' | |
| spends_X.rename(columns={"Week": "Date"}, inplace=True) | |
| # Save raw, spends and contribution data | |
| st.session_state["project_dct"]["current_media_performance"]["model_outputs"][ | |
| target_col | |
| ] = { | |
| "raw_data": raw_X, | |
| "contribution_data": contr_X, | |
| "spends_data": spends_X, | |
| } | |
| # Clear page metadata | |
| st.session_state["project_dct"]["scenario_planner"]["original_metadata_file"] = None | |
| st.session_state["project_dct"]["response_curves"]["original_metadata_file"] = None | |
| # st.session_state["project_dct"]["scenario_planner"]["modified_metadata_file"] = None | |
| # st.session_state["project_dct"]["response_curves"]["modified_metadata_file"] = None | |
| def initialize_data_cmp(target_col, is_panel, panel_col, start_date, end_date): | |
| start_date = pd.to_datetime(start_date) | |
| end_date = pd.to_datetime(end_date) | |
| # Remove "response_metric_" from the start and "_total" from the end | |
| if str(target_col).startswith("response_metric_"): | |
| target_col = target_col.replace("response_metric_", "", 1) | |
| # Remove the last 6 characters (length of "_total") | |
| if str(target_col).endswith("_total"): | |
| target_col = target_col[:-6] | |
| # Extract dataframes for raw data, spend input, and contribution data | |
| raw_df = st.session_state["project_dct"]["current_media_performance"][ | |
| "model_outputs" | |
| ][target_col]["raw_data"] | |
| spend_df = st.session_state["project_dct"]["current_media_performance"][ | |
| "model_outputs" | |
| ][target_col]["spends_data"] | |
| contri_df = st.session_state["project_dct"]["current_media_performance"][ | |
| "model_outputs" | |
| ][target_col]["contribution_data"] | |
| # Remove unnecessary columns | |
| unnamed_cols = [col for col in raw_df.columns if col.lower().startswith("unnamed")] | |
| exclude_columns = ["Date"] + unnamed_cols | |
| if is_panel: | |
| exclude_columns = exclude_columns + [panel_col] | |
| # Aggregate all 3 dfs to date level (from date-panel level) | |
| raw_df[date_col] = pd.to_datetime(raw_df[date_col]) | |
| raw_df = raw_df[raw_df[date_col] >= start_date] | |
| raw_df = raw_df[raw_df[date_col] <= end_date].reset_index(drop=True) | |
| raw_df_aggregations = {c: "sum" for c in raw_df.columns if c not in exclude_columns} | |
| raw_df = raw_df.groupby(date_col).agg(raw_df_aggregations).reset_index() | |
| contri_df[date_col] = pd.to_datetime(contri_df[date_col]) | |
| contri_df = contri_df[contri_df[date_col] >= start_date] | |
| contri_df = contri_df[contri_df[date_col] <= end_date].reset_index(drop=True) | |
| contri_df_aggregations = { | |
| c: "sum" for c in contri_df.columns if c not in exclude_columns | |
| } | |
| contri_df = contri_df.groupby(date_col).agg(contri_df_aggregations).reset_index() | |
| input_df = raw_df.sort_values(by=[date_col]) | |
| output_df = contri_df.sort_values(by=[date_col]) | |
| spend_df["Date"] = pd.to_datetime( | |
| spend_df["Date"], format="%Y-%m-%d", errors="coerce" | |
| ) | |
| spend_df = spend_df[spend_df["Date"] >= start_date] | |
| spend_df = spend_df[spend_df["Date"] <= end_date].reset_index(drop=True) | |
| spend_df_aggregations = { | |
| c: "sum" for c in spend_df.columns if c not in exclude_columns | |
| } | |
| spend_df = spend_df.groupby("Date").agg(spend_df_aggregations).reset_index() | |
| # spend_df['Week'] = pd.to_datetime(spend_df['Week'], errors='coerce') | |
| # spend_df = spend_df.sort_values(by='Week') | |
| channel_list = [col for col in input_df.columns if col not in exclude_columns] | |
| response_curves = {} | |
| mapes = {} | |
| rmses = {} | |
| upper_limits = {} | |
| powers = {} | |
| r2 = {} | |
| conv_rates = {} | |
| output_cols = [] | |
| channels = {} | |
| sales = None | |
| dates = input_df.Date.values | |
| actual_output_dic = {} | |
| actual_input_dic = {} | |
| # channel_list=['programmatic'] | |
| infeasible_channels = [ | |
| c | |
| for c in contri_df.select_dtypes(include=["float", "int"]).columns | |
| if contri_df[c].sum() <= 0 | |
| ] | |
| channel_list = list(set(channel_list) - set(infeasible_channels)) | |
| for inp_col in channel_list: | |
| spends = input_df[inp_col].values | |
| x = spends.copy() | |
| # upper limit for penalty | |
| upper_limits[inp_col] = 2 * x.max() | |
| out_col = inp_col | |
| y = output_df[out_col].values.copy() | |
| actual_output_dic[inp_col] = y.copy() | |
| actual_input_dic[inp_col] = x.copy() | |
| ##output cols aggregation | |
| output_cols.append(out_col) | |
| ## scale the input | |
| power = np.ceil(np.log(x.max()) / np.log(10)) - 3 | |
| if power >= 0: | |
| x = x / 10**power | |
| x = x.astype("float64") | |
| y = y.astype("float64") | |
| if y.max() <= 0.01: | |
| bounds = ( | |
| (0, 0, 0, 0), | |
| (3 * 0.01, 1000, 1, x.max() if x.max() > 0 else 0.01), | |
| ) | |
| else: | |
| bounds = ((0, 0, 0, 0), (3 * y.max(), 1000, 1, x.max())) | |
| params, _ = curve_fit( | |
| s_curve, | |
| x, | |
| y, | |
| p0=(2 * y.max(), 0.01, 1e-5, x.max()), | |
| bounds=bounds, | |
| maxfev=int(1e5), | |
| ) | |
| mape = (100 * abs(1 - s_curve(x, *params) / y.clip(min=1))).mean() | |
| rmse = np.sqrt(((y - s_curve(x, *params)) ** 2).mean()) | |
| r2_ = r2_score(y, s_curve(x, *params)) | |
| response_curves[inp_col] = { | |
| "K": params[0], | |
| "b": params[1], | |
| "a": params[2], | |
| "x0": params[3], | |
| } | |
| mapes[inp_col] = mape | |
| rmses[inp_col] = rmse | |
| r2[inp_col] = r2_ | |
| powers[inp_col] = power | |
| conv = spend_df[inp_col].sum() / max(input_df[inp_col].sum(), 1e-9) | |
| conv_rates[inp_col] = conv | |
| channel = Channel( | |
| name=inp_col, | |
| dates=dates, | |
| spends=spends, | |
| # conversion_rate = np.mean(list(conv_rates[inp_col].values())), | |
| conversion_rate=conv_rates[inp_col], | |
| response_curve_type="s-curve", | |
| response_curve_params={ | |
| "K": params[0], | |
| "b": params[1], | |
| "a": params[2], | |
| "x0": params[3], | |
| }, | |
| bounds=np.array([-10, 10]), | |
| correction=y - s_curve(x, *params), | |
| ) | |
| channels[inp_col] = channel | |
| if sales is None: | |
| sales = channel.actual_sales | |
| else: | |
| sales += channel.actual_sales | |
| other_contributions = ( | |
| output_df.drop([*output_cols], axis=1).sum(axis=1, numeric_only=True).values | |
| ) | |
| correction = output_df.drop(["Date"], axis=1).sum(axis=1).values - ( | |
| sales + other_contributions | |
| ) | |
| # Testing | |
| # scenario_test_df = pd.DataFrame( | |
| # columns=["other_contributions", "correction", "sales"] | |
| # ) | |
| # scenario_test_df["other_contributions"] = other_contributions | |
| # scenario_test_df["correction"] = correction | |
| # scenario_test_df["sales"] = sales | |
| # scenario_test_df.to_csv("test\scenario_test_df.csv", index=False) | |
| # output_df.to_csv("test\output_df.csv", index=False) | |
| scenario = Scenario( | |
| name="default", | |
| channels=channels, | |
| constant=other_contributions, | |
| correction=correction, | |
| ) | |
| ## setting session variables | |
| st.session_state["initialized"] = True | |
| st.session_state["actual_df"] = input_df | |
| st.session_state["raw_df"] = raw_df | |
| st.session_state["contri_df"] = output_df | |
| default_scenario_dict = class_to_dict(scenario) | |
| st.session_state["default_scenario_dict"] = default_scenario_dict | |
| st.session_state["scenario"] = scenario | |
| st.session_state["channels_list"] = channel_list | |
| st.session_state["optimization_channels"] = { | |
| channel_name: False for channel_name in channel_list | |
| } | |
| st.session_state["rcs"] = response_curves | |
| # orig_rcs_path = os.path.join( | |
| # st.session_state["project_path"], f"orig_rcs_{target_col}_{panel_col}.json" | |
| # ) | |
| # if Path(orig_rcs_path).exists(): | |
| # with open(orig_rcs_path, "r") as f: | |
| # st.session_state["orig_rcs"] = json.load(f) | |
| # else: | |
| # st.session_state["orig_rcs"] = response_curves.copy() | |
| # with open(orig_rcs_path, "w") as f: | |
| # json.dump(st.session_state["orig_rcs"], f) | |
| st.session_state["powers"] = powers | |
| st.session_state["actual_contribution_df"] = pd.DataFrame(actual_output_dic) | |
| st.session_state["actual_input_df"] = pd.DataFrame(actual_input_dic) | |
| for channel in channels.values(): | |
| st.session_state[channel.name] = numerize( | |
| channel.actual_total_spends * channel.conversion_rate, 1 | |
| ) | |
| # st.session_state["xlsx_buffer"] = io.BytesIO() | |
| # | |
| # if Path("../saved_scenarios.pkl").exists(): | |
| # with open("../saved_scenarios.pkl", "rb") as f: | |
| # st.session_state["saved_scenarios"] = pickle.load(f) | |
| # else: | |
| # st.session_state["saved_scenarios"] = OrderedDict() | |
| # | |
| # st.session_state["total_spends_change"] = 0 | |
| # st.session_state["optimization_channels"] = { | |
| # channel_name: False for channel_name in channel_list | |
| # } | |
| # st.session_state["disable_download_button"] = True | |
| # def initialize_data(): | |
| # # fetch data from excel | |
| # output = pd.read_excel('data.xlsx',sheet_name=None) | |
| # raw_df = output['RAW DATA MMM'] | |
| # contribution_df = output['CONTRIBUTION MMM'] | |
| # Revenue_df = output['Revenue'] | |
| # ## channels to be shows | |
| # channel_list = [] | |
| # for col in raw_df.columns: | |
| # if 'click' in col.lower() or 'spend' in col.lower() or 'imp' in col.lower(): | |
| # | |
| # channel_list.append(col) | |
| # else: | |
| # pass | |
| # ## NOTE : Considered only Desktop spends for all calculations | |
| # acutal_df = raw_df[raw_df.Region == 'Desktop'].copy() | |
| # ## NOTE : Considered one year of data | |
| # acutal_df = acutal_df[acutal_df.Date>'2020-12-31'] | |
| # actual_df = acutal_df.drop('Region',axis=1).sort_values(by='Date')[[*channel_list,'Date']] | |
| # ##load response curves | |
| # with open('./grammarly_response_curves.json','r') as f: | |
| # response_curves = json.load(f) | |
| # ## create channel dict for scenario creation | |
| # dates = actual_df.Date.values | |
| # channels = {} | |
| # rcs = {} | |
| # constant = 0. | |
| # for i,info_dict in enumerate(response_curves): | |
| # name = info_dict.get('name') | |
| # response_curve_type = info_dict.get('response_curve') | |
| # response_curve_params = info_dict.get('params') | |
| # rcs[name] = response_curve_params | |
| # if name != 'constant': | |
| # spends = actual_df[name].values | |
| # channel = Channel(name=name,dates=dates, | |
| # spends=spends, | |
| # response_curve_type=response_curve_type, | |
| # response_curve_params=response_curve_params, | |
| # bounds=np.array([-30,30])) | |
| # channels[name] = channel | |
| # else: | |
| # constant = info_dict.get('value',0.) * len(dates) | |
| # ## create scenario | |
| # scenario = Scenario(name='default', channels=channels, constant=constant) | |
| # default_scenario_dict = class_to_dict(scenario) | |
| # ## setting session variables | |
| # st.session_state['initialized'] = True | |
| # st.session_state['actual_df'] = actual_df | |
| # st.session_state['raw_df'] = raw_df | |
| # st.session_state['default_scenario_dict'] = default_scenario_dict | |
| # st.session_state['scenario'] = scenario | |
| # st.session_state['channels_list'] = channel_list | |
| # st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list} | |
| # st.session_state['rcs'] = rcs | |
| # for channel in channels.values(): | |
| # if channel.name not in st.session_state: | |
| # st.session_state[channel.name] = float(channel.actual_total_spends) | |
| # if 'xlsx_buffer' not in st.session_state: | |
| # st.session_state['xlsx_buffer'] = io.BytesIO() | |
| # ## for saving scenarios | |
| # if 'saved_scenarios' not in st.session_state: | |
| # if Path('../saved_scenarios.pkl').exists(): | |
| # with open('../saved_scenarios.pkl','rb') as f: | |
| # st.session_state['saved_scenarios'] = pickle.load(f) | |
| # else: | |
| # st.session_state['saved_scenarios'] = OrderedDict() | |
| # if 'total_spends_change' not in st.session_state: | |
| # st.session_state['total_spends_change'] = 0 | |
| # if 'optimization_channels' not in st.session_state: | |
| # st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list} | |
| # if 'disable_download_button' not in st.session_state: | |
| # st.session_state['disable_download_button'] = True | |
| def create_channel_summary(scenario, target_column): | |
| def round_off(x, round_off_decimal=0): | |
| # round off | |
| try: | |
| x = float(x) | |
| if x < 1 and x > 0: | |
| round_off_decimal = int(np.floor(np.abs(np.log10(x)))) + max( | |
| round_off_decimal, 1 | |
| ) | |
| x = np.round(x, round_off_decimal) | |
| elif x < 0 and x > -1: | |
| round_off_decimal = int(np.floor(np.abs(np.log10(np.abs(x))))) + max( | |
| round_off_decimal, 1 | |
| ) | |
| x = -np.round(x, round_off_decimal) | |
| else: | |
| x = np.round(x, round_off_decimal) | |
| return x | |
| except: | |
| return x | |
| summary_columns = [] | |
| actual_spends_rows = [] | |
| actual_sales_rows = [] | |
| actual_roi_rows = [] | |
| for channel in scenario.channels.values(): | |
| name_mod = channel.name.replace("_", " ") | |
| if name_mod.lower().endswith(" imp"): | |
| name_mod = name_mod.replace("Imp", " Impressions") | |
| summary_columns.append(name_mod) | |
| actual_spends_rows.append( | |
| format_numbers(float(channel.actual_total_spends * channel.conversion_rate)) | |
| ) | |
| actual_sales_rows.append(format_numbers((float(channel.actual_total_sales)))) | |
| roi = (channel.actual_total_sales) / ( | |
| channel.actual_total_spends * channel.conversion_rate | |
| ) | |
| if roi < 0.0001: | |
| roi = 0 | |
| actual_roi_rows.append( | |
| decimal_formater( | |
| str(round_off(roi, 2)), | |
| n_decimals=2, | |
| ) | |
| ) | |
| actual_summary_df = pd.DataFrame( | |
| [ | |
| summary_columns, | |
| actual_spends_rows, | |
| actual_sales_rows, | |
| actual_roi_rows, | |
| ] | |
| ).T | |
| actual_summary_df.columns = ["Channel", "Spends", target_column, "ROI"] | |
| actual_summary_df[target_column] = actual_summary_df[target_column].map( | |
| lambda x: str(x)[1:] | |
| ) | |
| return actual_summary_df | |
| # def create_channel_summary(scenario): | |
| # | |
| # # Provided data | |
| # data = { | |
| # 'Channel': ['Paid Search', 'Ga will cid baixo risco', 'Digital tactic others', 'Fb la tier 1', 'Fb la tier 2', 'Paid social others', 'Programmatic', 'Kwai', 'Indicacao', 'Infleux', 'Influencer'], | |
| # 'Spends': ['$ 11.3K', '$ 155.2K', '$ 50.7K', '$ 125.4K', '$ 125.2K', '$ 105K', '$ 3.3M', '$ 47.5K', '$ 55.9K', '$ 632.3K', '$ 48.3K'], | |
| # 'Revenue': ['558.0K', '3.5M', '5.2M', '3.1M', '3.1M', '2.1M', '20.8M', '1.6M', '728.4K', '22.9M', '4.8M'] | |
| # } | |
| # | |
| # # Create DataFrame | |
| # df = pd.DataFrame(data) | |
| # | |
| # # Convert currency strings to numeric values | |
| # df['Spends'] = df['Spends'].replace({'\$': '', 'K': '*1e3', 'M': '*1e6'}, regex=True).map(pd.eval).astype(int) | |
| # df['Revenue'] = df['Revenue'].replace({'\$': '', 'K': '*1e3', 'M': '*1e6'}, regex=True).map(pd.eval).astype(int) | |
| # | |
| # # Calculate ROI | |
| # df['ROI'] = ((df['Revenue'] - df['Spends']) / df['Spends']) | |
| # | |
| # # Format columns | |
| # format_currency = lambda x: f"${x:,.1f}" | |
| # format_roi = lambda x: f"{x:.1f}" | |
| # | |
| # df['Spends'] = ['$ 11.3K', '$ 155.2K', '$ 50.7K', '$ 125.4K', '$ 125.2K', '$ 105K', '$ 3.3M', '$ 47.5K', '$ 55.9K', '$ 632.3K', '$ 48.3K'] | |
| # df['Revenue'] = ['$ 536.3K', '$ 3.4M', '$ 5M', '$ 3M', '$ 3M', '$ 2M', '$ 20M', '$ 1.5M', '$ 7.1M', '$ 22M', '$ 4.6M'] | |
| # df['ROI'] = df['ROI'].apply(format_roi) | |
| # | |
| # return df | |
| # @st.cache_resource() | |
| # def create_contribution_pie(_scenario): | |
| # colors_map = { | |
| # col: color | |
| # for col, color in zip( | |
| # st.session_state["channels_list"], | |
| # plotly.colors.n_colors( | |
| # plotly.colors.hex_to_rgb("#BE6468"), | |
| # plotly.colors.hex_to_rgb("#E7B8B7"), | |
| # 20, | |
| # ), | |
| # ) | |
| # } | |
| # total_contribution_fig = make_subplots( | |
| # rows=1, | |
| # cols=2, | |
| # subplot_titles=["Spends", "Revenue"], | |
| # specs=[[{"type": "pie"}, {"type": "pie"}]], | |
| # ) | |
| # total_contribution_fig.add_trace( | |
| # go.Pie( | |
| # labels=[ | |
| # channel_name_formating(channel_name) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["Non Media"], | |
| # values=[ | |
| # round( | |
| # _scenario.channels[channel_name].actual_total_spends | |
| # * _scenario.channels[channel_name].conversion_rate, | |
| # 1, | |
| # ) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + [0], | |
| # marker=dict( | |
| # colors=[ | |
| # plotly.colors.label_rgb(colors_map[channel_name]) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["#F0F0F0"] | |
| # ), | |
| # hole=0.3, | |
| # ), | |
| # row=1, | |
| # col=1, | |
| # ) | |
| # total_contribution_fig.add_trace( | |
| # go.Pie( | |
| # labels=[ | |
| # channel_name_formating(channel_name) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["Non Media"], | |
| # values=[ | |
| # _scenario.channels[channel_name].actual_total_sales | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + [_scenario.correction.sum() + _scenario.constant.sum()], | |
| # hole=0.3, | |
| # ), | |
| # row=1, | |
| # col=2, | |
| # ) | |
| # total_contribution_fig.update_traces( | |
| # textposition="inside", texttemplate="%{percent:.1%}" | |
| # ) | |
| # total_contribution_fig.update_layout( | |
| # uniformtext_minsize=12, | |
| # title="Channel contribution", | |
| # uniformtext_mode="hide", | |
| # ) | |
| # return total_contribution_fig | |
| # @st.cache_resource() | |
| # def create_contribution_pie(_scenario): | |
| # colors = plotly.colors.qualitative.Plotly # A diverse color palette | |
| # colors_map = { | |
| # col: colors[i % len(colors)] | |
| # for i, col in enumerate(st.session_state["channels_list"]) | |
| # } | |
| # total_contribution_fig = make_subplots( | |
| # rows=1, | |
| # cols=2, | |
| # subplot_titles=["Spends", "Revenue"], | |
| # specs=[[{"type": "pie"}, {"type": "pie"}]], | |
| # ) | |
| # total_contribution_fig.add_trace( | |
| # go.Pie( | |
| # labels=[ | |
| # channel_name_formating(channel_name) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["Non Media"], | |
| # values=[ | |
| # round( | |
| # _scenario.channels[channel_name].actual_total_spends | |
| # * _scenario.channels[channel_name].conversion_rate, | |
| # 1, | |
| # ) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + [0], | |
| # marker=dict( | |
| # colors=[ | |
| # colors_map[channel_name] | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["#F0F0F0"] | |
| # ), | |
| # hole=0.3, | |
| # ), | |
| # row=1, | |
| # col=1, | |
| # ) | |
| # total_contribution_fig.add_trace( | |
| # go.Pie( | |
| # labels=[ | |
| # channel_name_formating(channel_name) | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["Non Media"], | |
| # values=[ | |
| # _scenario.channels[channel_name].actual_total_sales | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + [_scenario.correction.sum() + _scenario.constant.sum()], | |
| # marker=dict( | |
| # colors=[ | |
| # colors_map[channel_name] | |
| # for channel_name in st.session_state["channels_list"] | |
| # ] | |
| # + ["#F0F0F0"] | |
| # ), | |
| # hole=0.3, | |
| # ), | |
| # row=1, | |
| # col=2, | |
| # ) | |
| # total_contribution_fig.update_traces( | |
| # textposition="inside", texttemplate="%{percent:.1%}" | |
| # ) | |
| # total_contribution_fig.update_layout( | |
| # uniformtext_minsize=12, | |
| # title="Channel contribution", | |
| # uniformtext_mode="hide", | |
| # ) | |
| # return total_contribution_fig | |
| # @st.cache_resource() | |
| def create_contribution_pie(_scenario, target_col): | |
| colors = plotly.colors.qualitative.Plotly # A diverse color palette | |
| colors_map = { | |
| col: colors[i % len(colors)] | |
| for i, col in enumerate(st.session_state["channels_list"]) | |
| } | |
| spends_values = [ | |
| round( | |
| _scenario.channels[channel_name].actual_total_spends | |
| * _scenario.channels[channel_name].conversion_rate, | |
| 1, | |
| ) | |
| for channel_name in st.session_state["channels_list"] | |
| ] | |
| spends_values.append(0) # Adding Non Media value | |
| revenue_values = [ | |
| _scenario.channels[channel_name].actual_total_sales | |
| for channel_name in st.session_state["channels_list"] | |
| ] | |
| revenue_values.append( | |
| _scenario.correction.sum() + _scenario.constant.sum() | |
| ) # Adding Non Media value | |
| total_contribution_fig = make_subplots( | |
| rows=1, | |
| cols=2, | |
| subplot_titles=["Spend", target_col], | |
| specs=[[{"type": "pie"}, {"type": "pie"}]], | |
| ) | |
| total_contribution_fig.add_trace( | |
| go.Pie( | |
| labels=[ | |
| channel_name_formating(channel_name) | |
| for channel_name in st.session_state["channels_list"] | |
| ] | |
| + ["Non Media"], | |
| values=spends_values, | |
| marker=dict( | |
| colors=[ | |
| colors_map[channel_name] | |
| for channel_name in st.session_state["channels_list"] | |
| ] | |
| + ["#F0F0F0"] | |
| ), | |
| hole=0.3, | |
| ), | |
| row=1, | |
| col=1, | |
| ) | |
| total_contribution_fig.add_trace( | |
| go.Pie( | |
| labels=[ | |
| channel_name_formating(channel_name) | |
| for channel_name in st.session_state["channels_list"] | |
| ] | |
| + ["Non Media"], | |
| values=revenue_values, | |
| marker=dict( | |
| colors=[ | |
| colors_map[channel_name] | |
| for channel_name in st.session_state["channels_list"] | |
| ] | |
| + ["#F0F0F0"] | |
| ), | |
| hole=0.3, | |
| ), | |
| row=1, | |
| col=2, | |
| ) | |
| total_contribution_fig.update_traces( | |
| textposition="inside", texttemplate="%{percent:.1%}" | |
| ) | |
| total_contribution_fig.update_layout( | |
| uniformtext_minsize=12, | |
| title="Channel contribution", | |
| uniformtext_mode="hide", | |
| ) | |
| return total_contribution_fig | |
| # def create_contribuion_stacked_plot(scenario): | |
| # weekly_contribution_fig = make_subplots(rows=1, cols=2,subplot_titles=['Spends','Revenue'],specs=[[{"type": "bar"}, {"type": "bar"}]]) | |
| # raw_df = st.session_state['raw_df'] | |
| # df = raw_df.sort_values(by='Date') | |
| # x = df.Date | |
| # weekly_spends_data = [] | |
| # weekly_sales_data = [] | |
| # for channel_name in st.session_state['channels_list']: | |
| # weekly_spends_data.append((go.Bar(x=x, | |
| # y=scenario.channels[channel_name].actual_spends * scenario.channels[channel_name].conversion_rate, | |
| # name=channel_name_formating(channel_name), | |
| # hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}", | |
| # legendgroup=channel_name))) | |
| # weekly_sales_data.append((go.Bar(x=x, | |
| # y=scenario.channels[channel_name].actual_sales, | |
| # name=channel_name_formating(channel_name), | |
| # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| # legendgroup=channel_name, showlegend=False))) | |
| # for _d in weekly_spends_data: | |
| # weekly_contribution_fig.add_trace(_d, row=1, col=1) | |
| # for _d in weekly_sales_data: | |
| # weekly_contribution_fig.add_trace(_d, row=1, col=2) | |
| # weekly_contribution_fig.add_trace(go.Bar(x=x, | |
| # y=scenario.constant + scenario.correction, | |
| # name='Non Media', | |
| # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), row=1, col=2) | |
| # weekly_contribution_fig.update_layout(barmode='stack', title='Channel contribuion by week', xaxis_title='Date') | |
| # weekly_contribution_fig.update_xaxes(showgrid=False) | |
| # weekly_contribution_fig.update_yaxes(showgrid=False) | |
| # return weekly_contribution_fig | |
| # @st.cache_resource() | |
| # def create_channel_spends_sales_plot(channel): | |
| # if channel is not None: | |
| # x = channel.dates | |
| # _spends = channel.actual_spends * channel.conversion_rate | |
| # _sales = channel.actual_sales | |
| # channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]]) | |
| # channel_sales_spends_fig.add_trace(go.Bar(x=x, y=_sales,marker_color='#c1f7dc',name='Revenue', hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), secondary_y = False) | |
| # channel_sales_spends_fig.add_trace(go.Scatter(x=x, y=_spends,line=dict(color='#005b96'),name='Spends',hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}"), secondary_y = True) | |
| # channel_sales_spends_fig.update_layout(xaxis_title='Date',yaxis_title='Revenue',yaxis2_title='Spends ($)',title='Channel spends and Revenue week wise') | |
| # channel_sales_spends_fig.update_xaxes(showgrid=False) | |
| # channel_sales_spends_fig.update_yaxes(showgrid=False) | |
| # else: | |
| # raw_df = st.session_state['raw_df'] | |
| # df = raw_df.sort_values(by='Date') | |
| # x = df.Date | |
| # scenario = class_from_dict(st.session_state['default_scenario_dict']) | |
| # _sales = scenario.constant + scenario.correction | |
| # channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]]) | |
| # channel_sales_spends_fig.add_trace(go.Bar(x=x, y=_sales,marker_color='#c1f7dc',name='Revenue', hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), secondary_y = False) | |
| # # channel_sales_spends_fig.add_trace(go.Scatter(x=x, y=_spends,line=dict(color='#15C39A'),name='Spends',hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}"), secondary_y = True) | |
| # channel_sales_spends_fig.update_layout(xaxis_title='Date',yaxis_title='Revenue',yaxis2_title='Spends ($)',title='Channel spends and Revenue week wise') | |
| # channel_sales_spends_fig.update_xaxes(showgrid=False) | |
| # channel_sales_spends_fig.update_yaxes(showgrid=False) | |
| # return channel_sales_spends_fig | |
| # Define a shared color palette | |
| # def create_contribution_pie(): | |
| # color_palette = ['#F3F3F0', '#5E7D7E', '#2FA1FF', '#00EDED', '#00EAE4', '#304550', '#EDEBEB', '#7FBEFD', '#003059', '#A2F3F3', '#E1D6E2', '#B6B6B6'] | |
| # total_contribution_fig = make_subplots(rows=1, cols=2, subplot_titles=['Spends', 'Revenue'], specs=[[{"type": "pie"}, {"type": "pie"}]]) | |
| # | |
| # channels_list = ['Paid Search', 'Ga will cid baixo risco', 'Digital tactic others', 'Fb la tier 1', 'Fb la tier 2', 'Paid social others', 'Programmatic', 'Kwai', 'Indicacao', 'Infleux', 'Influencer', 'Non Media'] | |
| # | |
| # # Assign colors from the limited palette to channels | |
| # colors_map = {col: color_palette[i % len(color_palette)] for i, col in enumerate(channels_list)} | |
| # colors_map['Non Media'] = color_palette[5] # Assign fixed green color for 'Non Media' | |
| # | |
| # # Hardcoded values for Spends and Revenue | |
| # spends_values = [0.5, 3.36, 1.1, 2.7, 2.7, 2.27, 70.6, 1, 1, 13.7, 1, 0] | |
| # revenue_values = [1, 4, 5, 3, 3, 2, 50.8, 1.5, 0.7, 13, 0, 16] | |
| # | |
| # # Add trace for Spends pie chart | |
| # total_contribution_fig.add_trace( | |
| # go.Pie( | |
| # labels=[channel_name for channel_name in channels_list], | |
| # values=spends_values, | |
| # marker=dict(colors=[colors_map[channel_name] for channel_name in channels_list]), | |
| # hole=0.3 | |
| # ), | |
| # row=1, col=1 | |
| # ) | |
| # | |
| # # Add trace for Revenue pie chart | |
| # total_contribution_fig.add_trace( | |
| # go.Pie( | |
| # labels=[channel_name for channel_name in channels_list], | |
| # values=revenue_values, | |
| # marker=dict(colors=[colors_map[channel_name] for channel_name in channels_list]), | |
| # hole=0.3 | |
| # ), | |
| # row=1, col=2 | |
| # ) | |
| # | |
| # total_contribution_fig.update_traces(textposition='inside', texttemplate='%{percent:.1%}') | |
| # total_contribution_fig.update_layout(uniformtext_minsize=12, title='Channel contribution', uniformtext_mode='hide') | |
| # return total_contribution_fig | |
| # @st.cache_resource() | |
| # def create_contribuion_stacked_plot(_scenario): | |
| # weekly_contribution_fig = make_subplots( | |
| # rows=1, | |
| # cols=2, | |
| # subplot_titles=["Spends", "Revenue"], | |
| # specs=[[{"type": "bar"}, {"type": "bar"}]], | |
| # ) | |
| # raw_df = st.session_state["raw_df"] | |
| # df = raw_df.sort_values(by="Date") | |
| # x = df.Date | |
| # weekly_spends_data = [] | |
| # weekly_sales_data = [] | |
| # for i, channel_name in enumerate(st.session_state["channels_list"]): | |
| # color = color_palette[i % len(color_palette)] | |
| # weekly_spends_data.append( | |
| # go.Bar( | |
| # x=x, | |
| # y=_scenario.channels[channel_name].actual_spends | |
| # * _scenario.channels[channel_name].conversion_rate, | |
| # name=channel_name_formating(channel_name), | |
| # hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}", | |
| # legendgroup=channel_name, | |
| # marker_color=color, | |
| # ) | |
| # ) | |
| # weekly_sales_data.append( | |
| # go.Bar( | |
| # x=x, | |
| # y=_scenario.channels[channel_name].actual_sales, | |
| # name=channel_name_formating(channel_name), | |
| # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| # legendgroup=channel_name, | |
| # showlegend=False, | |
| # marker_color=color, | |
| # ) | |
| # ) | |
| # for _d in weekly_spends_data: | |
| # weekly_contribution_fig.add_trace(_d, row=1, col=1) | |
| # for _d in weekly_sales_data: | |
| # weekly_contribution_fig.add_trace(_d, row=1, col=2) | |
| # weekly_contribution_fig.add_trace( | |
| # go.Bar( | |
| # x=x, | |
| # y=_scenario.constant + _scenario.correction, | |
| # name="Non Media", | |
| # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| # marker_color=color_palette[-1], | |
| # ), | |
| # row=1, | |
| # col=2, | |
| # ) | |
| # weekly_contribution_fig.update_layout( | |
| # barmode="stack", | |
| # title="Channel contribution by week", | |
| # xaxis_title="Date", | |
| # ) | |
| # weekly_contribution_fig.update_xaxes(showgrid=False) | |
| # weekly_contribution_fig.update_yaxes(showgrid=False) | |
| # return weekly_contribution_fig | |
| # @st.cache_resource() | |
| def create_contribuion_stacked_plot(_scenario, target_col): | |
| color_palette = plotly.colors.qualitative.Plotly # A diverse color palette | |
| weekly_contribution_fig = make_subplots( | |
| rows=1, | |
| cols=2, | |
| subplot_titles=["Spend", target_col], | |
| specs=[[{"type": "bar"}, {"type": "bar"}]], | |
| ) | |
| raw_df = st.session_state["raw_df"] | |
| df = raw_df.sort_values(by="Date") | |
| x = df.Date | |
| weekly_spends_data = [] | |
| weekly_sales_data = [] | |
| for i, channel_name in enumerate(st.session_state["channels_list"]): | |
| color = color_palette[i % len(color_palette)] | |
| weekly_spends_data.append( | |
| go.Bar( | |
| x=x, | |
| y=_scenario.channels[channel_name].actual_spends | |
| * _scenario.channels[channel_name].conversion_rate, | |
| name=channel_name_formating(channel_name), | |
| hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}", | |
| legendgroup=channel_name, | |
| marker_color=color, | |
| ) | |
| ) | |
| weekly_sales_data.append( | |
| go.Bar( | |
| x=x, | |
| y=_scenario.channels[channel_name].actual_sales, | |
| name=channel_name_formating(channel_name), | |
| hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| legendgroup=channel_name, | |
| showlegend=False, | |
| marker_color=color, | |
| ) | |
| ) | |
| for _d in weekly_spends_data: | |
| weekly_contribution_fig.add_trace(_d, row=1, col=1) | |
| for _d in weekly_sales_data: | |
| weekly_contribution_fig.add_trace(_d, row=1, col=2) | |
| weekly_contribution_fig.add_trace( | |
| go.Bar( | |
| x=x, | |
| y=_scenario.constant + _scenario.correction, | |
| name="Non Media", | |
| hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| marker_color=color_palette[-1], | |
| ), | |
| row=1, | |
| col=2, | |
| ) | |
| weekly_contribution_fig.update_layout( | |
| barmode="stack", | |
| title="Channel contribution by week", | |
| xaxis_title="Date", | |
| ) | |
| weekly_contribution_fig.update_xaxes(showgrid=False) | |
| weekly_contribution_fig.update_yaxes(showgrid=False) | |
| return weekly_contribution_fig | |
| def create_channel_spends_sales_plot(channel, target_col): | |
| if channel is not None: | |
| x = channel.dates | |
| _spends = channel.actual_spends * channel.conversion_rate | |
| _sales = channel.actual_sales | |
| channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]]) | |
| channel_sales_spends_fig.add_trace( | |
| go.Bar( | |
| x=x, | |
| y=_sales, | |
| marker_color=color_palette[ | |
| 3 | |
| ], # You can choose a color from the palette | |
| name=target_col, | |
| hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| ), | |
| secondary_y=False, | |
| ) | |
| channel_sales_spends_fig.add_trace( | |
| go.Scatter( | |
| x=x, | |
| y=_spends, | |
| line=dict( | |
| color=color_palette[2] | |
| ), # You can choose another color from the palette | |
| name="Spends", | |
| hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}", | |
| ), | |
| secondary_y=True, | |
| ) | |
| channel_sales_spends_fig.update_layout( | |
| xaxis_title="Date", | |
| yaxis_title=target_col, | |
| yaxis2_title="Spend ($)", | |
| title="Weekly Channel Spends and " + target_col, | |
| ) | |
| channel_sales_spends_fig.update_xaxes(showgrid=False) | |
| channel_sales_spends_fig.update_yaxes(showgrid=False) | |
| else: | |
| raw_df = st.session_state["raw_df"] | |
| df = raw_df.sort_values(by="Date") | |
| x = df.Date | |
| scenario = class_from_dict(st.session_state["default_scenario_dict"]) | |
| _sales = scenario.constant + scenario.correction | |
| channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]]) | |
| channel_sales_spends_fig.add_trace( | |
| go.Bar( | |
| x=x, | |
| y=_sales, | |
| marker_color=color_palette[ | |
| 0 | |
| ], # You can choose a color from the palette | |
| name="Revenue", | |
| hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}", | |
| ), | |
| secondary_y=False, | |
| ) | |
| channel_sales_spends_fig.update_layout( | |
| xaxis_title="Date", | |
| yaxis_title="Revenue", | |
| yaxis2_title="Spend ($)", | |
| title="Channel spends and Revenue week-wise", | |
| ) | |
| channel_sales_spends_fig.update_xaxes(showgrid=False) | |
| channel_sales_spends_fig.update_yaxes(showgrid=False) | |
| return channel_sales_spends_fig | |
| def format_numbers(value, n_decimals=1, include_indicator=True): | |
| if include_indicator: | |
| return f"{CURRENCY_INDICATOR} {numerize(value,n_decimals)}" | |
| else: | |
| return f"{numerize(value,n_decimals)}" | |
| def decimal_formater(num_string, n_decimals=1): | |
| parts = num_string.split(".") | |
| if len(parts) == 1: | |
| return num_string + "." + "0" * n_decimals | |
| else: | |
| to_be_padded = n_decimals - len(parts[-1]) | |
| if to_be_padded > 0: | |
| return num_string + "0" * to_be_padded | |
| else: | |
| return num_string | |
| def channel_name_formating(channel_name): | |
| name_mod = channel_name.replace("_", " ") | |
| if name_mod.lower().endswith(" imp"): | |
| name_mod = name_mod.replace("Imp", "Spend") | |
| elif name_mod.lower().endswith(" clicks"): | |
| name_mod = name_mod.replace("Clicks", "Spend") | |
| return name_mod | |
| def send_email(email, message): | |
| s = smtplib.SMTP("smtp.gmail.com", 587) | |
| s.starttls() | |
| s.login("geethu4444@gmail.com", "jgydhpfusuremcol") | |
| s.sendmail("geethu4444@gmail.com", email, message) | |
| s.quit() | |
| # if __name__ == "__main__": | |
| # initialize_data() | |