AjaykumarPilla commited on
Commit
2d585ca
Β·
verified Β·
1 Parent(s): 5ddefb1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -77
app.py CHANGED
@@ -2,7 +2,6 @@ import streamlit as st
2
  import pandas as pd
3
  from prophet import Prophet
4
  from datetime import datetime, timedelta
5
- import numpy as np
6
  import plotly.graph_objects as go
7
 
8
  # Prepare data for Prophet
@@ -11,130 +10,124 @@ def prepare_prophet_data(usage_series):
11
  start_date = end_date - timedelta(days=len(usage_series) - 1)
12
  dates = [start_date + timedelta(days=i) for i in range(len(usage_series))]
13
 
14
- prophet_df = pd.DataFrame({
15
- 'ds': dates,
16
- 'y': usage_series
17
- })
18
- return prophet_df
19
-
20
- # Train model with sensitivity option
21
- def train_model_with_usage(usage_series, sensitivity):
22
- prophet_df = prepare_prophet_data(usage_series)
23
-
24
- # Set changepoint_prior_scale based on selected sensitivity
25
- if sensitivity == "Low":
26
- cps = 0.01
27
- elif sensitivity == "Medium":
28
- cps = 0.1
29
- else: # High
30
- cps = 0.5
31
-
32
  model = Prophet(
33
  yearly_seasonality=False,
34
  weekly_seasonality=True,
35
  daily_seasonality=True,
36
- changepoint_prior_scale=cps,
37
  changepoint_range=0.6
38
  )
39
- model.fit(prophet_df)
40
  return model
41
 
42
- # Function to make forecasts
43
  def make_forecast(model, periods):
44
  future = model.make_future_dataframe(periods=periods)
45
  forecast = model.predict(future)
46
  return forecast, round(forecast['yhat'].tail(periods).sum())
47
 
48
- # Function to validate input
49
  def validate_usage_series(usage_str):
50
  try:
51
- usage_list = [float(x) for x in usage_str.split(',')]
52
- if len(usage_list) != 60:
53
- return None, "Usage series must contain exactly 60 values."
54
- if any(x < 0 for x in usage_list):
55
  return None, "Usage values must be non-negative."
56
- return usage_list, None
57
  except:
58
- return None, "Invalid usage series format. Please enter 60 comma-separated numbers."
59
 
60
- # Main Streamlit app
61
  def main():
62
  st.title("SmartLab Consumables Forecast")
63
 
64
  st.header("Input Parameters")
65
  consumable_type = st.selectbox("Consumable Type", ['Filters', 'Reagents', 'Vials'])
66
- usage_series = st.text_input("Last 60 Days Usage (comma-separated)", "")
67
  current_stock = st.number_input("Current Stock", min_value=0, value=0)
68
- sensitivity = st.selectbox("Forecast Sensitivity", ['Low', 'Medium', 'High'])
69
 
70
  if st.button("Generate Forecast"):
71
- usage_list, error = validate_usage_series(usage_series)
72
  if error:
73
  st.error(error)
74
  return
75
 
76
- try:
77
- model = train_model_with_usage(usage_list, sensitivity)
78
- except Exception as e:
79
- st.error(f"Error training model: {str(e)}")
80
- return
81
-
82
  forecast_df, forecast_7 = make_forecast(model, 7)
83
  _, forecast_14 = make_forecast(model, 14)
84
  _, forecast_30 = make_forecast(model, 30)
85
 
86
  st.header("Forecast Results")
