ci_cd_prophet / app.py
muddasser's picture
Add files via upload
b079756 unverified
import streamlit as st
import pandas as pd
import numpy as np
from prophet import Prophet
import plotly.graph_objects as go
from datetime import datetime, timedelta
# ======================================
# Page Config
# ======================================
st.set_page_config(
page_title="✈️ Travel Demand Forecaster",
page_icon="✈️",
layout="wide"
)
st.title("✈️ Travel Demand Forecaster")
st.markdown("**Powered by Facebook Prophet** — Upload your travel data or use sample data to forecast future demand.")
# ======================================
# Sidebar Controls
# ======================================
st.sidebar.header("⚙️ Forecast Settings")
forecast_days = st.sidebar.slider(
"Forecast Period (days)",
min_value=30,
max_value=365,
value=90,
step=30
)
seasonality_mode = st.sidebar.selectbox(
"Seasonality Mode",
["multiplicative", "additive"],
index=0
)
yearly_seasonality = st.sidebar.checkbox("Yearly Seasonality", value=True)
weekly_seasonality = st.sidebar.checkbox("Weekly Seasonality", value=True)
daily_seasonality = st.sidebar.checkbox("Daily Seasonality", value=False)
# ======================================
# Data Input
# ======================================
st.subheader("📂 Data Input")
data_option = st.radio(
"Choose data source:",
["Use Sample Travel Data", "Upload CSV File"],
horizontal=True
)
def generate_sample_data():
"""Generate realistic travel demand sample data"""
np.random.seed(42)
dates = pd.date_range(start="2021-01-01", end="2023-12-31", freq="D")
# Base demand with trend
trend = np.linspace(100, 150, len(dates))
# Yearly seasonality (peak in summer and holidays)
yearly = 30 * np.sin(2 * np.pi * np.arange(len(dates)) / 365 - np.pi/2)
# Weekly seasonality (weekends higher)
weekly = 10 * np.sin(2 * np.pi * np.arange(len(dates)) / 7)
# Random noise
noise = np.random.normal(0, 8, len(dates))
demand = trend + yearly + weekly + noise
demand = np.maximum(demand, 10) # no negative demand
df = pd.DataFrame({"ds": dates, "y": demand.round(0)})
return df
if data_option == "Use Sample Travel Data":
df = generate_sample_data()
st.success("✅ Sample travel demand data loaded! (Jan 2021 — Dec 2023)")
st.dataframe(df.tail(10), use_container_width=True)
else:
uploaded_file = st.file_uploader(
"Upload CSV with 'ds' (date) and 'y' (value) columns",
type=["csv"]
)
if uploaded_file:
df = pd.read_csv(uploaded_file)
df['ds'] = pd.to_datetime(df['ds'])
st.success(f"✅ Loaded {len(df)} rows of data!")
st.dataframe(df.tail(10), use_container_width=True)
else:
st.info("👆 Please upload a CSV file with columns: **ds** (date) and **y** (value)")
st.stop()
# ======================================
# Train & Forecast
# ======================================
st.divider()
st.subheader("🔮 Forecast")
if st.button("🚀 Generate Forecast", type="primary"):
with st.spinner("Training Prophet model..."):
# Train model
model = Prophet(
seasonality_mode=seasonality_mode,
yearly_seasonality=yearly_seasonality,
weekly_seasonality=weekly_seasonality,
daily_seasonality=daily_seasonality
)
model.fit(df)
# Make future dataframe
future = model.make_future_dataframe(periods=forecast_days)
forecast = model.predict(future)
st.success(f"✅ Forecast generated for next {forecast_days} days!")
# ======================================
# Plot Results
# ======================================
col1, col2 = st.columns(2)
with col1:
st.metric(
"Average Forecasted Demand",
f"{forecast['yhat'].tail(forecast_days).mean():.0f}",
delta=f"+{forecast['yhat'].tail(forecast_days).mean() - df['y'].mean():.0f} vs historical"
)
with col2:
st.metric(
"Peak Forecasted Demand",
f"{forecast['yhat'].tail(forecast_days).max():.0f}",
delta="Next period peak"
)
# Main forecast plot
fig = go.Figure()
# Historical data
fig.add_trace(go.Scatter(
x=df['ds'], y=df['y'],
name='Historical',
mode='lines',
line=dict(color='#1f77b4', width=1.5)
))
# Forecast
forecast_future = forecast[forecast['ds'] > df['ds'].max()]
fig.add_trace(go.Scatter(
x=forecast_future['ds'], y=forecast_future['yhat'],
name='Forecast',
mode='lines',
line=dict(color='#ff7f0e', width=2, dash='dash')
))
# Confidence interval
fig.add_trace(go.Scatter(
x=pd.concat([forecast_future['ds'], forecast_future['ds'][::-1]]),
y=pd.concat([forecast_future['yhat_upper'], forecast_future['yhat_lower'][::-1]]),
fill='toself',
fillcolor='rgba(255,127,14,0.2)',
line=dict(color='rgba(255,255,255,0)'),
name='Confidence Interval'
))
fig.update_layout(
title="Travel Demand Forecast",
xaxis_title="Date",
yaxis_title="Demand",
hovermode='x unified',
height=450
)
st.plotly_chart(fig, use_container_width=True)
# Components plot
st.subheader("📊 Forecast Components")
col1, col2 = st.columns(2)
with col1:
# Trend
fig_trend = go.Figure()
fig_trend.add_trace(go.Scatter(
x=forecast['ds'], y=forecast['trend'],
mode='lines', line=dict(color='green', width=2)
))
fig_trend.update_layout(title="Trend", height=300)
st.plotly_chart(fig_trend, use_container_width=True)
with col2:
# Yearly seasonality
if yearly_seasonality:
fig_yearly = go.Figure()
fig_yearly.add_trace(go.Scatter(
x=forecast['ds'], y=forecast['yearly'],
mode='lines', line=dict(color='purple', width=2)
))
fig_yearly.update_layout(title="Yearly Seasonality", height=300)
st.plotly_chart(fig_yearly, use_container_width=True)
# Forecast table
st.subheader("📋 Forecast Data")
forecast_display = forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(forecast_days)
forecast_display.columns = ['Date', 'Forecast', 'Lower Bound', 'Upper Bound']
forecast_display = forecast_display.round(2)
st.dataframe(forecast_display, use_container_width=True)
# Download button
csv = forecast_display.to_csv(index=False)
st.download_button(
label="📥 Download Forecast CSV",
data=csv,
file_name="travel_forecast.csv",
mime="text/csv"
)