Time Series Introduction
By the end of this lesson you will be able to say what a time series is, spot the patterns inside one, and make three simple forecasts for next month. You will also see why the simplest forecast is sometimes the best one.
What is a time series?
A time series is a list of measurements taken in time order, usually at a regular gap. The gap is called the frequency: every hour, every day, every month.
You meet time series everywhere in India:
- The number of passengers at Jaipur railway station each day.
- Monthly electricity units on your home bill.
- Daily rainfall in Kochi during the monsoon.
- A kirana shop's sales each month.
Forecasting means using the past values of a series to estimate its future values. A shopkeeper who orders extra sweets before Diwali is forecasting, even without any code.
The order of the rows is the most important fact about a time series. If you shuffle a class register, nothing is lost. If you shuffle a year of sales, the story disappears, because each month is linked to the months around it.
The example series for this course
Most lessons use the same series: monthly sales of a kirana (grocery) shop in Jaipur, in thousands of rupees, from January 2019 to December 2025. The data is invented with NumPy, so we know exactly which patterns are inside it. That lets you check whether a method finds them.
Step 1. Run the code and read the first five months.
import numpy as np
import pandas as pd
def make_sales(seed=1):
"""Monthly sales (thousand rupees) of a Jaipur kirana shop, Jan 2019 to Dec 2025."""
rng = np.random.default_rng(seed)
months = pd.date_range("2019-01-01", "2025-12-01", freq="MS")
t = np.arange(len(months))
season = np.array([0.95, 0.92, 1.0, 0.98, 0.97, 0.93, 0.86, 0.88, 0.96, 1.22, 1.28, 1.05])
sales = (180 + 1.5 * t) * season[months.month - 1] + rng.normal(0, 6, len(t))
return pd.Series(sales.round(1), index=months, name="sales")
sales = make_sales()
print(sales.head())
print("Months of data:", len(sales))
print("First month:", sales.index[0].date(), " Last month:", sales.index[-1].date())2019-01-01 173.1 2019-02-01 171.9 2019-03-01 185.0 2019-04-01 173.0 2019-05-01 185.9 Freq: MS, Name: sales, dtype: float64 Months of data: 84 First month: 2019-01-01 Last month: 2025-12-01
You should see 84 months. Each label is the first day of a month, which pandas calls MS (month start). The make_sales function has three parts, and they are the three parts of almost every real series:
- Trend:
180 + 1.5 * tgrows by Rs 1,500 a month as the colony grows. - Seasonality: the
seasonlist repeats every year. July is weak (monsoon, fewer visitors) and October and November are strong (Diwali shopping). - Noise:
rng.normal(0, 6, ...)adds random ups and downs nobody can predict.
Look before you forecast
Step 2. Draw the series and print the average for each calendar month.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def make_sales(seed=1):
"""Monthly sales (thousand rupees) of a Jaipur kirana shop, Jan 2019 to Dec 2025."""
rng = np.random.default_rng(seed)
months = pd.date_range("2019-01-01", "2025-12-01", freq="MS")
t = np.arange(len(months))
season = np.array([0.95, 0.92, 1.0, 0.98, 0.97, 0.93, 0.86, 0.88, 0.96, 1.22, 1.28, 1.05])
sales = (180 + 1.5 * t) * season[months.month - 1] + rng.normal(0, 6, len(t))
return pd.Series(sales.round(1), index=months, name="sales")
sales = make_sales()
fig, ax = plt.subplots(figsize=(9, 3.5))
ax.plot(sales.index, sales.values, color="#4f46e5")
ax.set_title("Monthly sales of a Jaipur kirana shop")
ax.set_ylabel("Sales (thousand Rs)")
plt.show()
by_month = sales.groupby(sales.index.month).mean().round(1)
print("Average by calendar month:")
print(by_month.to_string())Average by calendar month: 1 219.1 2 215.9 3 236.5 4 232.0 5 233.2 6 222.2 7 210.5 8 213.0 9 236.1 10 304.2 11 319.0 12 262.9
The chart climbs from left to right, with a sharp peak every October and November. The table confirms it: November averages about 319, July only about 210. Notice how quickly the chart shows the pattern. Always plot a series before you model it, just as a doctor looks at you before writing a prescription.
Your first three forecasts
Pretend today is 30 September 2025. You know every month up to September and must guess October. Here are three simple ideas a shopkeeper might use.
Step 3. Run the code and compare each guess with what really happened.
import numpy as np
import pandas as pd
def make_sales(seed=1):
"""Monthly sales (thousand rupees) of a Jaipur kirana shop, Jan 2019 to Dec 2025."""
rng = np.random.default_rng(seed)
months = pd.date_range("2019-01-01", "2025-12-01", freq="MS")
t = np.arange(len(months))
season = np.array([0.95, 0.92, 1.0, 0.98, 0.97, 0.93, 0.86, 0.88, 0.96, 1.22, 1.28, 1.05])
sales = (180 + 1.5 * t) * season[months.month - 1] + rng.normal(0, 6, len(t))
return pd.Series(sales.round(1), index=months, name="sales")
sales = make_sales()
history = sales[:"2025-09-01"] # pretend today is the end of September 2025
actual = sales["2025-10-01"]
guesses = {
"Same as last month": history.iloc[-1],
"Average of last 3 months": history.iloc[-3:].mean(),
"Same month last year": history["2024-10-01"],
}
print("Actual October 2025:", actual)
for name, guess in guesses.items():
print(f"{name:<26} {guess:6.1f} error {actual - guess:+6.1f}")Actual October 2025: 369.0 Same as last month 292.7 error +76.3 Average of last 3 months 269.2 error +99.8 Same month last year 347.4 error +21.6
"Same month last year" wins easily, missing by about 22 thousand rupees. The other two ignore the Diwali season, so they guess far too low. The averaging method is even worse than "last month", because it mixes in the weak monsoon months.
This small test teaches the whole course in miniature. A good forecast must respect the patterns in the data. It must also be tested on months it has not seen. And every clever model must beat these simple ideas, called baselines, before anyone trusts it.
What this course covers
| Part | You will learn |
|---|---|
| Dates and resampling | Store dates in pandas, change daily data to weekly or monthly, smooth with rolling windows |
| Patterns | Split a series into trend, seasonality and noise; test stationarity; read autocorrelation |
| Measuring | MAE, RMSE and MAPE; splitting by time; walk-forward validation |
| Classical models | Baselines, exponential smoothing written by hand, Holt-Winters, ARIMA and SARIMA |
| Machine learning | Lag features with scikit-learn, multi-step forecasts, festivals, uncertainty |
| Project | A complete monthly demand forecast for a shop |
Common mistakes
- Shuffling the rows. Random train/test splits leak the future into the past.
- Skipping the plot. A one-minute chart often shows a trend or a broken month that statistics hide.
- Forgetting seasonality. "Last month" looks sensible until a festival or the monsoon arrives.
- Trusting a model without a baseline. If "same month last year" is just as good, the model adds nothing.
Key points
- A time series is a set of values in time order at a regular frequency.
- Most series contain a trend, seasonality and noise.
- Plot the series first; the eye spots patterns quickly.
- Simple baseline forecasts are easy to compute and often hard to beat.
- Always judge a forecast on data it has not seen.
Exercise
Pretend today is 31 October 2025 and forecast November 2025 with the same three methods. Print each guess and its error. Which method wins this time, and why?
Show solution
import numpy as np
import pandas as pd
def make_sales(seed=1):
rng = np.random.default_rng(seed)
months = pd.date_range("2019-01-01", "2025-12-01", freq="MS")
t = np.arange(len(months))
season = np.array([0.95, 0.92, 1.0, 0.98, 0.97, 0.93, 0.86, 0.88, 0.96, 1.22, 1.28, 1.05])
sales = (180 + 1.5 * t) * season[months.month - 1] + rng.normal(0, 6, len(t))
return pd.Series(sales.round(1), index=months, name="sales")
sales = make_sales()
history = sales[:"2025-10-01"]
actual = sales["2025-11-01"]
guesses = {
"Same as last month": history.iloc[-1],
"Average of last 3 months": history.iloc[-3:].mean(),
"Same month last year": history["2024-11-01"],
}
print("Actual November 2025:", actual)
for name, guess in guesses.items():
print(f"{name:<26} {guess:6.1f} error {actual - guess:+6.1f}")Actual November 2025: 378.1 Same as last month 369.0 error +9.1 Average of last 3 months 307.2 error +70.9 Same month last year 372.1 error +6.0
"Same month last year" wins again, missing by only 6. But "same as last month" is now a close second (9.1), because October and November are both festive months, so October is a good guide to November. The ranking of simple methods changes from month to month, which is why we test them over many months, not one.
Quick Quiz
1. Which of these is a time series?
Rainfall is measured repeatedly over time in order; the others are one-off snapshots.
2. What are the three common parts of a time series?
Most series combine a long-term direction, a repeating pattern and random variation.
3. Why did "same month last year" beat "average of last 3 months" for October 2025?
October is a festive month, so last October is a better guide than the monsoon months.
4. What is a baseline forecast?
Baselines set the minimum standard; a model that cannot beat them adds no value.
Learn Time Series with a mentor
Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
