Advanced
60–90 min read
#Time Series#EDA#Stationarity#Trend#Seasonality#STL#ACF#PACF#Resampling

Time Series Foundations & Exploratory Analysis

A detailed foundation for understanding, exploring, visualizing, and preparing time series data before statistical, machine learning, and deep learning forecasting.

Time Series Foundations & Exploratory Analysis

1. Learning Objectives#

By the end of this notebook, you should be able to:

  • Explain what time series data is and how it differs from cross-sectional data.
  • Identify trend, seasonality, cyclicity, and noise.
  • Understand why time order matters.
  • Work correctly with timestamps, frequencies, missing dates, and resampling.
  • Perform a structured exploratory analysis of time series data.
  • Understand stationarity and why it matters for many forecasting methods.
  • Calculate and interpret rolling statistics.
  • Understand autocorrelation and partial autocorrelation.
  • Use ACF and PACF plots to investigate temporal dependence.
  • Perform seasonal decomposition with STL.
  • Recognize common time-series data problems and avoid future-data leakage.
  • Prepare a clean dataset for the statistical and machine-learning notebooks that follow.

2. What Is Time Series Data?

A time series is a sequence of observations recorded over time, usually at regular or meaningful intervals.

Examples:

  • Daily stock prices
  • Hourly electricity demand
  • Monthly sales
  • Weekly website traffic
  • Minute-level sensor measurements
  • Daily temperature
  • Quarterly GDP
  • Number of orders per day

A simple time series can be represented as:

y1,y2,y3,,yty_1, y_2, y_3, \ldots, y_t

where each observation is associated with a timestamp:

(t1,y1),(t2,y2),,(tn,yn)(t_1,y_1), (t_2,y_2), \ldots, (t_n,y_n)

The key characteristic is that observations are ordered in time.

This means that:

The past can contain information about the future.

That temporal relationship is what makes time series different from ordinary tabular machine learning.


3. Cross-Sectional Data vs Time Series Data

3.1 Cross-Sectional Data#

Cross-sectional data contains observations from different entities at approximately the same point in time.

Example:

CustomerAgeIncomePurchases
A25500004
B41800009
C33620006

The order of rows usually does not carry predictive meaning.

3.2 Time Series Data#

Time series data contains observations indexed by time.

DateSales
2025-01-01120
2025-01-02135
2025-01-03128
2025-01-04145

Here, changing the order of observations destroys important information.

For example:

Salest1SalestSales_{t-1} \rightarrow Sales_t

may contain useful predictive information.

3.3 Why This Difference Matters#

In ordinary machine learning, randomly splitting observations into training and test sets can often be reasonable.

In time series, random splitting can allow information from the future to influence training.

For example:

Architecture & Data Flow
Past ------------------------------> Future

Training data Test data
|----------------------|------------->

The model should learn from the past and be evaluated on data that occurs later.

This principle becomes especially important in Notebook 3 and Notebook 5.


4. Types of Time Series

Time series can be classified in several ways.

4.1 Univariate Time Series#

Only one variable is being modeled over time.

Example:

text
Date Sales 2025-01-01 120 2025-01-02 135 2025-01-03 128

Mathematically:

yty_t

4.2 Multivariate Time Series#

Multiple variables are observed over time.

Example:

text
Date Sales Price Advertising Temperature 2025-01-01 120 10.5 500 24 2025-01-02 135 10.0 700 25

Now we may model:

yt=f(x1,t,x2,t,,xk,t)y_t = f(x_{1,t}, x_{2,t}, \ldots, x_{k,t})

Multivariate time series become particularly useful when external variables help explain the target.

4.3 Regularly Spaced Time Series#

Observations occur at a consistent frequency:

text
00:00 01:00 02:00 03:00

4.4 Irregularly Spaced Time Series#

Observations occur at different times:

text
09:03 09:07 09:15 09:28

Irregular data may need special preprocessing depending on the model.


5. Time Series Components

A time series is often understood through four major components:

  1. Trend
  2. Seasonality
  3. Cyclicity
  4. Noise

These components are conceptual tools. A real dataset may not cleanly separate into four independent parts.


6. Trend

Trend represents the long-term direction of a series.

A trend can be:

  • Increasing
  • Decreasing
  • Approximately flat
  • Nonlinear

Example:

Architecture & Data Flow
Value
 ^
 | *
 | *
 | *
 | *
 | *
 +----------------------------> Time

A company whose sales grow steadily over several years has an upward trend.

Why Trend Matters#

Many forecasting models behave differently when a strong trend exists.

For example, ARIMA models often use differencing to remove non-stationary trend behavior.


