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
- Change the decomposition
periodfrom 7 to 30 and compare the seasonal component - which cycle does it capture now? - Compute the correlation between
monthly_energyandmonthly_temp. - 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
- When is a dual y-axis chart appropriate?
- What three components does seasonal decomposition split a series into?
- What's the risk of a dual-axis chart if the axes aren't clearly labeled and color-coded?
Answer key
- When comparing two series with different units or scales on the same timeline
- Trend, seasonal, and residual
- Readers misread which line belongs to which axis, getting a misleading impression
40 / 63 sections · Course home · Join the coaching cohort