What Is Time Series Data?
A time series is a sequence of data points recorded at successive, evenly-spaced points in time. Stock prices sampled daily, hourly website traffic, monthly sales revenue, sensor readings every second — all of these are time series. What distinguishes time series analysis from standard statistical analysis is the temporal ordering of observations: the order matters, adjacent values are typically correlated, and past values often contain information about future ones.
Time series analysis is one of the most practical skill areas for data analysts. Nearly every business tracks metrics over time — revenue, active users, inventory levels, customer satisfaction scores — and stakeholders consistently want to understand trends, detect anomalies, and forecast what comes next. This article covers the foundational concepts, decomposition, stationarity, key models, and how to get started in Python.
Core Components of a Time Series
Most time series can be decomposed into four components that combine to produce the observed values.
Component | Description | Example |
|---|---|---|
Trend | Long-term upward or downward movement | Steady growth in monthly active users over years |
Seasonality | Regular, repeating patterns tied to calendar periods | Retail sales peaking every December |
Cyclicality | Irregular fluctuations over longer, non-fixed periods | Business cycle expansions and recessions |
Residual (Noise) | Random variation after removing trend and seasonality | Day-to-day fluctuations around a trend line |
Understanding which components are present shapes the analysis approach. A series with strong seasonality requires seasonal decomposition and seasonal models. A series with a clear trend may need differencing before modeling. A series dominated by noise may not be forecastable at all.
Decomposition
Decomposition separates a time series into its constituent components, making it easier to understand and model each part independently. The two main decomposition models are additive (observed = trend + seasonality + residual) and multiplicative (observed = trend × seasonality × residual). The additive model is appropriate when the seasonal fluctuations are roughly constant in magnitude; the multiplicative model is appropriate when they grow proportionally with the trend.
import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
df = pd.read_csv('monthly_sales.csv', parse_dates=['date'], index_col='date')
result = seasonal_decompose(df['sales'], model='multiplicative', period=12)
result.plot()
plt.tight_layout()
plt.show()
Examining the decomposition plot reveals whether the trend is linear or accelerating, whether seasonality is stable or evolving, and whether the residuals look like white noise (which suggests the model has captured the main patterns) or still contain structure (which suggests additional modeling is needed).
Stationarity
Most classical time series models assume the series is stationary — meaning its statistical properties (mean, variance, autocorrelation) do not change over time. A series with a trend or changing variance is non-stationary and typically needs transformation before applying models like ARIMA.
The Augmented Dickey-Fuller (ADF) test formally tests for stationarity. The null hypothesis is that the series has a unit root (is non-stationary):
from statsmodels.tsa.stattools import adfuller
result = adfuller(df['sales'])
print(f'ADF Statistic: {result[0]:.4f}')
print(f'p-value: {result[1]:.4f}')
if result[1] < 0.05:
print('Series is stationary (reject H0)')
else:
print('Series is non-stationary (fail to reject H0)')
If the series is non-stationary, differencing is the standard remedy — subtracting each value from the previous one to remove the trend. First-order differencing handles linear trends; second-order differencing handles quadratic trends:
df['sales_diff'] = df['sales'].diff()
df['sales_diff2'] = df['sales'].diff().diff()
Autocorrelation and Partial Autocorrelation
Autocorrelation measures the correlation of a series with its own lagged values. The Autocorrelation Function (ACF) plot shows correlations at all lags simultaneously. The Partial Autocorrelation Function (PACF) shows the direct correlation at each lag after removing the influence of shorter lags. Together, ACF and PACF plots are used to identify the appropriate orders for ARIMA models.
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
plot_acf(df['sales_diff'].dropna(), lags=24, ax=axes[0])
plot_pacf(df['sales_diff'].dropna(), lags=24, ax=axes[1])
plt.tight_layout()
plt.show()
A sharp cutoff in the ACF after lag q suggests a MA(q) process. A sharp cutoff in the PACF after lag p suggests an AR(p) process. Both tailing off gradually suggests an ARMA(p,q) process.
ARIMA and SARIMA Models
ARIMA (AutoRegressive Integrated Moving Average) is the most widely used classical forecasting model. It combines three components: AR(p) uses past values to predict future ones, I(d) applies differencing d times to achieve stationarity, and MA(q) uses past forecast errors.
from statsmodels.tsa.arima.model import ARIMA
model = ARIMA(df['sales'], order=(1, 1, 1))
result = model.fit()
print(result.summary())
forecast = result.forecast(steps=12)
print(forecast)
SARIMA extends ARIMA with seasonal terms: SARIMA(p,d,q)(P,D,Q,m) where m is the seasonal period (12 for monthly data, 7 for daily data with weekly seasonality). For model selection, information criteria like AIC (Akaike Information Criterion) and BIC (Bayesian Information Criterion) help compare models — lower values indicate better fit with appropriate complexity penalization.
Modern Forecasting: Prophet
Facebook's Prophet library provides a practical, robust forecasting tool that handles trends, seasonality, holidays, and missing data with minimal configuration. It is especially well-suited for business time series with strong seasonal effects and irregular events:
from prophet import Prophet
prophet_df = df.reset_index().rename(columns={'date': 'ds', 'sales': 'y'})
model = Prophet(yearly_seasonality=True, weekly_seasonality=False, daily_seasonality=False)
model.fit(prophet_df)
future = model.make_future_dataframe(periods=12, freq='M')
forecast = model.predict(future)
model.plot(forecast)
Prophet is more forgiving than ARIMA for analysts who are not time series specialists — it automatically detects changepoints in the trend, handles multiple seasonality periods, and produces uncertainty intervals that are easy to communicate to stakeholders.
Anomaly Detection in Time Series
A common analyst task is identifying anomalies — points that deviate significantly from expected behavior. Simple approaches use statistical control limits: if a value falls more than 2 or 3 standard deviations from a rolling mean, flag it as anomalous.
window = 30
df['rolling_mean'] = df['sales'].rolling(window).mean()
df['rolling_std'] = df['sales'].rolling(window).std()
df['upper'] = df['rolling_mean'] + 3 * df['rolling_std']
df['lower'] = df['rolling_mean'] - 3 * df['rolling_std']
df['anomaly'] = (df['sales'] > df['upper']) | (df['sales'] < df['lower'])
anomalies = df[df['anomaly']]
print(f'Found {len(anomalies)} anomalies')
More sophisticated methods include Isolation Forest, STL-based outlier detection, and learned models that compare predictions against actuals. The right approach depends on whether anomalies are expected to be point anomalies (single spikes), contextual anomalies (unusual given the time of year), or collective anomalies (patterns that are unusual together).
Choosing the Right Time Series Approach
Scenario | Recommended Approach |
|---|---|
Short series, strong seasonality, business context | Prophet |
Stationary or easily differenced series | ARIMA / SARIMA |
Multiple related series (e.g. many store locations) | Vector AR or hierarchical models |
Long series with complex patterns | LSTM, Temporal Fusion Transformer |
Trend and seasonality visualization | STL decomposition |
Quick anomaly flagging | Rolling z-score, IQR-based bounds |
Common Pitfalls
Several mistakes frequently arise when analysts first work with time series data. Data leakage is the most serious: using future information when training a model (for example, including the target variable's future values as a feature) produces models that appear highly accurate in evaluation but fail completely in production. Always use a strict time-based train/test split — never shuffle time series data randomly as you would with cross-sectional data.
Ignoring stationarity leads to spurious regression results — two unrelated trending series will appear strongly correlated simply because they both trend upward. Always test and transform for stationarity before fitting classical models. Overfitting to noise by using too many lags or too complex a model produces predictions that match historical data closely but generalize poorly. Forecast evaluation on a held-out test period — ideally a rolling window evaluation — is essential for honest model assessment.
Create a free reader account to keep reading.