7. Seasonality

Seasonality is a pattern that repeats at a known and fixed frequency.

Examples:

  • Retail sales increase every December.
  • Electricity demand changes every day.
  • Restaurant traffic is higher on weekends.
  • Website traffic follows a weekly pattern.

Suppose daily sales have a weekly seasonal pattern:

ytyt7y_t \approx y_{t-7}

The value seven days ago may therefore contain useful information.

Important Distinction#

Seasonality is tied to a known repeating calendar or frequency.

For example:

Mathematical Formulation
Daily data:
Weekly seasonality = 7

Monthly data:
Yearly seasonality = 12

Hourly data:
Daily seasonality = 24

The correct seasonal period depends on the sampling frequency and domain.


8. Cyclicity

Cycles are long-term rises and falls that do not necessarily repeat at a fixed frequency.

Examples:

  • Economic expansion and recession
  • Long-term business cycles
  • Industry demand cycles

The key distinction is:

Seasonality -> predictable fixed period Cyclicity -> variable / less regular duration

A cycle may last two years in one period and five years in another.

This makes cyclic behavior harder to model as simple seasonality.


9. Noise

Noise represents unpredictable variation that remains after systematic structure is removed.

Example:

Mathematical Formulation
Observed series
 =
Trend + Seasonality + Other structure + Noise

Noise is not necessarily "bad data."

Some variation is genuinely unpredictable.

The goal is not always to eliminate every fluctuation. The goal is to distinguish useful structure from random variation.


10. Additive vs Multiplicative Structure

A common conceptual decomposition is:

Additive#

yt=Tt+St+Ct+Rty_t = T_t + S_t + C_t + R_t

where:

  • TtT_t = trend
  • StS_t = seasonality
  • CtC_t = cyclic component
  • RtR_t = remainder/noise

Additive structure is useful when seasonal variation has roughly constant magnitude.

Multiplicative#

yt=Tt×St×Ct×Rty_t = T_t \times S_t \times C_t \times R_t

Multiplicative structure is useful when seasonal variation grows with the level of the series.

For example:

Low sales -> seasonal change of ±10 High sales -> seasonal change of ±100

A logarithmic transformation can sometimes convert multiplicative relationships into additive ones:

log(yt)log(Tt)+log(St)+\log(y_t) \approx \log(T_t) + \log(S_t) + \cdots

This will become important in the ARIMA notebook.


11. Time Series EDA Workflow

A useful exploratory workflow is:

Architecture & Data Flow
1. Load data
 |
2. Inspect timestamps
 |
3. Sort chronologically
 |
4. Check frequency
 |
5. Check missing dates
 |
6. Check missing values
 |
7. Visualize the raw series
 |
8. Investigate trend
 |
9. Investigate seasonality
 |
10. Examine rolling statistics
 |
11. Examine ACF/PACF
 |
12. Check stationarity
 |
13. Decompose the series
 |
14. Document findings

This workflow should become a habit.


12. Python Environment

We will primarily use:

🐍 Python
import numpy as np import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.seasonal import STL from statsmodels.graphics.tsaplots import plot_acf, plot_pacf from statsmodels.tsa.stattools import adfuller

For reproducible notebooks:

🐍 Python
pd.set_option("display.max_columns", None) pd.set_option("display.width", 120)

13. Create a Practice Time Series Dataset

To understand the concepts without depending on a specific external dataset, we can create a realistic synthetic daily series.

🐍 Python
import numpy as np import pandas as pd import matplotlib.pyplot as plt np.random.seed(42) dates = pd.date_range( start="2022-01-01", periods=3 * 365, freq="D" ) n = len(dates) trend = np.linspace(100, 180, n) weekly_seasonality = 12 * np.sin( 2 * np.pi * np.arange(n) / 7 ) yearly_seasonality = 8 * np.sin( 2 * np.pi * np.arange(n) / 365 ) noise = np.random.normal( loc=0, scale=5, size=n ) sales = ( trend + weekly_seasonality + yearly_seasonality + noise ) df = pd.DataFrame({ "date": dates, "sales": sales }) df.head()

14. Inspecting the Dataset

Always inspect the structure before analyzing it.

🐍 Python
df.shape
🐍 Python
df.info()
🐍 Python
df.head()
🐍 Python
df.describe()

Questions to ask:

  • Is the timestamp column correctly recognized?
  • Is the data sorted?
  • What is the frequency?
  • Are there missing values?
  • Are there duplicate timestamps?
  • Are there suspicious values?
  • What is the approximate range?

15. Datetime Handling

Convert timestamp columns explicitly.

