Time Series Forecasting: A Beginner-Friendly Introduction

A beginner-friendly introduction to time series forecasting on 36 years of real monthly CO2 measurements: what trend and seasonality are, why simple benchmark methods matter, how Holt-Winters combines both patterns, and how to test forecasts honestly by repeatedly forecasting the next year from past data only.

GloryTecks Team

Training & Placement Faculty

6 May 2026 7 min read

Last updated: 25 September 2026

Share:

A time series is a sequence of measurements in time order: daily sales, hourly electricity demand, monthly subscribers. Forecasting it means predicting future values from the past, and it differs from ordinary machine learning in one crucial way: the order of the data matters, so the future must never leak into the past. This guide builds and evaluates a first set of forecasts on a real series, and shows why the simplest methods deserve respect.

The data: 36 years of monthly CO2

NOAA's Global Monitoring Laboratory publishes the monthly average concentration of carbon dioxide in the air measured at Mauna Loa Observatory in Hawaii, as freely available data (NOAA: trends in atmospheric CO2). It is an ideal teaching series because it shows the two most common patterns clearly:

python
import numpy as np
import pandas as pd
from statsmodels.tsa.holtwinters import ExponentialSmoothing

co2 = pd.read_csv("https://gml.noaa.gov/webdata/ccgg/trends/co2/co2_mm_mlo.csv", comment="#")
co2 = co2[co2["year"].between(1990, 2025)].reset_index(drop=True)      # fixed window for reproducibility
y = co2["average"].to_numpy()
yearly = co2.groupby("year")["average"]
print(len(y), "months | 2025 range within the year:", round(yearly.max()[2025] - yearly.min()[2025], 2),
      "ppm | 2025 vs 2024 average:", round(yearly.mean()[2025] - yearly.mean()[2024], 2), "ppm")
text
432 months | 2025 range within the year: 6.14 ppm | 2025 vs 2024 average: 2.74 ppm
  • Trend: the level rises year after year; the 2025 average was 2.74 parts per million (ppm) above 2024's.
  • Seasonality: within each year the level swings by about 6 ppm, typically peaking in May and bottoming out in September, a pattern that repeats every 12 months.

Most business series have the same ingredients, often with more noise: a trend from growth, seasonality from weeks, months or holidays, and irregular shocks.

Five forecasting methods, from trivial to Holt-Winters

Start with benchmarks. Forecasting: Principles and Practice, the standard free textbook, describes simple methods that are "extremely simple and surprisingly effective" and uses them as benchmarks for everything else (Hyndman and Athanasopoulos: simple forecasting methods):

  • Naive: every future value equals the last observed value.
  • Drift: continue the average change seen over the whole history.
  • Seasonal naive: each month equals the same month last year.
  • Seasonal naive plus growth: last year's pattern, shifted up by last year's increase, a hand-made combination of trend and seasonality.
  • Holt-Winters: exponential smoothing that estimates a level, a trend and a seasonal pattern, updating each as new data arrives (statsmodels: ExponentialSmoothing).
python
def naive(history, h=12):
    return np.repeat(history[-1], h)

def drift(history, h=12):
    return history[-1] + (history[-1] - history[0]) / (len(history) - 1) * np.arange(1, h + 1)

def seasonal_naive(history, h=12):
    return history[-12:]

def seasonal_naive_growth(history, h=12):
    return history[-12:] + (history[-12:].mean() - history[-24:-12].mean())

def holt_winters(history, h=12):
    return ExponentialSmoothing(history, trend="add", seasonal="add", seasonal_periods=12).fit().forecast(h)

Evaluating honestly: a rolling forecasting origin

A random train-test split would let the model see the future. Instead, stand at the end of each December from 2014 to 2024, forecast the next 12 months using only data up to that point, and compare with what actually happened. Averaging over many such forecasts is called evaluation on a rolling forecasting origin (Hyndman and Athanasopoulos: time series cross-validation):

python
methods = {"naive": naive, "drift": drift, "seasonal naive": seasonal_naive,
           "seasonal naive + last year's growth": seasonal_naive_growth, "Holt-Winters": holt_winters}
origins = co2.index[(co2["month"] == 12) & co2["year"].between(2014, 2024)]   # forecast each following year
errors = {name: [] for name in methods}
for origin in origins:
    history, actual = y[: origin + 1], y[origin + 1: origin + 13]
    for name, forecast in methods.items():
        errors[name].append(np.abs(forecast(history) - actual))
