DevelopmentAugust 04, 20254 min read

Time Series Forecasting using Python and Streamlit

Forecast future values with Python and Streamlit — moving averages, linear trends, and seasonality on any time series, with honest accuracy checks.

Galvan

Galvan

Founder & Creator

Introduction

Forecasting has a reputation for requiring heavy machinery — ARIMA hyperparameters, Prophet installs, neural networks. But the honest first step is simpler: decompose the past, extend the pattern, and check yourself against reality. This app does exactly that on any CSV with dates and values: trend lines, seasonal patterns, and two forecast methods with a built-in backtest that shows you the error before you trust the future.

It builds on the resampling patterns from the sales analyzer and adds the discipline every forecasting tutorial skips: measuring your own accuracy.

Features

  • Any time series — upload a CSV with date and value columns.
  • Trend + seasonality view — rolling average and month-of-year pattern.
  • Two forecast methods — linear trend and seasonal-naive.
  • Adjustable horizon — forecast 1 to 12 periods ahead.
  • Backtesting — hold out the last 20% and report MAE before you trust anything.
  • Prerequisites

  • Python 3.8+ — from python.org.
  • Dependencies:
  • code
    pip install streamlit pandas numpy

    Step 1: Create the Script

    Save as forecast_app.py:

    code
    import streamlit as st
    import pandas as pd
    import numpy as np
    
    st.set_page_config(page_title="Time Series Forecast", page_icon="🔮", layout="wide")
    st.title("🔮 Time Series Forecasting")
    
    uploaded = st.file_uploader("Upload CSV with 'date' and 'value' columns", type=["csv"])
    
    if uploaded:
        df = pd.read_csv(uploaded, parse_dates=["date"]).sort_values("date")
        s = df.set_index("date")["value"].astype(float)
    
        horizon = st.slider("Forecast periods", 1, 12, 6)
        method = st.radio("Method", ["Linear trend", "Seasonal naive"], horizontal=True)
    
        # --- Backtest on the last 20% ---
        split = int(len(s) * 0.8)
        train, test = s[:split], s[split:]
    
        x_train = np.arange(len(train))
        coef = np.polyfit(x_train, train.values, 1)
        bt_pred = np.polyval(coef, np.arange(split, len(s)))
        mae = np.mean(np.abs(test.values - bt_pred))
        st.caption(f"Backtest MAE on last {len(test)} points: **{mae:.1f}** (lower = better)")
    
        # --- Forecast ---
        x_all = np.arange(len(s))
        future_x = np.arange(len(s), len(s) + horizon)
    
        if method == "Linear trend":
            coef_all = np.polyfit(x_all, s.values, 1)
            forecast = np.polyval(coef_all, future_x)
        else:
            season = 12 if len(s) >= 24 else max(2, len(s) // 3)
            forecast = np.array([s.values[-season + (i % season)] for i in range(horizon)])
    
        freq = pd.infer_freq(s.index) or "D"
        future_index = pd.date_range(s.index[-1], periods=horizon + 1, freq=freq)[1:]
    
        chart_df = pd.DataFrame({
            "history": s,
            "forecast": pd.Series(forecast, index=future_index),
        })
        st.line_chart(chart_df, height=350)
    
        with st.expander("Seasonality by month"):
            if s.index.month.nunique() > 1:
                st.bar_chart(s.groupby(s.index.month).mean())
                st.caption("Average value by month — repeating shape means seasonality.")
    else:
        st.info("Upload a CSV with date and value columns to begin.")

    Step 2: Run the App

    code
    streamlit run forecast_app.py

    Generate test data quickly with the sales analyzer's sample script, resampled monthly.

    How It Works

    Linear trend forecasting is np.polyfit — fitting a line through history and evaluating it at future x positions. It captures direction and slope but knows nothing about seasons. Seasonal naive is the opposite: it repeats the last full cycle of values (the last 12 months for monthly data), capturing seasonality but not trend. Comparing the two on your data — with the backtest MAE — tells you which pattern dominates.

    The backtest is the credibility engine: fit on the first 80%, predict the held-out last 20%, and report mean absolute error. A forecast you can't score against history is a guess; this turns the app into a small forecasting lab. If the backtest MAE is larger than the typical change between periods, no method will save you — the series is mostly noise.

    pd.infer_freq guesses the cadence (daily, monthly...) so the future index lines up with history on the chart. The seasonality bar chart — mean value per month — is the visual check for whether the seasonal method even applies.

    Common Errors & Fixes

  • `polyfit` warns about poorly conditioned data — x values are huge (days since 1970). Use positional indices (np.arange(len(s))) as above, never raw timestamps.
  • Forecast line doesn't connect to history — the future index starts one period after the last observation by design; overlap the first point if you want a connected line.
  • `infer_freq` returns None — irregular timestamps (gaps, duplicates); resample first (s.resample("D").mean()) to regularize.
  • Seasonal naive repeats wrong length — the season length must divide your data evenly-ish; the fallback max(2, len(s)//3) prevents degenerate season=1.
  • Key Concepts

  • Trend vs seasonality — two patterns, two methods, one chart to compare.
  • Backtesting — score on held-out history before trusting forecasts.
  • MAE — mean absolute error, in the units of your data.
  • Regular frequency — forecasting requires evenly spaced time.
  • What to Try Next

  • Add moving-average forecast — the average of the last k points, flat into the future.
  • Add confidence bands — forecast ± 2× backtest MAE as a shaded area with st.area_chart.
  • Try Holt-Winters (statsmodels) once linear/seasonal both feel limiting.
  • Apply it to your own expense tracker monthly totals — forecasting your spending is humbling.
  • FAQ

    Why not just use Prophet or ARIMA?

    You can — but both hide assumptions behind automation. Fitting a line and a seasonal repeat first teaches you what those libraries add, and often the simple method is within a few percent on stable series.

    What does MAE mean in practice?

    On average, the backtest predictions missed by that many units. A MAE of 42 on monthly sales averaging 1,000 is ~4% — decent. On a series averaging 60, it's useless.

    How much history do I need?

    For the seasonal method, at least two full cycles (24 monthly points). For linear trend, 10+ points gives a slope you can semi-trust — the backtest will tell you honestly.