🐍 Python
df["date"] = pd.to_datetime(df["date"])

Sort the data:

🐍 Python
df = df.sort_values("date")

Set the timestamp as the index:

🐍 Python
df = df.set_index("date")

Now:

🐍 Python
df.head()

The index should look like:

text
2022-01-01 2022-01-02 2022-01-03 ...

A proper datetime index makes time-based operations much easier.


16. Checking the Time Frequency

Inspect the time differences:

🐍 Python
df.index.to_series().diff().value_counts().head()

For daily data, you would typically see:

1 day

You can also inspect:

🐍 Python
pd.infer_freq(df.index)

If the frequency cannot be inferred, investigate why.

Possible reasons:

  • Missing dates
  • Duplicate timestamps
  • Irregular observations
  • Insufficient observations

17. Missing Dates vs Missing Values

These are not the same problem.

Suppose you expect daily data:

text
Jan 1 Jan 2 Jan 3 Jan 4

but receive:

text
Jan 1 Jan 2 Jan 4

January 3 is a missing date.

A missing value is different:

Jan 3 NaN

The timestamp exists, but the observation is missing.

Always check both.


18. Detect Missing Dates

For daily data:

🐍 Python
expected_dates = pd.date_range( start=df.index.min(), end=df.index.max(), freq="D" ) missing_dates = expected_dates.difference(df.index) missing_dates

If the result is empty:

No missing dates.

If dates appear, investigate them.


19. Reindexing to a Complete Frequency

If daily observations are expected:

🐍 Python
df = df.asfreq("D")

Missing dates will now appear as rows with missing values.

Check:

🐍 Python
df.isna().sum()

Do not automatically fill missing values.

The correct treatment depends on the meaning of the data.

Possible approaches include:

  • Forward fill
  • Backward fill
  • Interpolation
  • Domain-specific imputation
  • Leaving values missing
  • Removing affected observations

For example:

🐍 Python
df["sales"] = df["sales"].interpolate()

Interpolation should only be used when it makes sense for the underlying process.


20. Duplicate Timestamps

Check duplicates:

🐍 Python
df.index.duplicated().sum()

If duplicates exist:

🐍 Python
df[df.index.duplicated(keep=False)]

Duplicate timestamps may represent:

  • Multiple transactions at the same timestamp
  • Data ingestion errors
  • Multiple measurements
  • Different entities

Do not blindly remove duplicates. First understand what they mean.


21. First Visualization

Visualization should be one of the first analytical steps.

🐍 Python
plt.figure(figsize=(14, 5)) plt.plot(df.index, df["sales"]) plt.title("Daily Sales") plt.xlabel("Date") plt.ylabel("Sales") plt.show()

When looking at the plot, ask:

  • Is there an upward or downward trend?
  • Is the variance changing?
  • Is there visible seasonality?
  • Are there unusual spikes?
  • Are there structural breaks?
  • Are there long periods of missing data?

22. Zooming Into the Data

A full three-year chart may hide short-term patterns.

Look at a smaller window:

🐍 Python
df["sales"].iloc[:90].plot(figsize=(14, 5)) plt.title("First 90 Days") plt.xlabel("Date") plt.ylabel("Sales") plt.show()

Then inspect other periods.

This helps distinguish:

  • Short-term seasonality
  • Long-term trend
  • Outliers
  • Changes in behavior

23. Rolling Statistics

Rolling statistics calculate statistics over a moving window.

For example, a 7-day rolling mean:

RollingMeant=17i=06ytiRollingMean_t = \frac{1}{7} \sum_{i=0}^{6}y_{t-i}

In pandas:

🐍 Python
df["rolling_mean_7"] = df["sales"].rolling(7).mean()

Rolling standard deviation:

🐍 Python
df["rolling_std_7"] = df["sales"].rolling(7).std()

Visualize them:

🐍 Python
plt.figure(figsize=(14, 5)) plt.plot(df.index, df["sales"], label="Sales") plt.plot(df.index, df["rolling_mean_7"], label="7-Day Rolling Mean") plt.title("Sales and Rolling Mean") plt.legend() plt.show()

24. Why Rolling Statistics Matter

Rolling statistics help us investigate whether the statistical properties of a series change over time.

For example:

Architecture & Data Flow
Stable mean + stable variance
 |
 v
Potentially stationary behavior

versus:

Architecture & Data Flow
Changing mean
 |
 v
Possible trend / non-stationarity

and:

Architecture & Data Flow
Increasing variance
 |
 v
Possible changing volatility

Rolling statistics are exploratory tools, not definitive stationarity tests.


25. Rolling Window Size

