azrai99's picture
Update app.py
b1decbe verified
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
import time
from neuralforecast.core import NeuralForecast
from neuralforecast.models import NHITS, TimesNet, LSTM, TFT
from neuralforecast.losses.pytorch import HuberMQLoss
from neuralforecast.utils import AirPassengersDF
import plotly.graph_objects as go
st.set_page_config(layout='wide')
def generate_forecast(model, df, tag=False):
if tag == 'retrain':
return model.predict()
return model.predict(df=df)
def determine_frequency(df):
df['ds'] = pd.to_datetime(df['ds'])
df = df.drop_duplicates(subset='ds').set_index('ds')
freq = pd.infer_freq(df.index)
if not freq:
st.warning('Defaulting to Daily frequency due to date inconsistencies. Please check your data.', icon="⚠️")
freq = 'D'
return freq
def plot_forecasts(forecast_df, train_df, title):
plot_df = pd.concat([train_df, forecast_df]).set_index('ds')
historical_col = 'y'
forecast_col = next((col for col in plot_df.columns if 'median' in col), None)
lo_col = next((col for col in plot_df.columns if 'lo-90' in col), None)
hi_col = next((col for col in plot_df.columns if 'hi-90' in col), None)
if forecast_col is None:
raise KeyError("No forecast column found in the data.")
fig = go.Figure()
fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[historical_col], mode='lines', name='Historical'))
fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[forecast_col], mode='lines', name='Forecast'))
if lo_col and hi_col:
fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[hi_col], mode='lines', line=dict(color='rgba(0,100,80,0.2)'), showlegend=False))
fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[lo_col], mode='lines', line=dict(color='rgba(0,100,80,0.2)'), fill='tonexty', fillcolor='rgba(0,100,80,0.2)', name='90% Confidence Interval'))
fig.update_layout(title=title, xaxis_title='Timestamp [t]', yaxis_title='Value', template='plotly_white')
st.plotly_chart(fig)
def select_model(horizon, model_type, max_steps=50):
if model_type == 'NHITS':
return NHITS(input_size=5 * horizon, h=horizon, max_steps=max_steps, stack_types=3*['identity'], n_blocks=3*[1], mlp_units=[[256, 256] for _ in range(3)], batch_size=32, scaler_type='standard', loss=HuberMQLoss(level=[90]))
elif model_type == 'TimesNet':
return TimesNet(h=horizon, input_size=horizon * 5, hidden_size=32, conv_hidden_size=64, loss=HuberMQLoss(level=[90]), scaler_type='standard', learning_rate=1e-3, max_steps=max_steps)
elif model_type == 'LSTM':
return LSTM(h=horizon, input_size=horizon * 5, loss=HuberMQLoss(level=[90]), scaler_type='standard', encoder_n_layers=3, encoder_hidden_size=256, context_size=10, decoder_hidden_size=256, decoder_layers=3, max_steps=max_steps)
elif model_type == 'TFT':
return TFT(h=horizon, input_size=horizon*5, hidden_size=96, loss=HuberMQLoss(level=[90]), learning_rate=0.005, scaler_type='standard', windows_batch_size=128, max_steps=max_steps)
else:
raise ValueError(f"Unsupported model type: {model_type}")
def model_train(df, model, freq):
nf = NeuralForecast(models=[model], freq=freq)
df['ds'] = pd.to_datetime(df['ds'])
nf.fit(df)
return nf
def forecast_time_series(df, model_type, horizon, max_steps, y_col):
start_time = time.time()
freq = determine_frequency(df)
st.sidebar.write(f"Data frequency: {freq}")
selected_model = select_model(horizon, model_type, max_steps)
model = model_train(df, selected_model, freq)
forecast_results = {model_type: generate_forecast(model, df, tag='retrain')}
st.session_state.forecast_results = forecast_results
for model_name, forecast_df in forecast_results.items():
plot_forecasts(forecast_df, df, f'{model_name} Forecast for {y_col}')
time_taken = time.time() - start_time
st.success(f"Time taken for {model_type} forecast: {time_taken:.2f} seconds")
if 'forecast_results' in st.session_state:
st.markdown('Download Input and Forecast Data below')
tab_insample, tab_forecast = st.tabs(["Input data", "Forecast"])
with tab_insample:
df_grid = df.drop(columns="unique_id")
st.write(df_grid)
with tab_forecast:
if model_type in forecast_results:
df_grid = forecast_results[model_type]
st.write(df_grid)
@st.cache_data
def load_default():
return AirPassengersDF.copy()
def personalized_forecasting():
st.title("Personalized Neural Forecasting")
st.markdown("Train a time series forecasting model from scratch using various deep neural network models.")
with st.sidebar.expander("Upload and Configure Dataset", expanded=True):
uploaded_file = st.file_uploader("Upload your time series data (CSV)", type=["csv"])
df = pd.read_csv(uploaded_file) if uploaded_file else load_default()
columns = df.columns.tolist()
ds_col = st.selectbox("Select Date/Time column", options=columns, index=columns.index('ds') if 'ds' in columns else 0)
target_columns = [col for col in columns if col != ds_col]
y_col = st.selectbox("Select Target column", options=target_columns, index=0)
df = df.rename(columns={ds_col: 'ds', y_col: 'y'}).assign(unique_id=1)[['unique_id', 'ds', 'y']]
st.sidebar.subheader("Dynamic Model Selection and Forecasting")
dynamic_model_choice = st.sidebar.selectbox("Select model", ["NHITS", "TimesNet", "LSTM", "TFT"], key="dynamic_model_choice")
dynamic_horizon = st.sidebar.number_input("Forecast horizon", value=12)
dynamic_max_steps = st.sidebar.number_input('Max steps', value=20)
if st.sidebar.button("Submit"):
with st.spinner('Training model...'):
forecast_time_series(df, dynamic_model_choice, dynamic_horizon, dynamic_max_steps, y_col)
pg = st.navigation({
"Neuralforecast": [
st.Page(personalized_forecasting, title="Personalized Forecasting", icon=":material/query_stats:")
],
})
pg.run()