Lesson 3.4: Time series: trend, seasonality and simple forecasts
Learn to split a series into trend, seasonality and noise, build forecasts with seasonal indices and smoothing, and judge them against honest baselines.
By the end of this lesson you will be able to read a series of numbers over time, separate its trend, seasonal pattern and noise, produce a sensible forecast with a seasonal index and with exponential smoothing, and judge any forecast against simple baselines using an error measure. You will decompose twelve quarters of sales by hand, forecast the next four, and see why a method that tracks recent data can lose to one that just repeats last year. You should have read Lessons 1.3 and 3.1, since forecasting builds on averages and on the habit of beating a baseline. The mathematics is averages and ratios, and the Python uses a few lines of pandas and statsmodels.
A time series is a set of measurements taken at regular points in time: daily sales, monthly loan disbursals, weekly sign-ups. Time changes the rules. In earlier lessons the order of rows did not matter, and shuffling a table changed nothing. In a time series the order is the information. Today's value depends on yesterday's, and ignoring that gives answers that look fine and fail the first time the future arrives.
The parts of a series
Most business series are a mix of four components.
Trend: the long-run direction, such as steady growth in disbursals.
Seasonality: a regular pattern that repeats over a fixed period, such as a jump every festival quarter or a dip every Sunday.
Cycle: longer swings with no fixed length, such as economic booms and slowdowns.
Noise: the random leftover that no pattern explains.
Separating them is called decomposition, and it answers different questions. The trend tells you whether the business is growing. The seasonal pattern tells you what to expect in each period and prevents you from mistaking a normal dip for a problem. The noise tells you how much uncertainty remains.
There are two ways to combine them. In an additive model the series is trend plus seasonality plus noise, so a December bump is a fixed number of units above normal. In a multiplicative model the seasonal effect is a percentage of the trend, so a December bump grows as the business grows. Most business series with growth are multiplicative, because the festival season lifts a large business by more rupees than a small one. If the seasonal swings get wider as the level rises, use multiplicative.
A worked example: twelve quarters of sales
A retailer's quarterly sales, in ₹ lakh, for three years are:
Year · Q1 · Q2 · Q3 · Q4 · Year total · Quarterly averageYear 1 · 100 · 120 · 90 · 140 · 450 · 112.5
Year 2 · 110 · 132 · 99 · 154 · 495 · 123.75
Year 3 · 121 · 145 · 109 · 169 · 544 · 136.0
The picture shows both patterns at once. The trend rises, with the yearly average growing from 112.5 to 123.75, which is 10 per cent, and then to 136, which is 9.9 per cent. Superimposed is a seasonal shape: the second quarter is a little above the trend, the third is well below it and the fourth is the peak. The yearly totals confirm the trend, and the quarterly pattern is what repeats.
Predict first: without calculating, what share of its yearly average does Q4 sell, roughly: 100 per cent, 120 per cent or 150 per cent? Decide before reading on.
Estimating seasonal indices
A seasonal index states how a period compares with the average. To find it, divide each quarter by its year's quarterly average and then average across years. For Q1: 100 ÷ 112.5 = 0.889, 110 ÷ 123.75 = 0.889 and 121 ÷ 136 = 0.890, an average of 0.889. For Q2: 120 ÷ 112.5 = 1.067, 132 ÷ 123.75 = 1.067 and 145 ÷ 136 = 1.066, an average of 1.067. For Q3: 90 ÷ 112.5 = 0.800, 99 ÷ 123.75 = 0.800, 109 ÷ 136 = 0.801, average 0.800. For Q4: 140 ÷ 112.5 = 1.244, 154 ÷ 123.75 = 1.244, 169 ÷ 136 = 1.243, average 1.244.
The four indices add to 4.000, as they must, since they are shares of an average. Q4 sells about 124 per cent of an average quarter, so the answer to the question above is closest to 120 per cent. Because the indices are so stable across the three years, the pattern is reliable. If they varied a lot from year to year, the seasonal estimate would be weak.
Forecasting with trend and seasonality
To forecast Year 4, project the trend and apply the seasonal index. The yearly average grew about 10 per cent a year, so the Year 4 average is 136 × 1.10 = 149.6. Multiply by each index: Q1 is 149.6 × 0.889 = 133.0, Q2 is 149.6 × 1.067 = 159.6, Q3 is 149.6 × 0.800 = 119.7 and Q4 is 149.6 × 1.244 = 186.1. The forecast for the year totals about 598.
Judging a forecast: baselines and error
A forecast is only worth using if it beats a simple alternative. Two baselines are standard. The naive forecast repeats the last observed value. The seasonal naive forecast repeats the value from the same period last year. They cost nothing, so any sophisticated method must do better to deserve its complexity.
To compare methods you need an error measure. The mean absolute percentage error, or MAPE, is the average of the absolute error divided by the actual value, expressed as a percentage. It is easy to explain and compares across series of different sizes. Its weakness is that it blows up when actual values are near zero.
Suppose the actual sales for Year 4 turn out to be 130, 160, 118 and 185. The three methods are compared below.
Quarter · Actual · Naive (repeat last, 169) · Seasonal naive (last year's quarter) · Seasonal index with growthQ1 · 130 · 169 · 121 · 133.0
Q2 · 160 · 169 · 145 · 159.6
Q3 · 118 · 169 · 109 · 119.7
Q4 · 185 · 169 · 169 · 186.1
For the naive forecast, the absolute percentage errors are 39 ÷ 130 = 30.0 per cent, 9 ÷ 160 = 5.6 per cent, 51 ÷ 118 = 43.2 per cent and 16 ÷ 185 = 8.6 per cent, a MAPE of 21.9 per cent. For the seasonal naive, they are 9 ÷ 130 = 6.9 per cent, 15 ÷ 160 = 9.4 per cent, 9 ÷ 118 = 7.6 per cent and 16 ÷ 185 = 8.6 per cent, a MAPE of 8.1 per cent. For the seasonal index with growth, the errors are 3 ÷ 130 = 2.3 per cent, 0.4 ÷ 160 = 0.3 per cent, 1.7 ÷ 118 = 1.4 per cent and 1.1 ÷ 185 = 0.6 per cent, a MAPE of about 1.1 per cent.
Read this table with some suspicion, because the example is tidy by design. Real series have noise, breaks and one-off events, and a method that wins by this much on a clean series will usually win by less. The point is the comparison method: always show the error of the baselines beside the error of your model. If the model's MAPE of 12 per cent is not clearly below the seasonal naive MAPE of 14 per cent, the extra complexity is not earning its keep.
Smoothing: using recent data without chasing noise
A moving average replaces each value with the average of the last k values. It smooths noise and reveals trend, but it lags, because it takes time for the average to move. A three-month moving average of a series that is climbing will always be below the latest value.
A moving average by hand
Take six weekly orders: 10, 12, 11, 13, 15 and 14. A three-week moving average starts at the third week. The first value is (10 + 12 + 11) ÷ 3 = 11, the next is (12 + 11 + 13) ÷ 3 = 12, then (11 + 13 + 15) ÷ 3 = 13, and finally (13 + 15 + 14) ÷ 3 = 14. The raw series bounces up and down, while the smoothed one climbs steadily, which makes the upward trend obvious. But look at the last week: the actual was 14 and the moving average is 14 only because it has caught up. During the climb, each average sat below the latest actual by a week or two. That lag is the price of smoothing, and a longer window gives a smoother line with a longer lag. In pandas the calculation is series.rolling(3).mean().
Exponential smoothing improves on this by giving more weight to recent values, with weights that fall away smoothly. The forecast for the next period is
new forecast = α × latest actual + (1 − α) × previous forecast
where α, called the smoothing parameter, lies between 0 and 1. A high α, such as 0.8, trusts the latest value and reacts fast. A low α, such as 0.1, changes slowly and smooths heavily. Unrolling the formula shows that the value k periods ago gets the weight α × (1 − α) to the power k, a geometric fall that never quite reaches zero.
Take a forecast of 100 and a new actual of 120. With α = 0.3 the new forecast is 0.3 × 120 + 0.7 × 100 = 106. With α = 0.8 it is 0.8 × 120 + 0.2 × 100 = 116. The right α depends on the series: if it is steady with noise, a low α averages the noise out, and if it shifts level often, a high α keeps up.
Simple exponential smoothing forecasts a flat line, because it assumes no trend or season. Two extensions handle them. Holt's method adds a trend term, and Holt-Winters adds seasonal terms too. Both use the same idea of blending the new observation with the old estimate, applied separately to level, trend and season. They are the natural step up from the seasonal index of this lesson and are built into statsmodels.
Method · Handles · Strengths · WeaknessNaive · Nothing · Free baseline · Ignores trend and season
Seasonal naive · Season · Free and strong for stable patterns · Carries last year's noise
Moving average · Smooths noise · Simple · Lags a trend, flat forecast
Simple exponential smoothing · Level · Weights recent data · Flat forecast, no trend or season
Holt-Winters · Level, trend, season · A good all-round method · Needs enough seasons, sensitive to shocks
## Stationarity and why it matters
Many statistical methods assume a series is stationary, meaning its average and spread do not change over time. A series with a trend is not stationary, and neither is one whose swings grow. The standard fix is differencing: replace each value by its change from the previous one. Differencing a series that grows steadily gives a series of roughly constant changes. A seasonal difference subtracts the value from a year ago. You do not need these for the simple methods in this lesson, but you will meet them in more advanced models such as ARIMA, and the idea that you must remove trend and season before modelling the remainder is the same.
A related caution is autocorrelation: in a time series, successive values are related, so the independence assumed in Lesson 2.3's tests does not hold. A test that treats 365 daily values as 365 independent observations overstates how much information it has.
How wrong could the forecast be?
A forecast without a range invites false confidence. A rough range comes from the past errors: if the model's errors on the holdout have a standard deviation of 8 lakh, then about 95 per cent of the time you would expect the actual to fall within roughly two standard deviations, 16 lakh, of the forecast. For the Q4 forecast of 186 lakh that gives a plausible range of about 170 to 202 lakh. The range widens the further ahead you forecast, because errors compound, and it widens when the series has been shaken by events the model has not seen. Managers can plan with a range, with stock for the high case and a cash buffer for the low one, but a single number gives them nothing to plan around.
Validating forecasts honestly
Since order matters, you cannot validate a forecast by shuffling the data into random training and test sets. A model that sees the future when it learns will look wonderful and fail in use. The right approach is to train on the past and test on the future: fit on the first part of the series and measure the errors on a later holdout. A more careful version repeats this, moving the cut-off forward in time, which is called rolling-origin or time-series cross-validation, and Lesson 6.4 develops it. Always compare with the baselines on the same holdout.
python
import pandas as pd
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from statsmodels.tsa.seasonal import seasonaldecompose
sales = pd.Series(
[100, 120, 90, 140, 110, 132, 99, 154, 121, 145, 109, 169],
index=pd.periodrange("2024Q1", periods=12, freq="Q"),
)
parts = seasonaldecompose(sales.totimestamp(), model="multiplicative", period=4)
parts.seasonal.head(4) # the seasonal indices, close to 0.89, 1.07, 0.80, 1.24
train, test = sales[:8], sales[8:] # train on the past, test on the future
model = ExponentialSmoothing(train, trend="add", seasonal="mul", seasonalperiods=4).fit()
forecast = model.forecast(4)
import numpy as np
mape = np.mean(np.abs(forecast.values - test.values) / test.values) 100
baseline = np.mean(np.abs(train.values[-4:] - test.values) / test.values) 100 # seasonal naive
Practical points before you forecast
A short list of checks prevents most forecasting embarrassments.
Plot first. Look for trend, season, breaks and outliers before choosing a method.
Know the calendar. Festivals move each year, months differ in length and days of the week differ. A seasonal pattern tied to a moving festival needs a flag, not a fixed index.
Handle shocks. A pandemic quarter or a one-off policy change should be marked, not averaged in as if it were normal.
Respect the horizon. Short-term forecasts are far more reliable than long-term ones, and the error grows with distance.
Give a range, not just a number. A forecast of 186 lakh with a plausible range of 170 to 200 is more honest than a single figure.
Re-forecast regularly. Update as new actuals arrive, and track the error over time.
Tie it to a decision. Stock, staffing and funding plans use forecasts, so measure how much a forecast error costs.
Common mistakes
Shuffling time series data. Order carries the information. Split by time.
Skipping the baselines. Always compare with naive and seasonal naive forecasts.
Reading a pattern from too few seasons. Three years give three observations of each quarter, which is a thin basis.
Using an additive model for growing swings. If the seasonal swing widens with the level, use multiplicative.
Treating autocorrelated days as independent. Standard tests overstate your evidence.
Forgetting events. Festivals, outages and policy changes need flags.
Reporting one number with false precision. Give a range, and say what would change it.
Over-fitting a short series. A complicated model fitted to twelve points will chase noise.
Key takeaways
A time series has trend, seasonality, cycles and noise. Decompose it additively or multiplicatively, and use multiplicative when swings grow with the level.
A seasonal index is a period's share of an average period. In the example Q4 is 1.244 and Q3 is 0.800, summing to 4.
A simple forecast projects the trend and applies the seasonal index. Always judge it against naive and seasonal naive baselines using an error measure such as MAPE.
Exponential smoothing blends the latest actual and the previous forecast with a weight α. High α reacts fast and low α smooths. Holt-Winters adds trend and season.
Differencing removes trend, and autocorrelation breaks the independence assumed by earlier tests.
Validate by training on the past and testing on the future, and report a range around every forecast.
Check yourself
1. A seasonal index for a month is 1.25 and the yearly monthly average is 80. What is the expected value for that month?
80 × 1.25 = 100, before any trend adjustment.
2. Why does the series' order matter when you validate a model?
Using future data to train gives falsely good results. You must train on the past and test on a later period.
3. With α = 0.4, a previous forecast of 200 and a new actual of 230, what is the new forecast?
0.4 × 230 + 0.6 × 200 = 92 + 120 = 212.
4. A naive forecast has a MAPE of 20 per cent and your model has 19 per cent. Should you use the model?
Probably not on this evidence alone. The gain is small, so the added complexity may not be worth it. Check on more periods.
5. Sales grow 10 per cent a year and the Q4 index is 1.244. Last year's quarterly average was 136. What is the Q4 forecast for next year?
136 × 1.10 × 1.244 = 186.1.
Try this
Use a series you care about: monthly or quarterly sales, daily orders, or open data such as monthly electricity use. You need at least three years of seasonal data, or about 24 months. Set aside forty-five minutes.
1. Plot the series and write down what you see: trend, season, breaks, outliers.
2. Compute the yearly averages and the seasonal index for each period, as in the worked example. Check that the indices sum to the number of periods.
3. Build a forecast for the next year from the trend and the index, and also a naive and a seasonal naive forecast.
4. Hold out the most recent year, build the forecasts from the earlier data only, and compute the MAPE of each method on the holdout.
5. Try exponential smoothing with two values of α in the simulator and in code. Note how the forecast reacts to a recent jump.
6. Mark any unusual event in the history and say how you would treat it.
7. Write a four-sentence summary: the trend, the season, the best method against the baselines, and the range you would give a manager.
A good result is a forecast with an error measured on data the method never saw, beside the error of a free baseline. In the next module you move from describing and forecasting to building models: a method for each question, starting with regression.
Tags: Data Science Time series Forecasting Seasonality Exponential smoothing