The window controls how much history is used.

🐍 Python
df["rolling_mean_7"] = df["sales"].rolling(7).mean() df["rolling_mean_30"] = df["sales"].rolling(30).mean() df["rolling_mean_90"] = df["sales"].rolling(90).mean()

Interpretation:

  • 7-day window: short-term behavior
  • 30-day window: medium-term behavior
  • 90-day window: smoother long-term behavior

The appropriate window depends on the business and sampling frequency.


26. Stationarity

Stationarity is one of the most important concepts in classical time series analysis.

A weakly stationary series generally has:

  1. Constant mean over time.
  2. Constant variance over time.
  3. Autocovariance that depends on the lag, not the absolute time.

Informally:

The statistical behavior of the process does not systematically change over time.

A simple stationary-looking series might fluctuate around a stable level:

Architecture & Data Flow
Value
 ^
 | * * *
 | * * * *
 | * * *
 | * * *
 +------------------> Time

A trending series is generally non-stationary:

Architecture & Data Flow
Value
 ^
 | *
 | *
 | *
 | *
 | *
 +------------------> Time

27. Why Stationarity Matters

Several classical statistical models work best when the relevant series is stationary.

ARIMA, for example, explicitly includes differencing to transform a non-stationary series into a more stationary representation.

Stationarity also makes relationships between observations more stable over time.

However:

Not every forecasting problem requires the raw target itself to be stationary.

Modern machine-learning and deep-learning methods can work directly with non-stationary data, provided the modeling strategy handles temporal structure appropriately.


28. Trend and Stationarity

Suppose:

yt=100+2t+ϵty_t = 100 + 2t + \epsilon_t

The mean changes as time increases.

Therefore, the series is not stationary in the usual weak-sense framework.

Differencing can remove the deterministic trend:

Δyt=ytyt1\Delta y_t = y_t - y_{t-1}

If:

yt=100+2t+ϵty_t = 100 + 2t + \epsilon_t

then approximately:

Δyt=2+ϵtϵt1\Delta y_t = 2 + \epsilon_t - \epsilon_{t-1}

which may be much closer to stationary.


29. Differencing

First-order differencing:

🐍 Python
df["sales_diff"] = df["sales"].diff()

Visualize:

🐍 Python
df["sales_diff"].plot(figsize=(14, 5)) plt.title("First Difference of Sales") plt.xlabel("Date") plt.ylabel("Differenced Sales") plt.show()

Differencing is central to ARIMA and will be covered in depth in Notebook 2.


30. Autocorrelation

Autocorrelation measures the relationship between a time series and its previous values.

At lag kk:

ρk=Corr(yt,ytk)\rho_k = Corr(y_t, y_{t-k})

Examples:

  • Lag 1: relationship with yesterday
  • Lag 7: relationship with one week ago
  • Lag 30: relationship with approximately one month ago

Strong autocorrelation means past observations contain information about later observations.


31. Intuitive Example of Autocorrelation

Suppose:

Mathematical Formulation
Today's temperature ≈ Yesterday's temperature

Then lag-1 autocorrelation may be high.

For daily sales with weekly seasonality:

Mathematical Formulation
Monday sales ≈ Previous Monday sales

Then lag-7 autocorrelation may be strong.

This is one reason autocorrelation is useful for discovering temporal structure.


32. ACF Plot

The Autocorrelation Function (ACF) shows correlation between the series and lagged versions of itself.

🐍 Python
from statsmodels.graphics.tsaplots import plot_acf plot_acf( df["sales"].dropna(), lags=40 ) plt.show()

Interpretation:

  • Large correlation at lag 1 -> strong short-term dependence.
  • Large correlation at lag 7 -> possible weekly seasonality.
  • Repeating peaks -> possible seasonal structure.
  • Slowly declining ACF -> possible non-stationarity or persistent dependence.

ACF is diagnostic evidence, not proof of a particular model.


33. Partial Autocorrelation

PACF measures the relationship between yty_t and ytky_{t-k} after accounting for the effects of intermediate lags.

For example, PACF at lag 3 asks approximately:

How much direct relationship remains between today's value and the value three periods ago after accounting for lags 1 and 2?

Plot it:

🐍 Python
from statsmodels.graphics.tsaplots import plot_pacf plot_pacf( df["sales"].dropna(), lags=40, method="ywm" ) plt.show()

PACF is especially useful when thinking about autoregressive model orders.


34. ACF vs PACF

A useful intuition:

ToolMain Question
ACFHow related is the series to its past at different lags?
PACFWhat relationship remains at a lag after accounting for shorter lags?

