Data Analysis for Engineers/Module 4

Module 4 · Section 7 of 10

Lesson 4.6 - Time Series Visualization

Target: ~9 min read - 20 min hands-on

Overview

Dual y-axis charts overlay two differently-scaled series (energy in kWh and temperature in C) on a shared timeline. Area charts emphasize magnitude/accumulation. Seasonal decomposition splits a time series into trend, seasonal, and residual components.

Why This Matters (Engineering Context)

A dual-axis chart pairing energy with temperature is a standard way to communicate why consumption rises to a non-technical stakeholder - and seasonal decomposition quantifies a pattern you suspect is there.

Code-Along

from statsmodels.tsa.seasonal import seasonal_decompose

# Roll the daily series up to monthly: to_period("M") -> to_timestamp() gives a
# clean month-start date to group on.
m = site1.assign(month=site1["date"].dt.to_period("M").dt.to_timestamp())
monthly_energy = m.groupby("month")["energy_kwh"].sum()
monthly_temp = m.groupby("month")["ambient_c"].mean()

# --- Dual y-axis: ax2 = ax1.twinx() shares the x-axis but has its own y-scale ---
fig, ax1 = plt.subplots(figsize=(10, 5))
ax1.plot(monthly_energy.index, monthly_energy.values, color="steelblue", label="Energy (kWh)")
ax1.set_ylabel("Energy (kWh)", color="steelblue"); ax1.set_xlabel("Month")
ax2 = ax1.twinx()
ax2.plot(monthly_temp.index, monthly_temp.values, color="firebrick", linestyle="--", label="Avg Temp (C)")
ax2.set_ylabel("Avg Temp (C)", color="firebrick")
fig.suptitle("Charlie DC Monthly Energy vs Ambient Temperature")
plt.tight_layout(); plt.show()

# --- Area chart: fill_between shades the region under the line ---
fig, ax = plt.subplots(figsize=(10, 4))
ax.fill_between(monthly_energy.index, monthly_energy.values, color="skyblue", alpha=0.6)
ax.plot(monthly_energy.index, monthly_energy.values, color="steelblue")
ax.set_title("Charlie DC Monthly Energy (Area Chart)"); ax.set_ylabel("Energy (kWh)")
plt.tight_layout(); plt.show()

# --- Seasonal decomposition: split a series into trend + seasonal + residual ---
# asfreq("D") forces a regular daily index; interpolate() fills any gaps it exposes.
daily = site1.set_index("date")["energy_kwh"].asfreq("D").interpolate()
decomposition = seasonal_decompose(daily, model="additive", period=7)   # period=7 -> weekly cycle
fig = decomposition.plot(); fig.set_size_inches(9, 7)
plt.tight_layout(); plt.show()

Run it: the dual-axis chart shows energy and temperature rising and falling together through the year. The period=7 seasonal decomposition isolates the weekday/weekend cycle as the seasonal component, a slow trend, and a residual.

Practice Exercises

  1. Change the decomposition period from 7 to 30 and compare the seasonal component - which cycle does it capture now?
  2. Compute the correlation between monthly_energy and monthly_temp.
  3. Build a dual-axis chart for a different site (e.g. "Bravo Fab") - energy vs temperature by month.
# Try the practice exercises here

Knowledge Check

  1. When is a dual y-axis chart appropriate?
  2. What three components does seasonal decomposition split a series into?
  3. What's the risk of a dual-axis chart if the axes aren't clearly labeled and color-coded?
Answer key
  1. When comparing two series with different units or scales on the same timeline
  2. Trend, seasonal, and residual
  3. Readers misread which line belongs to which axis, getting a misleading impression

40 / 63 sections · Course home · Join the coaching cohort