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
Last updated: 25 September 2026
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:
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")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).
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):
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)")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
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))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
- Plot the series and name its patterns: trend, seasonal periods, outliers, level shifts and missing periods.
- Fix the question: what to forecast, at what granularity, how far ahead, and how the forecast will be used.
- Set up a rolling-origin evaluation that mirrors how forecasts will be made in practice.
- Score the benchmarks first: naive, seasonal naive and drift.
- 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.
- 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?
How much history do I need to forecast?
Should I use ARIMA, Holt-Winters or machine learning?
How do I measure forecast accuracy?
Forecasting also appears as a Sales Forecasting project in the Data Science course.
Sources
- Trends in atmospheric carbon dioxide (NOAA Global Monitoring Laboratory) — NOAA Global Monitoring Laboratory
- Some simple forecasting methods (Forecasting: Principles and Practice, 3rd edition) — OTexts
- Time series cross-validation (Forecasting: Principles and Practice) — OTexts
- ExponentialSmoothing (statsmodels) — statsmodels documentation
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.
Related articles
Data Science Roadmap 2026: A Complete Step-by-Step Guide
A data science roadmap in dependency order, from Python and SQL through cleaning, statistics and honest model evaluation to communicating results, with an exit test at every stage and one dataset that carries through the whole path.
Data Scientist Salary in Hyderabad 2026: How Pay Grows From Fresher to Senior
Salary pages for data scientists in Hyderabad usually show neat ranges with no source. This guide uses real 2025 survey data instead: how pay in India grows from the first years to senior level, how much the stages overlap, and what the data can and cannot tell you about a Hyderabad offer.
Data Science vs Data Analytics: Which Career Is Right for You?
A career-choice guide rather than a feature table: what a working week looks like for a data analyst and a data scientist, a self-check to see which kind of work suits you, the skills to build first for each, how people move between the two, and why the choice is less permanent than it feels.