In classical ARIMA modeling, ACF and PACF can provide clues about model structure.

We will use them much more heavily in Notebook 2.


35. Seasonal Decomposition

Decomposition attempts to separate a series into interpretable components.

A classical additive representation is:

yt=Tt+St+Rty_t = T_t + S_t + R_t

where:

  • TtT_t = trend
  • StS_t = seasonal component
  • RtR_t = remainder

STL stands for:

Seasonal-Trend decomposition using Loess.

It is a flexible decomposition method that can handle changing trend and seasonal behavior better than simple classical decomposition in many situations.


36. STL Decomposition

For daily data with weekly seasonality:

🐍 Python
from statsmodels.tsa.seasonal import STL series = df["sales"].dropna() stl = STL( series, period=7 ) result = stl.fit() result.plot() plt.show()

The output typically contains:

  • Observed
  • Trend
  • Seasonal
  • Residual

37. Understanding STL Output

Observed#

The original series.

Trend#

The estimated long-term movement.

Seasonal#

The repeating pattern associated with the selected period.

Residual#

What remains after estimated trend and seasonality are removed.

Conceptually:

Residualt=ObservedtTrendtSeasonaltResidual_t = Observed_t - Trend_t - Seasonal_t

for an additive decomposition.

The residual should not automatically be assumed to be pure white noise. It must be diagnosed.


38. Choosing the STL Period

The period parameter is extremely important.

Examples:

🐍 Python
STL(series, period=7)

for weekly seasonality in daily data.

🐍 Python
STL(series, period=12)

for yearly seasonality in monthly data.

🐍 Python
STL(series, period=24)

for daily seasonality in hourly data.

Choosing the wrong period can produce misleading decomposition results.

Domain knowledge and exploratory analysis should guide the choice.


39. Multiple Seasonalities

Real-world data can contain multiple seasonal patterns.

For example, hourly electricity demand may have:

  • Daily seasonality
  • Weekly seasonality
  • Annual seasonality

A single classical seasonal period may not fully represent such data.

This is an important limitation to understand before moving into more advanced forecasting methods.

Notebook 3 will discuss machine-learning approaches for engineered multiple seasonal features, while Notebook 5 will discuss more advanced forecasting architectures.


40. Resampling

Resampling changes the frequency of a time series.

Examples:

Architecture & Data Flow
Daily -> Weekly
Daily -> Monthly
Hourly -> Daily
Minute -> Hourly

For example, daily sales to monthly sales:

🐍 Python
monthly_sales = df["sales"].resample("ME").sum()

For average daily values:

🐍 Python
monthly_average = df["sales"].resample("ME").mean()

The aggregation function must match the meaning of the variable.


41. Choosing the Correct Aggregation

For a flow variable such as sales:

🐍 Python
monthly_sales = df["sales"].resample("ME").sum()

For a measurement such as temperature:

🐍 Python
monthly_temperature = df["temperature"].resample("ME").mean()

For an ending balance:

🐍 Python
monthly_balance = df["balance"].resample("ME").last()

For maximum daily demand:

🐍 Python
monthly_peak = df["demand"].resample("ME").max()

Never assume mean() is always correct.


42. Downsampling vs Upsampling

Downsampling#

Moving to a lower-frequency representation.

Example:

Hourly -> Daily

This generally requires aggregation.

Upsampling#

Moving to a higher-frequency representation.

Example:

Daily -> Hourly

This creates new timestamps.

For example:

🐍 Python
hourly = df["sales"].resample("h").asfreq()

The newly created observations may be missing and require appropriate treatment.

Upsampling does not magically create information that was never measured.


43. Time-Based Features

Before moving to machine learning, it is useful to understand calendar features.

🐍 Python
df["day_of_week"] = df.index.dayofweek df["day_of_month"] = df.index.day df["month"] = df.index.month df["quarter"] = df.index.quarter df["year"] = df.index.year

For example:

🐍 Python
df["is_weekend"] = df.index.dayofweek >= 5

These features can be valuable in machine-learning forecasting.

However, they should be created carefully so that features available at prediction time are not accidentally based on future information.


44. Time Series Outliers

An outlier is an observation that behaves unusually relative to the surrounding data.

Examples:

  • Sudden sales spike
  • Sensor malfunction
  • Website traffic explosion
  • Market crash
  • Data-entry error

Not every outlier should be removed.

An extreme observation may represent a real event.

A useful investigation asks:

  1. Is the observation valid?
  2. Is it a measurement error?
  3. Was there a real-world event?
  4. Would the same event occur again?
  5. Should the forecasting model learn this behavior?

45. Structural Breaks