87
- st.write(f"**7-Day Forecast**: {forecast_7} units")
88
- st.write(f"**14-Day Forecast**: {forecast_14} units")
89
- st.write(f"**30-Day Forecast**: {forecast_30} units")
90
-
91
- st.header("Threshold Alerts")
92
- if current_stock < forecast_7:
93
- st.warning(f"⚠️ Stock ({current_stock}) is below 7-day forecast ({forecast_7})")
94
- if current_stock < forecast_14:
95
- st.warning(f"⚠️ Stock ({current_stock}) is below 14-day forecast ({forecast_14})")
96
- if current_stock < forecast_30:
97
- st.warning(f"⚠️ Stock ({current_stock}) is below 30-day forecast ({forecast_30})")
98
-
99
- st.header("Order Suggestions")
100
- st.write(f"πŸ›’ **Order for 7 Days**: {max(0, forecast_7 - current_stock)} units")
101
- st.write(f"πŸ›’ **Order for 14 Days**: {max(0, forecast_14 - current_stock)} units")
102
- st.write(f"πŸ›’ **Order for 30 Days**: {max(0, forecast_30 - current_stock)} units")
103
-
104
- # Forecast Trend Visualization
105
- st.header("Forecast Curve")
 
 
 
 
106
  fig_curve = go.Figure()
107
  fig_curve.add_trace(go.Scatter(
108
  x=forecast_df['ds'],
109
  y=forecast_df['yhat'],
110
  mode='lines',
111
- name='Forecasted Usage',
112
- line=dict(color='royalblue')
113
  ))
114
  fig_curve.update_layout(
115
- title='Forecast Trend (yhat)',
116
- xaxis_title='Date',
117
- yaxis_title='Predicted Usage',
118
- template='plotly_white'
119
  )
120
  st.plotly_chart(fig_curve)
121
 
122
- # Summary Bar Chart
123
- st.header("Forecast Summary")
124
- bar_data = pd.DataFrame({
125
- 'Period': ['7 Days', '14 Days', '30 Days'],
126
- 'Forecast Units': [forecast_7, forecast_14, forecast_30]
 
127
  })
128
- fig_bar = go.Figure(data=[
129
- go.Bar(x=bar_data['Period'], y=bar_data['Forecast Units'], marker_color='blue')
130
- ])
131
- fig_bar.update_layout(
132
- title="Forecast Summary",
133
- xaxis_title="Forecast Period",
 
 
 
 
 
 
 
 
134
  yaxis_title="Units",
135
- template='plotly_white'
136
  )
137
- st.plotly_chart(fig_bar)
138
 
139
  if __name__ == "__main__":
140
  main()
 
2
  import pandas as pd
3
  from prophet import Prophet
4
  from datetime import datetime, timedelta
 
5
  import plotly.graph_objects as go
6
 
7
  # Prepare data for Prophet
 
10
  start_date = end_date - timedelta(days=len(usage_series) - 1)
11
  dates = [start_date + timedelta(days=i) for i in range(len(usage_series))]
12
 
13
+ return pd.DataFrame({'ds': dates, 'y': usage_series})
14
+
15
+ # Train model with fixed low sensitivity
16
+ def train_model(usage_series):
17
+ df = prepare_prophet_data(usage_series)
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  model = Prophet(
19
  yearly_seasonality=False,
20
  weekly_seasonality=True,
21
  daily_seasonality=True,
22
+ changepoint_prior_scale=0.05, # Low sensitivity to recent changes
23
  changepoint_range=0.6
24
  )
25
+ model.fit(df)
26
  return model
27
 
28
+ # Forecast future usage
29
  def make_forecast(model, periods):
30
  future = model.make_future_dataframe(periods=periods)
31
  forecast = model.predict(future)
32
  return forecast, round(forecast['yhat'].tail(periods).sum())
33
 
34
+ # Validate input
35
  def validate_usage_series(usage_str):
36
  try:
37
+ usage = [float(x) for x in usage_str.split(',')]
38
+ if len(usage) != 60:
39
+ return None, "Please enter exactly 60 values."
40
+ if any(x < 0 for x in usage):
41
  return None, "Usage values must be non-negative."
42
+ return usage, None
43
  except:
44
+ return None, "Invalid format. Use 60 comma-separated numbers."
45
 
46
+ # Main app
47
  def main():
48
  st.title("SmartLab Consumables Forecast")
49
 
50
  st.header("Input Parameters")
