Spaces:
Sleeping
Sleeping
Pragya Jatav
commited on
Commit
·
b12e77d
1
Parent(s):
bca631e
m1
Browse files- Model_Result_Overview.py +148 -279
- Overview_data_test_panel@#prospects.xlsx +0 -0
- Streamlit_functions.py +154 -1
- __pycache__/Streamlit_functions.cpython-310.pyc +0 -0
- __pycache__/classes.cpython-310.pyc +0 -0
- __pycache__/utilities.cpython-310.pyc +0 -0
- classes.py +73 -50
- pages/1_Model_Quality.py +1 -1
- pages/2_Scenario_Planner.py +4 -1
- pages/5_Glossary.py +17 -14
- response_curves_parameters.xlsx +0 -0
- summary_df.pkl +1 -1
- utilities.py +4 -3
Model_Result_Overview.py
CHANGED
@@ -107,287 +107,156 @@ if auth_status:
|
|
107 |
if not is_state_initiaized:
|
108 |
a=1
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
options = [
|
154 |
-
"Month on Month",
|
155 |
-
"Year on Year"]
|
156 |
-
col1, col2 = st.columns(2)
|
157 |
-
# Create a dropdown menu
|
158 |
-
with col1:
|
159 |
-
selected_option = st.selectbox('Select a comparison', options)
|
160 |
-
with col2:
|
161 |
-
st.markdown("""</br>""",unsafe_allow_html=True)
|
162 |
-
if selected_option == "Month on Month" :
|
163 |
-
|
164 |
-
st.markdown(
|
165 |
-
f"""
|
166 |
-
<div style="padding: 5px; border-radius: 5px; background-color: #FFFFE0; width: fit-content; display: inline-block;">
|
167 |
-
<strong> Comparision of current month spends to previous month spends</strong>
|
168 |
-
</div>
|
169 |
-
""",
|
170 |
-
unsafe_allow_html=True
|
171 |
-
)
|
172 |
-
else :
|
173 |
-
st.markdown(
|
174 |
-
f"""
|
175 |
-
<div style="padding: 5px; border-radius: 5px; background-color: #FFFFE0; width: fit-content; display: inline-block;">
|
176 |
-
<strong> Comparision of current month spends to the same month in previous year</strong>
|
177 |
-
</div>
|
178 |
-
""",
|
179 |
-
unsafe_allow_html=True
|
180 |
-
)
|
181 |
-
# Waterfall chart
|
182 |
-
|
183 |
-
def get_month_year_list(start_date, end_date):
|
184 |
-
# Generate a range of dates from start_date to end_date with a monthly frequency
|
185 |
-
dates = pd.date_range(start=start_date, end=end_date, freq='MS') # 'MS' is month start frequency
|
186 |
-
|
187 |
-
# Extract month and year from each date and create a list of tuples
|
188 |
-
month_year_list = [(date.month, date.year) for date in dates]
|
189 |
-
|
190 |
-
return month_year_list
|
191 |
-
def get_start_end_dates(month, year):
|
192 |
-
start_date = datetime(year, month, 1).date()
|
193 |
-
|
194 |
-
if month == 12:
|
195 |
-
end_date = datetime(year + 1, 1, 1).date() - timedelta(days=1)
|
196 |
-
else:
|
197 |
-
end_date = datetime(year, month + 1, 1).date() - timedelta(days=1)
|
198 |
-
|
199 |
-
return start_date, end_date
|
200 |
-
|
201 |
-
month_year_list = get_month_year_list(start_date, end_date)
|
202 |
-
dropdown_options = [f"{date.strftime('%B %Y')}" for date in pd.date_range(start=start_date, end=end_date, freq='MS')]
|
203 |
-
waterfall_option = st.selectbox("Select a month:", dropdown_options)
|
204 |
-
waterfall_date = datetime.strptime(waterfall_option, "%B %Y")
|
205 |
-
waterfall_month = waterfall_date.month
|
206 |
-
waterfall_year = waterfall_date.year
|
207 |
-
waterfall_start_date, waterfall_end_date = get_start_end_dates(waterfall_month, waterfall_year)
|
208 |
-
|
209 |
-
fig = sf.waterfall(waterfall_start_date,waterfall_end_date,selected_option)
|
210 |
-
st.plotly_chart(fig,use_container_width=True)
|
211 |
-
|
212 |
-
# Waterfall table
|
213 |
-
shares_df = sf.shares_df_func(waterfall_start_date,waterfall_end_date)
|
214 |
-
st.table(sf.waterfall_table_func(shares_df).style.format("{:.0%}"))
|
215 |
-
|
216 |
-
## Channel Contribution Bar Chart
|
217 |
-
st.plotly_chart(sf.channel_contribution(start_date,end_date),use_container_width=True)
|
218 |
-
st.plotly_chart(sf.chanel_spends(start_date,end_date),use_container_width=True)
|
219 |
-
# Format first three rows in percentage format
|
220 |
-
# styled_df = sf.shares_table_func(shares_df)
|
221 |
-
# # styled_df = styled_df.round(0).astype(int)
|
222 |
-
# styled_df.iloc[:3] = (styled_df.iloc[:3]).astype(int)
|
223 |
-
|
224 |
-
# # Round next two rows to two decimal places
|
225 |
-
# styled_df.iloc[3:5] = styled_df.iloc[3:5].round(0).astype(str)
|
226 |
-
|
227 |
-
# st.table(styled_df)
|
228 |
-
st.dataframe(sf.shares_table_func(shares_df),use_container_width=True)
|
229 |
-
|
230 |
-
st.dataframe(sf.eff_table_func(shares_df).style.format({"TOTAL SPEND": "{:,.0f}", "TOTAL SUPPORT": "{:,.0f}", "TOTAL CONTRIBUTION": "{:,.0f}"}),use_container_width=True)
|
231 |
-
|
232 |
-
### CPP CHART
|
233 |
-
st.plotly_chart(sf.cpp(start_date,end_date),use_container_width=True)
|
234 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
### Base decomp CHART
|
236 |
-
|
237 |
|
238 |
### Media decomp CHART
|
239 |
-
|
240 |
|
241 |
-
|
242 |
-
# st.write(fig.columns)
|
243 |
-
# st.dataframe(fig)
|
244 |
-
|
245 |
-
# def panel_fetch(file_selected):
|
246 |
-
# raw_data_mmm_df = pd.read_excel(file_selected, sheet_name="RAW DATA MMM")
|
247 |
-
|
248 |
-
# if "Panel" in raw_data_mmm_df.columns:
|
249 |
-
# panel = list(set(raw_data_mmm_df["Panel"]))
|
250 |
-
# else:
|
251 |
-
# raw_data_mmm_df = None
|
252 |
-
# panel = None
|
253 |
-
|
254 |
-
# return panel
|
255 |
-
|
256 |
-
# def rerun():
|
257 |
-
# st.rerun()
|
258 |
-
|
259 |
-
# metrics_selected='prospects'
|
260 |
-
|
261 |
-
# file_selected = (
|
262 |
-
# f"Overview_data_test_panel@#{metrics_selected}.xlsx"
|
263 |
-
# )
|
264 |
-
# panel_list = panel_fetch(file_selected)
|
265 |
-
|
266 |
-
# if "selected_markets" not in st.session_state:
|
267 |
-
# st.session_state['selected_markets']='DMA1'
|
268 |
-
|
269 |
-
|
270 |
-
# st.header('Overview of previous spends')
|
271 |
-
|
272 |
-
# selected_market= st.selectbox(
|
273 |
-
# "Select Markets",
|
274 |
-
# ["Total Market"] + panel_list
|
275 |
-
# )
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
# initialize_data(target_col,selected_market)
|
280 |
-
# scenario = st.session_state['scenario']
|
281 |
-
# raw_df = st.session_state['raw_df']
|
282 |
-
# st.write(scenario.actual_total_spends)
|
283 |
-
# st.write(scenario.actual_total_sales)
|
284 |
-
# columns = st.columns((1,1,3))
|
285 |
-
|
286 |
-
# with columns[0]:
|
287 |
-
# st.metric(label='Spends', value=format_numbers(float(scenario.actual_total_spends)))
|
288 |
-
# #### print(f"##################### {scenario.actual_total_sales} ##################")
|
289 |
-
# with columns[1]:
|
290 |
-
# st.metric(label=target, value=format_numbers(float(scenario.actual_total_sales),include_indicator=False))
|
291 |
-
|
292 |
-
|
293 |
-
# actual_summary_df = create_channel_summary(scenario)
|
294 |
-
# actual_summary_df['Channel'] = actual_summary_df['Channel'].apply(channel_name_formating)
|
295 |
-
|
296 |
-
# columns = st.columns((2,1))
|
297 |
-
# #with columns[0]:
|
298 |
-
# with st.expander('Channel wise overview'):
|
299 |
-
# st.markdown(actual_summary_df.style.set_table_styles(
|
300 |
-
# [{
|
301 |
-
# 'selector': 'th',
|
302 |
-
# 'props': [('background-color', '#FFFFF')]
|
303 |
-
# },
|
304 |
-
# {
|
305 |
-
# 'selector' : 'tr:nth-child(even)',
|
306 |
-
# 'props' : [('background-color', '#FFFFF')]
|
307 |
-
# }]).to_html(), unsafe_allow_html=True)
|
308 |
-
|
309 |
-
# st.markdown("<hr>",unsafe_allow_html=True)
|
310 |
-
# ##############################
|
311 |
-
|
312 |
-
# st.plotly_chart(create_contribution_pie(scenario),use_container_width=True)
|
313 |
-
# st.markdown("<hr>",unsafe_allow_html=True)
|
314 |
-
|
315 |
-
|
316 |
-
# ################################3
|
317 |
-
# st.plotly_chart(create_contribuion_stacked_plot(scenario),use_container_width=True)
|
318 |
-
# st.markdown("<hr>",unsafe_allow_html=True)
|
319 |
-
# #######################################
|
320 |
-
|
321 |
-
# selected_channel_name = st.selectbox('Channel', st.session_state['channels_list'] + ['non media'], format_func=channel_name_formating)
|
322 |
-
# selected_channel = scenario.channels.get(selected_channel_name,None)
|
323 |
-
|
324 |
-
# st.plotly_chart(create_channel_spends_sales_plot(selected_channel), use_container_width=True)
|
325 |
-
|
326 |
-
# st.markdown("<hr>",unsafe_allow_html=True)
|
327 |
-
|
328 |
-
# elif auth_status == False:
|
329 |
-
# st.error('Username/Password is incorrect')
|
330 |
-
|
331 |
-
# if auth_status != True:
|
332 |
-
# try:
|
333 |
-
# username_forgot_pw, email_forgot_password, random_password = authenticator.forgot_password('Forgot password')
|
334 |
-
# if username_forgot_pw:
|
335 |
-
# st.success('New password sent securely')
|
336 |
-
# # Random password to be transferred to user securely
|
337 |
-
# elif username_forgot_pw == False:
|
338 |
-
# st.error('Username not found')
|
339 |
-
# except Exception as e:
|
340 |
-
# st.error(e)
|
341 |
-
# st.header("")
|
342 |
-
# st.markdown("<h5 style='font-weight: normal;'>MMM Readout for Selected Period</h5>", unsafe_allow_html=True)
|
343 |
-
# #### Input Select Start and End Date
|
344 |
-
|
345 |
-
# # Create two columns for start date and end date input
|
346 |
-
# col1, col2 = st.columns(2)
|
347 |
-
|
348 |
-
# with col1:
|
349 |
-
# start_date = st.date_input("Start Date: ")
|
350 |
-
|
351 |
-
# with col2:
|
352 |
-
# end_date = st.date_input("End Date: ")
|
353 |
-
# # Dropdown menu options
|
354 |
-
# options = [
|
355 |
-
# "Month on Month",
|
356 |
-
# "Year on Year"]
|
357 |
-
# col1, col2 = st.columns(2)
|
358 |
-
# # Create a dropdown menu
|
359 |
-
# with col1:
|
360 |
-
# selected_option = st.selectbox('Select a comparison', options)
|
361 |
-
# with col2:
|
362 |
-
# st.write("")
|
363 |
-
# # Waterfall chart
|
364 |
-
# fig = sf.waterfall(start_date,end_date,selected_option)
|
365 |
-
# st.plotly_chart(fig)
|
366 |
-
|
367 |
-
# # Waterfall table
|
368 |
-
# shares_df = sf.shares_df_func(start_date,end_date)
|
369 |
-
# st.table(sf.waterfall_table_func(shares_df).style.format("{:.0%}"))
|
370 |
-
|
371 |
-
# ## Channel Contribution Bar Chart
|
372 |
-
# st.plotly_chart(sf.channel_contribution(start_date,end_date))
|
373 |
-
# # Format first three rows in percentage format
|
374 |
-
# # styled_df = sf.shares_table_func(shares_df)
|
375 |
-
# # # styled_df = styled_df.round(0).astype(int)
|
376 |
-
# # styled_df.iloc[:3] = (styled_df.iloc[:3]).astype(int)
|
377 |
-
|
378 |
-
# # # Round next two rows to two decimal places
|
379 |
-
# # styled_df.iloc[3:5] = styled_df.iloc[3:5].round(0).astype(str)
|
380 |
-
|
381 |
-
# # st.table(styled_df)
|
382 |
-
# st.dataframe(sf.shares_table_func(shares_df))
|
383 |
-
|
384 |
-
# st.dataframe(sf.eff_table_func(shares_df))
|
385 |
-
|
386 |
-
# ### CPP CHART
|
387 |
-
# st.plotly_chart(sf.cpp(start_date,end_date))
|
388 |
-
|
389 |
-
# ### Base decomp CHART
|
390 |
-
# st.plotly_chart(sf.base_decomp())
|
391 |
-
|
392 |
-
# ### Media decomp CHART
|
393 |
-
# st.plotly_chart(sf.media_decomp())
|
|
|
107 |
if not is_state_initiaized:
|
108 |
a=1
|
109 |
|
110 |
+
with st.expander("View Channel Wise Spend And Prospect Analysis "):
|
111 |
+
# Create two columns for start date and end date input
|
112 |
+
col1, col2 = st.columns(2)
|
113 |
+
min_date,max_date = sf.get_date_range()
|
114 |
+
# st.write(min_date,max_date)
|
115 |
+
# min_date = datetime(2023, 1, 1)
|
116 |
+
# max_date = datetime(2024, 12, 31)
|
117 |
+
default_date1,default_date2 = sf.get_default_dates()
|
118 |
+
# st.write(default_date1,default_date2)
|
119 |
+
with col1:
|
120 |
+
start_date = st.date_input("Start Date: ",value=default_date1,min_value=min_date,
|
121 |
+
max_value=max_date)
|
122 |
+
with col2:
|
123 |
+
end_date = st.date_input("End Date: ",value = default_date2,min_value=min_date,
|
124 |
+
max_value=max_date)
|
125 |
+
# col1, col2 = st.columns(2)
|
126 |
+
# with col1:
|
127 |
+
# fig = sf.pie_spend(start_date,end_date)
|
128 |
+
# st.plotly_chart(fig,use_container_width=True)
|
129 |
+
# with col2:
|
130 |
+
# fig = sf.pie_contributions(start_date,end_date)
|
131 |
+
# st.plotly_chart(fig,use_container_width=True)
|
132 |
+
# st.header("Distribution of Spends and Contributions")
|
133 |
+
fig = sf.pie_charts(start_date,end_date)
|
134 |
+
st.plotly_chart(fig,use_container_width=True)
|
135 |
+
|
136 |
+
## Channel Contribution Bar Chart
|
137 |
+
st.plotly_chart(sf.channel_contribution(start_date,end_date),use_container_width=True)
|
138 |
+
st.plotly_chart(sf.chanel_spends(start_date,end_date),use_container_width=True)
|
139 |
+
# Format first three rows in percentage format
|
140 |
+
# styled_df = sf.shares_table_func(shares_df)
|
141 |
+
# # styled_df = styled_df.round(0).astype(int)
|
142 |
+
# styled_df.iloc[:3] = (styled_df.iloc[:3]).astype(int)
|
143 |
+
|
144 |
+
# # Round next two rows to two decimal places
|
145 |
+
# styled_df.iloc[3:5] = styled_df.iloc[3:5].round(0).astype(str)
|
146 |
+
|
147 |
+
# st.table(styled_df)
|
148 |
+
shares_df = sf.shares_df_func(start_date,end_date)
|
149 |
+
st.dataframe(sf.shares_table_func(shares_df),use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
+
st.dataframe(sf.eff_table_func(shares_df).style.format({"TOTAL SPEND": "{:,.0f}", "TOTAL SUPPORT": "{:,.0f}", "TOTAL CONTRIBUTION": "{:,.0f}"}),use_container_width=True)
|
152 |
+
|
153 |
+
### CPP CHART
|
154 |
+
st.plotly_chart(sf.cpp(start_date,end_date),use_container_width=True)
|
155 |
+
data_selection_type = st.radio("Select Input Type",["Compare Monthly Change", "Compare Custom Range"])
|
156 |
+
waterfall_start_date,waterfall_end_date = start_date,end_date
|
157 |
+
with st.expander("View Change in MMM Estimated Prospect Contributions Analysis"):
|
158 |
+
# Dropdown menu options
|
159 |
+
st.markdown("<h1 style='font-size:28px;'>Change in MMM Estimated Prospect Contributions</h1>", unsafe_allow_html=True)
|
160 |
+
if data_selection_type == "Compare Monthly Change":
|
161 |
+
options = [
|
162 |
+
"Month on Month",
|
163 |
+
"Year on Year"]
|
164 |
+
col1, col2 = st.columns(2)
|
165 |
+
# Create a dropdown menu
|
166 |
+
with col1:
|
167 |
+
selected_option = st.selectbox('Select a comparison', options)
|
168 |
+
with col2:
|
169 |
+
st.markdown("""</br>""",unsafe_allow_html=True)
|
170 |
+
if selected_option == "Month on Month" :
|
171 |
+
|
172 |
+
st.markdown(
|
173 |
+
f"""
|
174 |
+
<div style="padding: 5px; border-radius: 5px; background-color: #FFFFE0; width: fit-content; display: inline-block;">
|
175 |
+
<strong> Comparision of current month spends to previous month spends</strong>
|
176 |
+
</div>
|
177 |
+
""",
|
178 |
+
unsafe_allow_html=True
|
179 |
+
)
|
180 |
+
else :
|
181 |
+
st.markdown(
|
182 |
+
f"""
|
183 |
+
<div style="padding: 5px; border-radius: 5px; background-color: #FFFFE0; width: fit-content; display: inline-block;">
|
184 |
+
<strong> Comparision of current month spends to the same month in previous year</strong>
|
185 |
+
</div>
|
186 |
+
""",
|
187 |
+
unsafe_allow_html=True
|
188 |
+
)
|
189 |
+
# Waterfall chart
|
190 |
+
|
191 |
+
def get_month_year_list(start_date, end_date):
|
192 |
+
# Generate a range of dates from start_date to end_date with a monthly frequency
|
193 |
+
dates = pd.date_range(start=start_date, end=end_date, freq='MS') # 'MS' is month start frequency
|
194 |
+
|
195 |
+
# Extract month and year from each date and create a list of tuples
|
196 |
+
month_year_list = [(date.month, date.year) for date in dates]
|
197 |
+
|
198 |
+
return month_year_list
|
199 |
+
def get_start_end_dates(month, year):
|
200 |
+
start_date = datetime(year, month, 1).date()
|
201 |
+
|
202 |
+
if month == 12:
|
203 |
+
end_date = datetime(year + 1, 1, 1).date() - timedelta(days=1)
|
204 |
+
else:
|
205 |
+
end_date = datetime(year, month + 1, 1).date() - timedelta(days=1)
|
206 |
+
|
207 |
+
return start_date, end_date
|
208 |
+
|
209 |
+
month_year_list = get_month_year_list(start_date, end_date)
|
210 |
+
dropdown_options = [f"{date.strftime('%B %Y')}" for date in pd.date_range(start=start_date, end=end_date, freq='MS')]
|
211 |
+
waterfall_option = st.selectbox("Select a month:", dropdown_options)
|
212 |
+
waterfall_date = datetime.strptime(waterfall_option, "%B %Y")
|
213 |
+
waterfall_month = waterfall_date.month
|
214 |
+
waterfall_year = waterfall_date.year
|
215 |
+
waterfall_start_date, waterfall_end_date = get_start_end_dates(waterfall_month, waterfall_year)
|
216 |
+
# st.write("abc")
|
217 |
+
# figw = sf.waterfall(waterfall_start_date,waterfall_end_date)
|
218 |
+
figw= sf.waterfall(waterfall_start_date,waterfall_end_date,selected_option)
|
219 |
+
st.plotly_chart(figw,use_container_width=True)
|
220 |
+
|
221 |
+
elif data_selection_type == "Compare Custom Range":
|
222 |
+
col1, col2 = st.columns(2)
|
223 |
+
min_date,max_date = sf.get_date_range()
|
224 |
+
with col1:
|
225 |
+
st.write("Select Time Period 1")
|
226 |
+
sc1,sc2 = st.columns(2)
|
227 |
+
with sc1:
|
228 |
+
waterfall_start_date1 = st.date_input("Start Date 1: ",value=start_date,min_value=min_date,
|
229 |
+
max_value=max_date)
|
230 |
+
with sc2:
|
231 |
+
waterfall_end_date1 = st.date_input("End Date 1: ",value = end_date,min_value=min_date,
|
232 |
+
max_value=max_date)
|
233 |
+
with col2:
|
234 |
+
st.write("Select Time Period 2")
|
235 |
+
ec1,ec2 = st.columns(2)
|
236 |
+
with ec1:
|
237 |
+
waterfall_start_date2 = st.date_input("Start Date 2: ",value=end_date-timedelta(days = -1),min_value=min_date,
|
238 |
+
max_value=max_date)
|
239 |
+
with ec2:
|
240 |
+
diff = min((start_date-end_date).days,-30)
|
241 |
+
waterfall_end_date2 = st.date_input("End Date 2: ",value = start_date,min_value=min_date,
|
242 |
+
max_value=max_date)
|
243 |
+
try:
|
244 |
+
figw= sf.waterfall2(waterfall_start_date1,waterfall_end_date1,waterfall_start_date2,waterfall_end_date2)
|
245 |
+
st.plotly_chart(figw,use_container_width=True)
|
246 |
+
except:
|
247 |
+
st.warning("Previous data does not exist")
|
248 |
+
|
249 |
+
|
250 |
+
|
251 |
+
# Waterfall table
|
252 |
+
# shares_df = sf.shares_df_func(waterfall_start_date,waterfall_end_date)
|
253 |
+
st.table(sf.waterfall_table_func(shares_df).style.format("{:.0%}"))
|
254 |
+
|
255 |
+
|
256 |
+
with st.expander("View Decomposition Analysis"):
|
257 |
### Base decomp CHART
|
258 |
+
st.plotly_chart(sf.base_decomp(),use_container_width=True)
|
259 |
|
260 |
### Media decomp CHART
|
261 |
+
st.plotly_chart(sf.media_decomp(),use_container_width=True)
|
262 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Overview_data_test_panel@#prospects.xlsx
CHANGED
Binary files a/Overview_data_test_panel@#prospects.xlsx and b/Overview_data_test_panel@#prospects.xlsx differ
|
|
Streamlit_functions.py
CHANGED
@@ -254,7 +254,160 @@ def pie_contributions(start_date,end_date):
|
|
254 |
|
255 |
# Show the figure
|
256 |
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
def waterfall(start_date,end_date,btn_chart):
|
259 |
# if pd.isnull(start_date) == True :
|
260 |
# start_date = datetime(2024, 1, 28)
|
@@ -1314,7 +1467,7 @@ def scenario_spend_forecasting2(delta_df,start_date,end_date):
|
|
1314 |
return "Invalid month number"
|
1315 |
|
1316 |
data2["Month year"] = data2["Month"].apply(get_month_name) + ' ' +(data2["Date"].dt.year+1).astype(str)
|
1317 |
-
print(data2.columns)
|
1318 |
data2 = data2[['Month year' ,'BROADCAST TV', 'CABLE TV',
|
1319 |
'CONNECTED & OTT TV', 'VIDEO', 'DISPLAY PROSPECTING',
|
1320 |
'DISPLAY RETARGETING', 'SOCIAL PROSPECTING', 'SOCIAL RETARGETING',
|
|
|
254 |
|
255 |
# Show the figure
|
256 |
return fig
|
257 |
+
def waterfall2(start_date1,end_date1,start_date2,end_date2):
|
258 |
+
btn_chart = "Month on Month"
|
259 |
+
# if pd.isnull(start_date) == True :
|
260 |
+
# start_date = datetime(2024, 1, 28)
|
261 |
+
# if pd.isnull(end_date) == True :
|
262 |
+
# end_date = datetime(2024, 2, 24)
|
263 |
+
# start_date = datetime.strptime(start_date, "%Y-%m-%d")
|
264 |
+
# end_date = datetime.strptime(end_date, "%Y-%m-%d")
|
265 |
+
# start_date = start_date.datetime.data
|
266 |
+
# end_date = end_date.datetime.data
|
267 |
+
start_date1 = pd.to_datetime(start_date1)
|
268 |
+
end_date1 = pd.to_datetime(end_date1)
|
269 |
+
start_date2 = pd.to_datetime(start_date2)
|
270 |
+
end_date2 = pd.to_datetime(end_date2)
|
271 |
+
|
272 |
+
# if btn_chart == "Month on Month":
|
273 |
+
# start_date_prev = start_date +timedelta(weeks=-4)
|
274 |
+
# end_date_prev = start_date +timedelta(days=-1)
|
275 |
+
# else:
|
276 |
+
# start_date_prev = start_date +timedelta(weeks=-52)
|
277 |
+
# end_date_prev = start_date_prev +timedelta(weeks=4) +timedelta(days=-1)
|
278 |
+
|
279 |
+
|
280 |
+
if start_date1 < df['Date'].min() :
|
281 |
+
return "a"
|
282 |
+
|
283 |
+
cur_data = df[(df['Date'] >= start_date2) & (df['Date'] <= end_date2)]
|
284 |
+
prev_data = df[(df['Date'] >= start_date1) & (df['Date'] <= end_date1)]
|
285 |
+
|
286 |
+
|
287 |
+
# Example data for the waterfall chart
|
288 |
+
data = [
|
289 |
+
{'label': 'Previous Period', 'value': round(prev_data[contribution_cols].values.sum())},
|
290 |
+
{'label': 'Broadcast TV', 'value': round(cur_data['Broadcast TV_Prospects'].sum()-prev_data['Broadcast TV_Prospects'].sum())},
|
291 |
+
{'label': 'Cable TV', 'value': round(cur_data['Cable TV_Prospects'].sum()-prev_data['Cable TV_Prospects'].sum())},
|
292 |
+
{'label': 'Connected & OTT TV', 'value': round(cur_data['Connected & OTT TV_Prospects'].sum()-prev_data['Connected & OTT TV_Prospects'].sum())},
|
293 |
+
{'label': 'Video', 'value': round(cur_data['Video_Prospects'].sum()-prev_data['Video_Prospects'].sum())},
|
294 |
+
{'label': 'Display Prospecting', 'value': round(cur_data['Display Prospecting_Prospects'].sum()-prev_data['Display Prospecting_Prospects'].sum())},
|
295 |
+
{'label': 'Display Retargeting', 'value': round(cur_data['Display Retargeting_Prospects'].sum()-prev_data['Display Retargeting_Prospects'].sum())},
|
296 |
+
{'label': 'Social Prospecting', 'value': round(cur_data['Social Prospecting_Prospects'].sum()-prev_data['Social Prospecting_Prospects'].sum())},
|
297 |
+
{'label': 'Social Retargeting', 'value': round(cur_data['Social Retargeting_Prospects'].sum()-prev_data['Social Retargeting_Prospects'].sum())},
|
298 |
+
{'label': 'Search Brand', 'value': round(cur_data['Search Brand_Prospects'].sum()-prev_data['Search Brand_Prospects'].sum())},
|
299 |
+
{'label': 'Search Non-brand', 'value': round(cur_data['Search Non-brand_Prospects'].sum()-prev_data['Search Non-brand_Prospects'].sum())},
|
300 |
+
{'label': 'Digital Partners', 'value': round(cur_data['Digital Partners_Prospects'].sum()-prev_data['Digital Partners_Prospects'].sum())},
|
301 |
+
{'label': 'Audio', 'value': round(cur_data['Audio_Prospects'].sum()-prev_data['Audio_Prospects'].sum())},
|
302 |
+
{'label': 'Email', 'value': round(cur_data['Email_Prospects'].sum()-prev_data['Email_Prospects'].sum())},
|
303 |
+
{'label': 'Current Period', 'value': round(cur_data[contribution_cols].values.sum())}
|
304 |
+
]
|
305 |
+
|
306 |
+
# Calculate cumulative values for the waterfall chart
|
307 |
+
cumulative = [0]
|
308 |
+
for i in range(len(data)):
|
309 |
+
cumulative.append(cumulative[-1] + data[i]['value'])
|
310 |
+
|
311 |
+
# Adjusting values to start from zero for both first and last columns
|
312 |
+
cumulative[-1] = 0 # Set the last cumulative value to zero
|
313 |
+
|
314 |
+
# Extracting labels and values
|
315 |
+
labels = [item['label'] for item in data]
|
316 |
+
values = [item['value'] for item in data]
|
317 |
+
|
318 |
+
# Plotting the waterfall chart using go.Bar
|
319 |
+
bars = []
|
320 |
+
for i in range(len(data)):
|
321 |
+
color = '#4A88D9' if i == 0 or i == len(data) - 1 else '#DC5537' # Blue for first and last, gray for others
|
322 |
+
hover_text = f"<b>{labels[i]}</b><br>Value: {abs(values[i])}"
|
323 |
+
|
324 |
+
bars.append(go.Bar(
|
325 |
+
x=[labels[i]],
|
326 |
+
y=[cumulative[i+1] - cumulative[i]],
|
327 |
+
base=[cumulative[i]],
|
328 |
+
text=[f"{abs(values[i]):,}"],
|
329 |
+
textposition='auto',
|
330 |
+
hovertemplate=hover_text,
|
331 |
+
marker=dict(color=color),
|
332 |
+
showlegend=False
|
333 |
+
))
|
334 |
|
335 |
+
# Creating the figure
|
336 |
+
fig = go.Figure(data=bars)
|
337 |
+
|
338 |
+
# Updating layout for black background and gray gridlines
|
339 |
+
if btn_chart == "Month on Month":
|
340 |
+
fig.update_layout(
|
341 |
+
title=f"Change In MMM Estimated Prospect Contribution"
|
342 |
+
,showlegend=False,
|
343 |
+
# plot_bgcolor='black',
|
344 |
+
# paper_bgcolor='black',
|
345 |
+
# font=dict(color='white'), # Changing font color to white for better contrast
|
346 |
+
xaxis=dict(
|
347 |
+
showgrid=False,
|
348 |
+
zeroline=False, # Hiding the x-axis zero line
|
349 |
+
),
|
350 |
+
yaxis=dict(
|
351 |
+
title="Prospects",
|
352 |
+
showgrid=True,
|
353 |
+
gridcolor='lightgray',
|
354 |
+
griddash='dot', # Setting y-axis gridline color to gray
|
355 |
+
zeroline=False, # Hiding the y-axis zero line
|
356 |
+
# range=[18000, max(max(cumulative), max(values)) + 1000] # Setting the y-axis range from 19k to slightly above the maximum value
|
357 |
+
)
|
358 |
+
)
|
359 |
+
fig.add_annotation(
|
360 |
+
text=f"{start_date2.strftime('%m-%d-%Y')} to {end_date2.strftime('%m-%d-%Y')} vs. {start_date1.strftime('%m-%d-%Y')} To {end_date1.strftime('%m-%d-%Y')}",
|
361 |
+
x=0,
|
362 |
+
y=1.15,
|
363 |
+
xref="x domain",
|
364 |
+
yref="y domain",
|
365 |
+
showarrow=False,
|
366 |
+
font=dict(size=16),
|
367 |
+
# align='left'
|
368 |
+
)
|
369 |
+
# fig.update_xaxes(
|
370 |
+
# tickmode="array",
|
371 |
+
# # categoryorder="total ascending",
|
372 |
+
# tickvals=[f"{abs(values[i])}"],
|
373 |
+
# ticktext=[f"{abs(values[i])}"],
|
374 |
+
# ticklabelposition="outside",
|
375 |
+
# tickfont=dict(color="white"),
|
376 |
+
# )
|
377 |
+
else :
|
378 |
+
fig.update_layout(
|
379 |
+
showlegend=False,
|
380 |
+
# plot_bgcolor='black',
|
381 |
+
# paper_bgcolor='black',
|
382 |
+
# font=dict(color='white'), # Changing font color to white for better contrast
|
383 |
+
xaxis=dict(
|
384 |
+
showgrid=False,
|
385 |
+
zeroline=False, # Hiding the x-axis zero line
|
386 |
+
),
|
387 |
+
yaxis=dict(
|
388 |
+
title="Prospects",
|
389 |
+
showgrid=True,
|
390 |
+
gridcolor='lightgray',
|
391 |
+
griddash='dot', # Setting y-axis gridline color to gray
|
392 |
+
zeroline=False, # Hiding the y-axis zero line
|
393 |
+
# range=[10000, max(cumulative)+1000] # Setting the y-axis range from 19k to slightly above the maximum value
|
394 |
+
)
|
395 |
+
|
396 |
+
)
|
397 |
+
fig.add_annotation(
|
398 |
+
text=f"{start_date_prev.strftime('%m-%d-%Y')} to {end_date_prev.strftime('%m-%d-%Y')} vs. {start_date.strftime('%m-%d-%Y')} To {end_date.strftime('%m-%d-%Y')}",
|
399 |
+
x=0,
|
400 |
+
y=1.15,
|
401 |
+
xref="x domain",
|
402 |
+
yref="y domain",
|
403 |
+
showarrow=False,
|
404 |
+
font=dict(size=16),
|
405 |
+
# align='left'
|
406 |
+
)
|
407 |
+
# # print(cur_data)
|
408 |
+
# # print(prev_data)
|
409 |
+
# fig.show()
|
410 |
+
return fig
|
411 |
def waterfall(start_date,end_date,btn_chart):
|
412 |
# if pd.isnull(start_date) == True :
|
413 |
# start_date = datetime(2024, 1, 28)
|
|
|
1467 |
return "Invalid month number"
|
1468 |
|
1469 |
data2["Month year"] = data2["Month"].apply(get_month_name) + ' ' +(data2["Date"].dt.year+1).astype(str)
|
1470 |
+
# print(data2.columns)
|
1471 |
data2 = data2[['Month year' ,'BROADCAST TV', 'CABLE TV',
|
1472 |
'CONNECTED & OTT TV', 'VIDEO', 'DISPLAY PROSPECTING',
|
1473 |
'DISPLAY RETARGETING', 'SOCIAL PROSPECTING', 'SOCIAL RETARGETING',
|
__pycache__/Streamlit_functions.cpython-310.pyc
CHANGED
Binary files a/__pycache__/Streamlit_functions.cpython-310.pyc and b/__pycache__/Streamlit_functions.cpython-310.pyc differ
|
|
__pycache__/classes.cpython-310.pyc
CHANGED
Binary files a/__pycache__/classes.cpython-310.pyc and b/__pycache__/classes.cpython-310.pyc differ
|
|
__pycache__/utilities.cpython-310.pyc
CHANGED
Binary files a/__pycache__/utilities.cpython-310.pyc and b/__pycache__/utilities.cpython-310.pyc differ
|
|
classes.py
CHANGED
@@ -100,7 +100,7 @@ class Channel:
|
|
100 |
self.modified_total_sales = self.modified_sales.sum()
|
101 |
self.delta_spends = self.modified_total_spends - self.actual_total_spends
|
102 |
self.delta_sales = self.modified_total_sales - self.actual_total_sales
|
103 |
-
|
104 |
def update_penalty(self, penalty):
|
105 |
self.penalty = penalty
|
106 |
|
@@ -111,29 +111,39 @@ class Channel:
|
|
111 |
self.modified_spends = (
|
112 |
self.modified_spends * total_spends / self.modified_spends.sum()
|
113 |
)
|
|
|
|
|
114 |
|
115 |
def calculate_sales(self):
|
116 |
-
|
|
|
117 |
return self.response_curve(self.modified_spends)
|
118 |
|
119 |
def hill_equation(x, Kd, n):
|
120 |
return x**n / (Kd**n + x**n)
|
121 |
def response_curve(self, x):
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
|
|
|
|
128 |
if self.response_curve_type == "hill-eq":
|
129 |
# dividing_parameter = check_dividing_parameter()
|
130 |
-
#
|
131 |
-
# # print(self.name)
|
|
|
|
|
|
|
132 |
if len(x) == 1:
|
133 |
-
dividing_rate =
|
|
|
134 |
# x = np.sum(x)
|
135 |
else:
|
136 |
dividing_rate = 1
|
|
|
137 |
# x = np.sum(x)
|
138 |
# dividing_rate = 104
|
139 |
Kd= self.response_curve_params["Kd"]
|
@@ -160,6 +170,9 @@ class Channel:
|
|
160 |
# # print(sales)
|
161 |
# # print(np.sum(sales))
|
162 |
# # print("sales",sales)
|
|
|
|
|
|
|
163 |
if self.response_curve_type == "s-curve":
|
164 |
if self.power >= 0:
|
165 |
x = x / 10**self.power
|
@@ -260,7 +273,7 @@ class Scenario:
|
|
260 |
def calculate_modified_total_spends(self):
|
261 |
total_actual_spends = 0.0
|
262 |
for channel in self.channels.values():
|
263 |
-
total_actual_spends += channel.actual_total_spends *
|
264 |
return total_actual_spends
|
265 |
|
266 |
def calculate_modified_total_spends(self):
|
@@ -269,13 +282,14 @@ class Scenario:
|
|
269 |
# import streamlit as st
|
270 |
# st.write(channel.modified_total_spends )
|
271 |
total_modified_spends += (
|
272 |
-
channel.modified_total_spends *
|
|
|
273 |
)
|
274 |
return total_modified_spends
|
275 |
|
276 |
def calculate_actual_total_sales(self):
|
277 |
total_actual_sales = 0#self.constant.sum() + self.correction.sum()
|
278 |
-
print("a")
|
279 |
for channel in self.channels.values():
|
280 |
total_actual_sales += channel.actual_total_sales
|
281 |
# # print(channel.actual_total_sales)
|
@@ -343,16 +357,7 @@ class Scenario:
|
|
343 |
|
344 |
# return zip(channels_list, res.x)
|
345 |
|
346 |
-
|
347 |
-
return x**n / (Kd**n + x**n)
|
348 |
-
|
349 |
-
def cost_func(channel,x):
|
350 |
-
x_inp = (x/104 - param_dicts["x_min"][channel]) / (param_dicts["x_max"][channel] - param_dicts["x_min"][channel])
|
351 |
-
# # print(x_inp)
|
352 |
-
x_out = hill_equation(x_inp, param_dicts["Kd"][channel], param_dicts["n"][channel])
|
353 |
-
# # print(x_out)
|
354 |
-
#
|
355 |
-
return (param_dicts["y_max"][channel] - param_dicts["y_min"][channel])*(x_out + param_dicts["y_min"][channel])*104
|
356 |
|
357 |
|
358 |
|
@@ -393,10 +398,10 @@ class Scenario:
|
|
393 |
|
394 |
for channel_name, modified_spends in zip(channels_list, res.x):
|
395 |
self.update(channel_name, modified_spends)
|
396 |
-
|
397 |
return zip(channels_list, res.x)
|
398 |
|
399 |
-
|
400 |
|
401 |
def optimize(self, spends_percent, channels_list):
|
402 |
# channels_list = self.channels.keys()
|
@@ -406,17 +411,18 @@ class Scenario:
|
|
406 |
for channel_name in channels_list:
|
407 |
# spends_constraint += self.channels[channel_name].modified_total_spends
|
408 |
spends_constant.append(self.channels[channel_name].conversion_rate)
|
|
|
409 |
spends_constraint += (
|
410 |
self.channels[channel_name].actual_total_spends
|
411 |
-
*
|
412 |
)
|
413 |
spends_constraint = spends_constraint * (1 + spends_percent / 100)
|
414 |
-
|
415 |
-
constraint = LinearConstraint(
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
)
|
420 |
bounds = []
|
421 |
old_spends = []
|
422 |
for channel_name in channels_list:
|
@@ -426,14 +432,41 @@ class Scenario:
|
|
426 |
(1 + spends_percent / 100)
|
427 |
)
|
428 |
old_spends.append(channel_actual_total_spends)
|
429 |
-
bounds.append((1+ channel_bounds / 100) * channel_actual_total_spends)
|
430 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
431 |
def objective_function(x):
|
|
|
432 |
for channel_name, modified_spends in zip(channels_list, x):
|
433 |
-
|
|
|
434 |
return -1 * self.modified_total_sales
|
435 |
|
436 |
-
|
437 |
# # print("$"*100)
|
438 |
res = minimize(
|
439 |
lambda x: objective_function(x) / 1e3,
|
@@ -441,7 +474,7 @@ class Scenario:
|
|
441 |
x0=old_spends,
|
442 |
constraints=constraint,
|
443 |
bounds=bounds,
|
444 |
-
options={"maxiter": int(1e7), "xtol":
|
445 |
)
|
446 |
# res = dual_annealing(
|
447 |
# objective_function,
|
@@ -454,6 +487,7 @@ class Scenario:
|
|
454 |
# # print(res)
|
455 |
for channel_name, modified_spends in zip(channels_list, res.x):
|
456 |
self.update(channel_name, modified_spends)
|
|
|
457 |
|
458 |
return zip(channels_list, res.x)
|
459 |
|
@@ -461,18 +495,7 @@ class Scenario:
|
|
461 |
def hill_equation(self,x, Kd, n):
|
462 |
return x**n / (Kd**n + x**n)
|
463 |
|
464 |
-
|
465 |
-
|
466 |
-
response_curve_params = pd.read_excel("response_curves_parameters.xlsx",index_col = "channel")
|
467 |
-
param_dicts = {col: response_curve_params[col].to_dict() for col in response_curve_params.columns}
|
468 |
-
|
469 |
-
x_inp = (x/104 - param_dicts["x_min"][channel]) / (param_dicts["x_max"][channel] - param_dicts["x_min"][channel])
|
470 |
-
# # print(x_inp)
|
471 |
-
x_out = self.hill_equation(x_inp, param_dicts["Kd"][channel], param_dicts["n"][channel])
|
472 |
-
# # print(x_out)
|
473 |
-
#
|
474 |
-
return (param_dicts["y_max"][channel] - param_dicts["y_min"][channel])*(x_out + param_dicts["y_min"][channel])*104
|
475 |
-
|
476 |
|
477 |
# def spends_optimisation(self, spends_percent,channels_list):
|
478 |
# m = GEKKO(remote=False)
|
|
|
100 |
self.modified_total_sales = self.modified_sales.sum()
|
101 |
self.delta_spends = self.modified_total_spends - self.actual_total_spends
|
102 |
self.delta_sales = self.modified_total_sales - self.actual_total_sales
|
103 |
+
# print(self.actual_total_spends)
|
104 |
def update_penalty(self, penalty):
|
105 |
self.penalty = penalty
|
106 |
|
|
|
111 |
self.modified_spends = (
|
112 |
self.modified_spends * total_spends / self.modified_spends.sum()
|
113 |
)
|
114 |
+
print("modified spends")
|
115 |
+
print(self.modified_spends)
|
116 |
|
117 |
def calculate_sales(self):
|
118 |
+
print("in calc_sales")
|
119 |
+
print(self.modified_spends)
|
120 |
return self.response_curve(self.modified_spends)
|
121 |
|
122 |
def hill_equation(x, Kd, n):
|
123 |
return x**n / (Kd**n + x**n)
|
124 |
def response_curve(self, x):
|
125 |
+
print(x)
|
126 |
+
# if self.penalty:
|
127 |
+
# print("in penalty")
|
128 |
+
# x = np.where(
|
129 |
+
# x < self.upper_limit,
|
130 |
+
# x,
|
131 |
+
# self.upper_limit + (x - self.upper_limit) * self.upper_limit / x,
|
132 |
+
# )
|
133 |
if self.response_curve_type == "hill-eq":
|
134 |
# dividing_parameter = check_dividing_parameter()
|
135 |
+
# print("lalala")
|
136 |
+
# # print(self.name)\
|
137 |
+
# print(len(x))
|
138 |
+
print("in response curve function")
|
139 |
+
print(x)
|
140 |
if len(x) == 1:
|
141 |
+
dividing_rate = self.response_curve_params["num_pos_obsv"]
|
142 |
+
# print(dividing_rate)
|
143 |
# x = np.sum(x)
|
144 |
else:
|
145 |
dividing_rate = 1
|
146 |
+
# dividing_rate = self.response_curve_params["num_pos_obsv"]
|
147 |
# x = np.sum(x)
|
148 |
# dividing_rate = 104
|
149 |
Kd= self.response_curve_params["Kd"]
|
|
|
170 |
# # print(sales)
|
171 |
# # print(np.sum(sales))
|
172 |
# # print("sales",sales)
|
173 |
+
print("aa")
|
174 |
+
print(sales)
|
175 |
+
print("aa1")
|
176 |
if self.response_curve_type == "s-curve":
|
177 |
if self.power >= 0:
|
178 |
x = x / 10**self.power
|
|
|
273 |
def calculate_modified_total_spends(self):
|
274 |
total_actual_spends = 0.0
|
275 |
for channel in self.channels.values():
|
276 |
+
total_actual_spends += channel.actual_total_spends * 1.0
|
277 |
return total_actual_spends
|
278 |
|
279 |
def calculate_modified_total_spends(self):
|
|
|
282 |
# import streamlit as st
|
283 |
# st.write(channel.modified_total_spends )
|
284 |
total_modified_spends += (
|
285 |
+
channel.modified_total_spends * 1.0
|
286 |
+
|
287 |
)
|
288 |
return total_modified_spends
|
289 |
|
290 |
def calculate_actual_total_sales(self):
|
291 |
total_actual_sales = 0#self.constant.sum() + self.correction.sum()
|
292 |
+
# print("a")
|
293 |
for channel in self.channels.values():
|
294 |
total_actual_sales += channel.actual_total_sales
|
295 |
# # print(channel.actual_total_sales)
|
|
|
357 |
|
358 |
# return zip(channels_list, res.x)
|
359 |
|
360 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
361 |
|
362 |
|
363 |
|
|
|
398 |
|
399 |
for channel_name, modified_spends in zip(channels_list, res.x):
|
400 |
self.update(channel_name, modified_spends)
|
401 |
+
|
402 |
return zip(channels_list, res.x)
|
403 |
|
404 |
+
|
405 |
|
406 |
def optimize(self, spends_percent, channels_list):
|
407 |
# channels_list = self.channels.keys()
|
|
|
411 |
for channel_name in channels_list:
|
412 |
# spends_constraint += self.channels[channel_name].modified_total_spends
|
413 |
spends_constant.append(self.channels[channel_name].conversion_rate)
|
414 |
+
# print(spends_constant)
|
415 |
spends_constraint += (
|
416 |
self.channels[channel_name].actual_total_spends
|
417 |
+
* 1.0
|
418 |
)
|
419 |
spends_constraint = spends_constraint * (1 + spends_percent / 100)
|
420 |
+
constraint= LinearConstraint(np.ones((num_channels,)), lb = spends_constraint, ub = spends_constraint)
|
421 |
+
# constraint = LinearConstraint(
|
422 |
+
# np.array(spends_constant),
|
423 |
+
# lb=spends_constraint,
|
424 |
+
# ub=spends_constraint,
|
425 |
+
# )
|
426 |
bounds = []
|
427 |
old_spends = []
|
428 |
for channel_name in channels_list:
|
|
|
432 |
(1 + spends_percent / 100)
|
433 |
)
|
434 |
old_spends.append(channel_actual_total_spends)
|
435 |
+
# bounds.append((1+ channel_bounds / 100) * channel_actual_total_spends)
|
436 |
+
lb = (1- _channel_class.channel_bounds_min / 100) * channel_actual_total_spends
|
437 |
+
ub = (1+ _channel_class.channel_bounds_max / 100) * channel_actual_total_spends
|
438 |
+
bounds.append((lb,ub))
|
439 |
+
# _channel_class.channel_bounds_min
|
440 |
+
# _channel_class.channel_bounds_max
|
441 |
+
def cost_func1(channel,x):
|
442 |
+
response_curve_params = pd.read_excel("response_curves_parameters.xlsx",index_col = "channel")
|
443 |
+
param_dicts = {col: response_curve_params[col].to_dict() for col in response_curve_params.columns}
|
444 |
+
|
445 |
+
Kd= param_dicts["Kd"][channel]
|
446 |
+
n= param_dicts["n"][channel]
|
447 |
+
x_min= param_dicts["x_min"][channel]
|
448 |
+
x_max= param_dicts["x_max"][channel]
|
449 |
+
y_min= param_dicts["y_min"][channel]
|
450 |
+
y_max= param_dicts['y_max'][channel]
|
451 |
+
division_parameter = param_dicts['num_pos_obsv'][channel]
|
452 |
+
x_inp = ( x/division_parameter- x_min) / (x_max - x_min)
|
453 |
+
# print(x_inp)
|
454 |
+
x_out = x_inp**n / (Kd**n + x_inp**n)
|
455 |
+
x_val_inv = (x_out*x_max + (1 - x_out) * x_min)
|
456 |
+
sales = (x_val_inv*y_min/y_max)*division_parameter
|
457 |
+
if np.isnan(sales):
|
458 |
+
# print(sales,channel)
|
459 |
+
sales = 0
|
460 |
+
# print(sales,channel)
|
461 |
+
return sales
|
462 |
def objective_function(x):
|
463 |
+
modified_total_sales = 0
|
464 |
for channel_name, modified_spends in zip(channels_list, x):
|
465 |
+
modified_total_sales = modified_total_sales + cost_func1(channel_name,modified_spends)
|
466 |
+
# self.update(channel_name, modified_spends)
|
467 |
return -1 * self.modified_total_sales
|
468 |
|
469 |
+
print(bounds)
|
470 |
# # print("$"*100)
|
471 |
res = minimize(
|
472 |
lambda x: objective_function(x) / 1e3,
|
|
|
474 |
x0=old_spends,
|
475 |
constraints=constraint,
|
476 |
bounds=bounds,
|
477 |
+
options={"maxiter": int(1e7), "xtol": 0.1},
|
478 |
)
|
479 |
# res = dual_annealing(
|
480 |
# objective_function,
|
|
|
487 |
# # print(res)
|
488 |
for channel_name, modified_spends in zip(channels_list, res.x):
|
489 |
self.update(channel_name, modified_spends)
|
490 |
+
print(channel_name, modified_spends,cost_func1(channel_name, modified_spends))
|
491 |
|
492 |
return zip(channels_list, res.x)
|
493 |
|
|
|
495 |
def hill_equation(self,x, Kd, n):
|
496 |
return x**n / (Kd**n + x**n)
|
497 |
|
498 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
499 |
|
500 |
# def spends_optimisation(self, spends_percent,channels_list):
|
501 |
# m = GEKKO(remote=False)
|
pages/1_Model_Quality.py
CHANGED
@@ -16,7 +16,7 @@ st.plotly_chart(sf.mmm_model_quality(),use_container_width=True)
|
|
16 |
|
17 |
media_df = sf.media_data()
|
18 |
# Create two columns for start date and end date input
|
19 |
-
col1, col2 , col3 = st.columns([1,
|
20 |
df1 = sf.model_metrics_table_func()
|
21 |
st.dataframe(df1,hide_index = True,use_container_width=True)
|
22 |
|
|
|
16 |
|
17 |
media_df = sf.media_data()
|
18 |
# Create two columns for start date and end date input
|
19 |
+
col1, col2 , col3 = st.columns([1,0.2,1])
|
20 |
df1 = sf.model_metrics_table_func()
|
21 |
st.dataframe(df1,hide_index = True,use_container_width=True)
|
22 |
|
pages/2_Scenario_Planner.py
CHANGED
@@ -98,6 +98,9 @@ def optimize(key, status_placeholder):
|
|
98 |
# result = st.session_state["scenario"].spends_optimisation(
|
99 |
# st.session_state["total_spends_change"], channel_list
|
100 |
)
|
|
|
|
|
|
|
101 |
|
102 |
|
103 |
# elif key.lower() == "revenue":
|
@@ -111,7 +114,7 @@ def optimize(key, status_placeholder):
|
|
111 |
for channel_name, modified_spends in result:
|
112 |
|
113 |
st.session_state[channel_name] = numerize(
|
114 |
-
modified_spends *
|
115 |
1,
|
116 |
)
|
117 |
prev_spends = (
|
|
|
98 |
# result = st.session_state["scenario"].spends_optimisation(
|
99 |
# st.session_state["total_spends_change"], channel_list
|
100 |
)
|
101 |
+
print("")
|
102 |
+
print(list(zip(*result)))
|
103 |
+
|
104 |
|
105 |
|
106 |
# elif key.lower() == "revenue":
|
|
|
114 |
for channel_name, modified_spends in result:
|
115 |
|
116 |
st.session_state[channel_name] = numerize(
|
117 |
+
modified_spends * 1.0,
|
118 |
1,
|
119 |
)
|
120 |
prev_spends = (
|
pages/5_Glossary.py
CHANGED
@@ -5,29 +5,32 @@ import streamlit as st
|
|
5 |
# )
|
6 |
|
7 |
def glossary_run():
|
8 |
-
st.
|
9 |
-
st.
|
|
|
|
|
10 |
|
11 |
-
|
12 |
|
13 |
-
|
14 |
|
15 |
-
|
16 |
|
17 |
-
|
18 |
|
19 |
-
|
20 |
|
21 |
-
|
22 |
|
23 |
-
|
24 |
|
25 |
-
|
26 |
|
27 |
-
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
st.write("**• Estimated CPC \(Cost Per Click\):** This is an estimation of the cost for each time someone clicks on its advertisement via that media channel. The default values are generated from historical averages.")
|
32 |
|
|
|
|
|
|
|
33 |
glossary_run()
|
|
|
5 |
# )
|
6 |
|
7 |
def glossary_run():
|
8 |
+
st.header("Glossary")
|
9 |
+
with st.expander("Model MMM Terminology"):
|
10 |
+
st.subheader("Glossary of MMM Terminology")
|
11 |
+
st.write("**• Model R-squared \(R\)\:** This is a statistical measure used to determine the percentage of variation in the dependent variable that the independent variables explain collectively. It ranges between 0 and 1, where 1 indicates a perfect fit and 0 indicates no linear relationship. An R2 greater than 0.8 usually indicates a great model fit.")
|
12 |
|
13 |
+
st.write("**• Mean Absolute Percentage Error \(MAPE\):** This is a measure used to determine the accuracy of a predictive model. It calculates the average absolute percentage difference between the actual and predicted values, expressing the result as a percentage to provide a sense of scale for the error.")
|
14 |
|
15 |
+
st.write("**• Media & Baseline Elasticity:** It refers to the percentage change in the number of prospects in response to a percentage change in a marketing input \(media channel spends\) or a baseline factor \(like seasonality. macro factors, competitors spending, etc.\). It is a measure of the responsiveness of the number of prospects to changes in the marketing input or the baseline factor")
|
16 |
|
17 |
+
st.write("**• Media Half-Life:** This represents the time it takes for a media spend's impact to reduce to half of its initial impact. It is a key aspect of media decay rates, which represent how the effect of advertising diminishes over time \(in weeks\). This term refers to a curve that illustrates the relationship between media spend and the resulting number of prospects.")
|
18 |
|
19 |
+
st.write("**• Support:** Equivalent to Impression or Click depending on the media channel.")
|
20 |
|
21 |
+
st.write("**• Contribution Share:** Unit is %. It refers to the percentage contribution of a specific marketing channel to the number of prospects. It is calculated by dividing the contribution from a particular channel by the total number of prospects from all media channels \(not including base contributions\).")
|
22 |
|
23 |
+
st.write("**• Spend Share:** Unit is %. It refers to the percentage of the total marketing budget that is allocated to a specific marketing channel. It is calculated by dividing the amount spent on a particular channel by the total marketing spend")
|
24 |
|
25 |
+
st.write("**• Support Share:** Unit is %. It refers to the percentage of the total media impression that is allocated to a specific marketing channel. It is calculated by dividing support on a particular channel by the total marketing spend")
|
26 |
|
27 |
+
st.write("**• Efficiency Index:** it is a metric that measures the cost-effectiveness of a campaign. It is calculated by dividing Contribution Share by Spend Share. An efficiency index above 1 suggests that a channel is more cost-effective than the benchmark, while an efficiency index below 1 suggests it is less cost-effective. The higher the efficiency index, the more cost-effective its channel is")
|
28 |
|
29 |
+
st.write("**• Effectiveness Index:** It is a metric that measures how well a particular marketing channel is performing relative to its support/impression. It is calculated by dividing the Contribution Share by the Spend Share for each channel")
|
30 |
|
31 |
+
st.write("**• Estimated CPM \(Cost Per Thousand Impressions\):** This is an estimation of the cost for every thousand impressions \(or views\) of its advertisement via that media channel. The default values are generated from historical averages.")
|
|
|
|
|
32 |
|
33 |
+
st.write("**• Estimated CPC \(Cost Per Click\):** This is an estimation of the cost for each time someone clicks on its advertisement via that media channel. The default values are generated from historical averages.")
|
34 |
+
with st.expander("Deployment Plan"):
|
35 |
+
st.image(r"C:\Users\PragyaJatav\Downloads\image (2).png")
|
36 |
glossary_run()
|
response_curves_parameters.xlsx
CHANGED
Binary files a/response_curves_parameters.xlsx and b/response_curves_parameters.xlsx differ
|
|
summary_df.pkl
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
size 1822
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:02f8f23713eadad9c91bae3af6649f9f2e7e2aa8d73fb28986bd48903cb74f38
|
3 |
size 1822
|
utilities.py
CHANGED
@@ -335,7 +335,7 @@ def initialize_data(
|
|
335 |
spends=spends,
|
336 |
sales= y.copy(),
|
337 |
# conversion_rate = np.mean(list(conv_rates[inp_col].values())),
|
338 |
-
conversion_rate=conv_rates[inp_col],
|
339 |
response_curve_type="hill-eq",
|
340 |
response_curve_params={
|
341 |
"Kd": param_dicts["Kd"][inp_col],
|
@@ -343,7 +343,8 @@ def initialize_data(
|
|
343 |
"x_min": param_dicts["x_min"][inp_col],
|
344 |
"x_max": param_dicts["x_max"][inp_col],
|
345 |
"y_min": param_dicts["y_min"][inp_col],
|
346 |
-
"y_max": param_dicts["y_max"][inp_col]
|
|
|
347 |
},
|
348 |
bounds=np.array([-10, 10]),
|
349 |
channel_bounds_min = 10,
|
@@ -387,7 +388,7 @@ def initialize_data(
|
|
387 |
|
388 |
for channel in channels.values():
|
389 |
st.session_state[channel.name] = numerize(
|
390 |
-
channel.actual_total_spends *
|
391 |
)
|
392 |
|
393 |
st.session_state["xlsx_buffer"] = io.BytesIO()
|
|
|
335 |
spends=spends,
|
336 |
sales= y.copy(),
|
337 |
# conversion_rate = np.mean(list(conv_rates[inp_col].values())),
|
338 |
+
conversion_rate=1.0 ,#conv_rates[inp_col],
|
339 |
response_curve_type="hill-eq",
|
340 |
response_curve_params={
|
341 |
"Kd": param_dicts["Kd"][inp_col],
|
|
|
343 |
"x_min": param_dicts["x_min"][inp_col],
|
344 |
"x_max": param_dicts["x_max"][inp_col],
|
345 |
"y_min": param_dicts["y_min"][inp_col],
|
346 |
+
"y_max": param_dicts["y_max"][inp_col],
|
347 |
+
"num_pos_obsv":param_dicts["num_pos_obsv"][inp_col]
|
348 |
},
|
349 |
bounds=np.array([-10, 10]),
|
350 |
channel_bounds_min = 10,
|
|
|
388 |
|
389 |
for channel in channels.values():
|
390 |
st.session_state[channel.name] = numerize(
|
391 |
+
channel.actual_total_spends * 1.0, 1
|
392 |
)
|
393 |
|
394 |
st.session_state["xlsx_buffer"] = io.BytesIO()
|