A structural break occurs when the underlying behavior of the series changes.

Example:

text
Before event: Sales around 100–120 After event: Sales around 180–220

Possible causes:

  • New product launch
  • Economic shock
  • Policy change
  • Business expansion
  • Pandemic
  • Change in measurement system

A structural break can make a previously useful model perform poorly because the data-generating process has changed.


46. Missing Data Strategies

Common strategies include:

Forward Fill#

🐍 Python
df["sales"].ffill()

Useful when the previous value remains valid until a new observation arrives.

Backward Fill#

🐍 Python
df["sales"].bfill()

Uses the next available value.

Interpolation#

🐍 Python
df["sales"].interpolate()

Can be useful for smoothly varying measurements.

Model-Based Imputation#

A separate model estimates missing values.

The correct strategy depends on the domain.

Avoid filling missing values simply because a library function makes it easy.


47. Data Leakage in Time Series

Data leakage occurs when information unavailable at prediction time is used during training.

Example:

Architecture & Data Flow
Prediction date: January 10

Allowed:
January 1 -> January 9

Not allowed:
January 11 -> January 20

A common mistake is calculating features using the full dataset before splitting it into train and test periods.

For example, a centered rolling statistic can use future observations.

Avoid:

🐍 Python
df["rolling_mean"] = df["sales"].rolling( window=7, center=True ).mean()

for a forecasting feature unless the future values are genuinely available at prediction time.

For forecasting, causal features generally use past information:

🐍 Python
df["lag_7"] = df["sales"].shift(7)

This topic becomes critical in Notebook 3 and Notebook 5.


48. A Practical EDA Checklist

Before modeling a time series, check:

Timestamp#

  • Is the timestamp parsed correctly?
  • Is it sorted?
  • Are there duplicates?
  • What is the frequency?

Completeness#

  • Are dates missing?
  • Are values missing?
  • Are there gaps?

Distribution#

  • Minimum
  • Maximum
  • Mean
  • Standard deviation
  • Extreme values

Temporal Structure#

  • Trend
  • Seasonality
  • Cycles
  • Autocorrelation
  • Structural breaks

Statistical Properties#

  • Is the mean stable?
  • Is variance stable?
  • Does differencing help?
  • What does the ACF look like?
  • What does the PACF look like?

Modeling Readiness#

  • What is the forecasting target?
  • What information is available at prediction time?
  • What forecast horizon is required?
  • What baseline should be used?

49. Building a Reusable EDA Function

For practical projects, it is useful to automate basic checks.

🐍 Python
def time_series_summary(df, target): print("Shape:", df.shape) print("\nMissing values:") print(df[target].isna().sum()) print("\nDuplicate timestamps:") print(df.index.duplicated().sum()) print("\nDate range:") print(df.index.min(), "to", df.index.max()) print("\nInferred frequency:") print(pd.infer_freq(df.index)) print("\nDescriptive statistics:") print(df[target].describe())

Use it:

🐍 Python
time_series_summary(df, "sales")

This should be considered a starting point, not a replacement for investigation.


50. Stationarity Test: Augmented Dickey-Fuller

Visual inspection is useful, but statistical tests can provide additional evidence.

The Augmented Dickey-Fuller (ADF) test evaluates a null hypothesis associated with a unit root.

In simplified terms:

  • Null hypothesis: the series has a unit root / is non-stationary.
  • Alternative hypothesis: the series is stationary.

Example:

🐍 Python
from statsmodels.tsa.stattools import adfuller result = adfuller(df["sales"].dropna()) print("ADF Statistic:", result[0]) print("p-value:", result[1])

A commonly used interpretation is:

Architecture & Data Flow
p-value < 0.05
 -> reject the null hypothesis
 -> evidence supporting stationarity

p-value >= 0.05
 -> insufficient evidence to reject the null
 -> investigate possible non-stationarity

The threshold is not a universal law, and statistical tests should be interpreted alongside plots and domain knowledge.


51. ADF Test After Differencing

🐍 Python
sales_diff = df["sales"].diff().dropna() result = adfuller(sales_diff) print("ADF Statistic:", result[0]) print("p-value:", result[1])

If differencing substantially changes the evidence for stationarity, that is useful information for later ARIMA modeling.


52. White Noise

A white-noise series contains no predictable temporal structure in its mean.

A simplified white-noise process can be written as:

yt=ϵty_t = \epsilon_t

where:

E[ϵt]=0E[\epsilon_t] = 0

and observations are uncorrelated across time.

A useful intuition:

Architecture & Data Flow
Past value
 |
 X
 |
Future value

Knowing the past does not provide useful linear predictive information about the future.