for name, e in errors.items():
    print(f"{name:<36} MAE {np.mean(e):.2f} ppm")
print(f"({len(origins)} forecast years, 2015 to 2025)")
text
naive                                MAE 2.36 ppm
drift                                MAE 2.04 ppm
seasonal naive                       MAE 2.59 ppm
seasonal naive + last year's growth  MAE 0.72 ppm
Holt-Winters                         MAE 0.40 ppm
(11 forecast years, 2015 to 2025)

Each result teaches something:

  • Naive and drift ignore the seasonal swing, so they are off by the size of the swing.
  • Seasonal naive is the worst here, despite capturing the seasonal shape, because it assumes no growth; with growth of roughly 2 to 3.5 ppm a year over this period, last year's values are always too low.
  • Adding last year's growth fixes most of that, cutting the error to 0.72 ppm with one line of arithmetic.
  • Holt-Winters, which models trend and seasonality together, halves the error again, to 0.40 ppm.

The benchmarks earned their place: they show exactly which pattern matters, and they set the bar any fancier model must clear. On noisier business data, seasonal naive is often very hard to beat.

Forecasts get worse the further ahead you look

python
by_horizon = np.mean(errors["Holt-Winters"], axis=0)
print("Holt-Winters MAE 1, 3, 6 and 12 months ahead:", by_horizon[[0, 2, 5, 11]].round(2))
text
Holt-Winters MAE 1, 3, 6 and 12 months ahead: [0.29 0.36 0.43 0.51]

The error one month ahead is 0.29 ppm and grows to 0.51 ppm a year ahead. Uncertainty always widens with the horizon, so report accuracy for the horizon that matters to the decision, whether that is next week's staffing or next year's budget, and give ranges rather than single numbers when the stakes are high.

Beyond the classics

  • ARIMA models describe a series through its own past values and past errors, and are a standard alternative to exponential smoothing.
  • Regression and machine learning with lag features use previous values, rolling averages and calendar features as inputs to models such as gradient-boosted trees. Feature engineering shows time features in action on hourly bike rentals.
  • Deep learning and foundation models can help with many related series or complex patterns, but they must still beat the benchmarks above in the same rolling evaluation.
  • External drivers such as prices, promotions and weather often matter more than the choice of model, when they are known in advance.

A workflow for your own series

  1. Plot the series and name its patterns: trend, seasonal periods, outliers, level shifts and missing periods.
  2. Fix the question: what to forecast, at what granularity, how far ahead, and how the forecast will be used.
  3. Set up a rolling-origin evaluation that mirrors how forecasts will be made in practice.
  4. Score the benchmarks first: naive, seasonal naive and drift.
  5. Try one or two stronger methods, such as Holt-Winters or a lag-feature model, and keep them only if they beat the benchmarks at the horizons that matter.
  6. Monitor after launch, comparing each new forecast with what actually happened.

Common mistakes

  • Random train-test splits that leak the future into training.
  • No benchmark. A model with an error of 5% sounds good until seasonal naive scores 4%.
  • Evaluating on a single period. One unusual year can flatter or punish any method; roll the origin.
  • Ignoring changes in the process. A new product, a pricing change or a pandemic breaks patterns learned from the past; forecasts need monitoring and retraining.

What is the difference between a trend and seasonality?

A trend is a long-term increase or decrease. Seasonality is a pattern that repeats over a fixed period, such as every week or every year. The CO2 series has both: a steady rise and a yearly swing.

How much history do I need to forecast?

Enough to see the patterns you want to model; for yearly seasonality, at least two or three full years is a practical minimum, and more helps estimate the pattern reliably.

Should I use ARIMA, Holt-Winters or machine learning?

Try the simple benchmarks first, then Holt-Winters or ARIMA for a single series with clear patterns, and machine learning with lag features when you have many related series or useful external drivers. Choose by rolling-origin accuracy, not by reputation.

How do I measure forecast accuracy?

With errors on held-out future periods, such as mean absolute error, averaged over several forecast origins and reported for the horizons that matter. Evaluation metrics explained discusses choosing between MAE and RMSE.

Forecasting also appears as a Sales Forecasting project in the Data Science course.

Sources

GloryTecks Team

Training & Placement Faculty

The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.

Want to master Data Science?

Join GloryTecks’ expert-led Data Science training in Hyderabad with real-time projects and 100% placement support.

Explore course