51
  consumable_type = st.selectbox("Consumable Type", ['Filters', 'Reagents', 'Vials'])
52
+ usage_input = st.text_input("Last 60 Days Usage (comma-separated)", "")
53
  current_stock = st.number_input("Current Stock", min_value=0, value=0)
 
54
 
55
  if st.button("Generate Forecast"):
56
+ usage_series, error = validate_usage_series(usage_input)
57
  if error:
58
  st.error(error)
59
  return
60
 
61
+ model = train_model(usage_series)
 
 
 
 
 
62
  forecast_df, forecast_7 = make_forecast(model, 7)
63
  _, forecast_14 = make_forecast(model, 14)
64
  _, forecast_30 = make_forecast(model, 30)
65
 
66
  st.header("Forecast Results")
67
+ st.write(f"πŸ“Š **7-Day Forecast**: {forecast_7} units")
68
+ st.write(f"πŸ“Š **14-Day Forecast**: {forecast_14} units")
69
+ st.write(f"πŸ“Š **30-Day Forecast**: {forecast_30} units")
70
+
71
+ st.header("Threshold Alerts 🚨")
72
+ alert_7 = current_stock < forecast_7
73
+ alert_14 = current_stock < forecast_14
74
+ alert_30 = current_stock < forecast_30
75
+
76
+ if alert_7:
77
+ st.warning(f"🚩 Current stock ({current_stock}) is below 7-day forecast ({forecast_7})")
78
+ if alert_14:
79
+ st.warning(f"🚩 Current stock ({current_stock}) is below 14-day forecast ({forecast_14})")
80
+ if alert_30:
81
+ st.warning(f"🚩 Current stock ({current_stock}) is below 30-day forecast ({forecast_30})")
82
+
83
+ st.header("Order Suggestions πŸ›’")
84
+ st.write(f"➑️ **7 Days**: Order {max(0, forecast_7 - current_stock)} units")
85
+ st.write(f"➑️ **14 Days**: Order {max(0, forecast_14 - current_stock)} units")
86
+ st.write(f"➑️ **30 Days**: Order {max(0, forecast_30 - current_stock)} units")
87
+
88
+ # Forecast trend line
89
+ st.header("Forecast Trend")
90
  fig_curve = go.Figure()
91
  fig_curve.add_trace(go.Scatter(
92
  x=forecast_df['ds'],
93
  y=forecast_df['yhat'],
94
  mode='lines',
95
+ name='Predicted Usage',
96
+ line=dict(color='blue')
97
  ))
98
  fig_curve.update_layout(
99
+ title="Forecast Curve",
100
+ xaxis_title="Date",
101
+ yaxis_title="Units",
102
+ template="plotly_white"
103
  )
104
  st.plotly_chart(fig_curve)
105
 
106
+ # Threshold bar chart
107
+ st.header("Stock vs Forecast 🚦")
108
+ alert_data = pd.DataFrame({
109
+ 'Category': ['Current Stock', '7-Day Forecast', '14-Day Forecast', '30-Day Forecast'],
110
+ 'Units': [current_stock, forecast_7, forecast_14, forecast_30],
111
+ 'Flag': [False, alert_7, alert_14, alert_30]
112
  })
113
+ colors = ['green'] + ['red' if a else 'blue' for a in alert_data['Flag'][1:]]
114
+ flags = ["" if not f else "🚩" for f in alert_data['Flag']]
115
+
116
+ fig_alert = go.Figure()
117
+ fig_alert.add_trace(go.Bar(
118
+ x=alert_data['Category'],
119
+ y=alert_data['Units'],
120
+ marker_color=colors,
121
+ text=flags,
122
+ textposition='auto'
123
+ ))
124
+ fig_alert.update_layout(
125
+ title="Threshold Alerts Overview",
126
+ xaxis_title="Category",
127
  yaxis_title="Units",
128
+ template="plotly_white"
129
  )
130
+ st.plotly_chart(fig_alert)
131
 
132
  if __name__ == "__main__":
133
  main()