For a well-fitted forecasting model, residuals should ideally behave approximately like white noise.

This will become especially important in Notebook 2.


53. What We Should Expect From Good Residuals

After fitting a model, we generally want residuals with:

  • Approximately zero mean
  • No obvious trend
  • No obvious seasonality
  • Little remaining autocorrelation
  • Stable variance where appropriate

Residual diagnostics answer:

Did the model successfully capture the predictable structure?

If strong autocorrelation remains, the model may have missed information.


54. A Complete Mini EDA Pipeline

A compact workflow might look like:

🐍 Python
# 1. Parse timestamp df["date"] = pd.to_datetime(df["date"]) # 2. Sort df = df.sort_values("date") # 3. Set index df = df.set_index("date") # 4. Inspect frequency print(pd.infer_freq(df.index)) # 5. Check missing values print(df.isna().sum()) # 6. Plot original series df["sales"].plot(figsize=(14, 5)) plt.show() # 7. Rolling statistics rolling_mean = df["sales"].rolling(7).mean() rolling_std = df["sales"].rolling(7).std() # 8. ACF plot_acf(df["sales"].dropna(), lags=40) plt.show() # 9. PACF plot_pacf(df["sales"].dropna(), lags=40, method="ywm") plt.show() # 10. STL stl = STL(df["sales"].dropna(), period=7) result = stl.fit() result.plot() plt.show() # 11. Stationarity test adf_result = adfuller(df["sales"].dropna()) print("ADF Statistic:", adf_result[0]) print("p-value:", adf_result[1])

55. Common Beginner Mistakes

Mistake 1: Random Train/Test Splitting#

Bad for ordinary forecasting scenarios:

🐍 Python
train_test_split(X, y)

because observations from the future may enter the training set.

Use chronological splits.

Mistake 2: Ignoring Missing Dates#

A dataset can have no NaN values but still have missing timestamps.

Always check the expected frequency.

Mistake 3: Treating Every Spike as an Outlier#

A spike may represent a genuine business event.

Mistake 4: Assuming Every Time Series Is Stationary#

Many real-world series contain trends, seasonality, changing variance, or structural breaks.

Mistake 5: Blindly Differencing#

Differencing can remove useful information and can be overused.

Mistake 6: Using Future Information in Features#

Forecasting features must respect the information available at prediction time.

Mistake 7: Choosing Seasonal Period Arbitrarily#

The period should be justified by data frequency and domain knowledge.


56. Beginner Mental Model

Think about time series in this order:

Architecture & Data Flow
TIME
 |
 v
What happened?
 |
 v
TREND
 |
 v
Does something repeat?
 |
 v
SEASONALITY
 |
 v
Does it move in irregular long cycles?
 |
 v
CYCLICITY
 |
 v
What remains?
 |
 v
NOISE

Then ask:

Architecture & Data Flow
Is the statistical behavior stable?
 |
 Yes
 |
 v
 Model structure

 OR

 No
 |
 v
Transform / difference /
engineer features / choose
an appropriate model

57. Advanced Perspective

For advanced practitioners, the important idea is that a time series is not merely a column containing dates and numbers.

It is an observed realization of an underlying stochastic process.

We can write:

Yt=f(Yt1,Yt2,,Xt,ϵt)Y_t = f(Y_{t-1},Y_{t-2},\ldots,X_t,\epsilon_t)

where:

  • YtY_t is the target at time tt.
  • Past values may influence the present.
  • XtX_t represents external variables.
  • ϵt\epsilon_t represents unpredictable variation.

Different forecasting families make different assumptions about this relationship.

Statistical Models#

ARIMA-style models explicitly model temporal dependence and transformations.

Machine Learning#

Tree-based models learn relationships from engineered temporal features.

Deep Learning#

RNNs, LSTMs, GRUs, and Transformers can learn complex sequential representations.

The next notebooks will build these approaches progressively.


58. What We Have Learned

In this notebook we learned:

  • Time series observations are ordered in time.
  • Time order creates dependencies that ordinary tabular ML does not necessarily have.
  • Time series can be univariate or multivariate.
  • Trend describes long-term movement.
  • Seasonality describes repeating patterns at known frequencies.
  • Cyclicity describes less regular long-term fluctuations.
  • Noise represents unpredictable variation.
  • Stationarity describes stable statistical behavior.
  • Rolling statistics help investigate changing mean and variance.
  • ACF measures relationships across lags.
  • PACF examines lag relationships after accounting for intermediate lags.
  • STL separates a series into trend, seasonal, and residual components.
  • Resampling changes the temporal frequency.
  • Missing timestamps and missing values are different problems.
  • Data leakage is especially dangerous in forecasting.
  • Residuals should be investigated rather than automatically treated as noise.

59. Exercises

Exercise 1: Basic Inspection#

Given a DataFrame containing:

date sales

perform:

  1. Datetime conversion.
  2. Sorting.
  3. Indexing.
  4. Frequency detection.
  5. Missing-value detection.
  6. Duplicate timestamp detection.

Exercise 2: Visualization#

Create:

  1. Full-series plot.
  2. First 90-day plot.
  3. 7-day rolling mean.
  4. 30-day rolling mean.

Explain what each visualization tells you.


Exercise 3: Stationarity#

  1. Plot the original series.
  2. Run the ADF test.
  3. Difference the series.
  4. Plot the differenced series.
  5. Run ADF again.
  6. Compare the results.

Exercise 4: ACF and PACF#

Generate:

🐍 Python
plot_acf(...) plot_pacf(...)

for:

  • Original series
  • Differenced series

Explain how the patterns changed.


Exercise 5: STL#

Perform STL decomposition using the appropriate seasonal period.

Answer:

  • Is there a visible trend?
  • Is seasonality strong?
  • Is the seasonal pattern stable?
  • Does the residual contain obvious structure?

60. Mini Project: Time Series EDA Report

Choose a real-world time series dataset such as:

  • Retail sales
  • Electricity demand
  • Temperature
  • Website traffic
  • Stock prices
  • Transportation demand

Your report should contain:

Section A: Data Understanding#

  • Dataset description
  • Time range
  • Frequency
  • Target variable
  • Number of observations

Section B: Data Quality#

  • Missing timestamps
  • Missing values
  • Duplicate timestamps
  • Outliers
  • Irregular intervals

Section C: Visualization#

  • Original series
  • Rolling mean
  • Rolling standard deviation

Section D: Temporal Structure#

  • Trend
  • Seasonality
  • Cyclic behavior
  • ACF
  • PACF

Section E: Decomposition#

Perform STL and interpret:

  • Trend
  • Seasonal
  • Residual

Section F: Stationarity#

  • ADF test
  • Differenced series if appropriate
  • Interpretation

Section G: Modeling Recommendation#

Based on your EDA, explain whether you would initially consider:

  • ARIMA/SARIMA
  • Tree-based ML
  • Deep learning
  • A combination of approaches

Do not train the final forecasting model yet. The purpose of this project is to develop the analytical reasoning required before modeling.


61. Preparation for Notebook 2

The next notebook will move from exploration to statistical forecasting.

We will build the concepts in this order:

Architecture & Data Flow
Time Series EDA
 |
 v
Stationarity
 |
 v
Differencing
 |
 v
White Noise
 |
 v
AR
 |
 v
MA
 |
 v
ARMA
 |
 v
ARIMA
 |
 v
SARIMA
 |
 v
Residual Diagnostics
 |
 v
Forecasting

The goal is not simply to memorize ARIMA syntax.

The goal is to understand:

Why a particular statistical model is appropriate for a particular temporal structure.


62. Final Takeaway

Time series forecasting starts long before model training.

A strong practitioner first asks:

  1. What exactly is being measured?
  2. How is time represented?
  3. Is the frequency regular?
  4. Are there missing dates?
  5. Is there a trend?
  6. Is there seasonality?
  7. Is there autocorrelation?
  8. Is the series stationary?
  9. Are there structural breaks or unusual events?
  10. What information will actually be available at prediction time?

Only after answering these questions should we choose a forecasting model.

The central principle of this course is:

text
Understand the temporal structure ↓ Prepare the data correctly ↓ Choose the appropriate model ↓ Validate chronologically ↓ Diagnose errors ↓ Deploy and monitor

This foundation will support every forecasting approach covered in the remaining notebooks.

Knowledge Checkpoint

Time Series Stationarity & EDA Checkpoint

Q1.What statistical test is standard for determining whether a time series has a unit root (is non-stationary)?
AAugmented Dickey-Fuller (ADF) Test
BStudent's t-Test
CANOVA Test
DShapiro-Wilk Test
Q2.What defines a Weakly (Covariance) Stationary time series?
AConstant mean, constant variance, and autocovariance depending solely on the time lag between points rather than actual time $t$.
BA strictly increasing linear trend.
CA time series with zero noise.
DData that repeats every 24 hours.
Q3.What tool helps identify the order of the Autoregressive (AR) component $p$ in an ARIMA model?
APartial Autocorrelation Function (PACF) plot
BHistogram distribution
CScatter plot matrix
DBox plot
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.