A dual-axis time series plot overlays two related time-dependent datasets in the same figure, mapping each series to its own y-axis while sharing a common x-axis.
A dual-axis (or secondary-axis) time-series chart is a powerful visualization that lets you compare two variables with different value ranges or units across time. In Matplotlib, you create one set of axes, plot the first series, and then call ax.twinx()
(or ax.twiny()
) to spawn a second axis that shares the same x-axis. Properly labelled, colored, and scaled, the resulting figure communicates relationships that would be hard to see on separate plots.
Figure
and Axes
with plt.subplots()
.ax2 = ax.twinx()
.ax2
.ax2.set_ylim()
.Time-series require a datetime index. Use pandas
to parse dates and handle missing values. Resample if necessary to minimise clutter.
Users often fail to keep axis colors consistent with their respective line colors. A best practice is to set the y-axis spine, ticks, and label to match the plot color. This minimizes cognitive load.
Dual grids can confuse. Enable the primary axis grid only (ax.grid(True)
) and disable it on the twin axis to prevent a tangled lattice.
Matplotlib cannot combine legends from separate axes automatically. Combine handles manually:
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax.legend(lines+lines2, labels+labels2, loc="upper left")
Use matplotlib.dates
formatters to avoid overlapping ticks. Rotate labels by 30–45° if needed and set ax.xaxis.set_major_locator
for monthly or quarterly intervals.
Always call fig.tight_layout()
or fig.autofmt_xdate()
before saving. For publications, save as SVG or PDF to preserve vector quality.
matplotlib.cm.tab10
) improve accessibility.Below we plot daily average temperature (°C) and electricity demand (MWh) for a city over one year:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# Sample dataset
df = pd.read_csv("city_energy_temp.csv", parse_dates=["date"]).set_index("date")
fig, ax = plt.subplots(figsize=(10, 5))
color1 = "tab:blue"
color2 = "tab:red"
# Primary axis: Temperature
a1 = ax.plot(df.index, df["temp_c"], color=color1, label="Temperature (°C)")
ax.set_ylabel("Temperature (°C)", color=color1, fontsize=12)
ax.tick_params(axis="y", labelcolor=color1)
# Secondary axis: Demand
ax2 = ax.twinx()
a2 = ax2.plot(df.index, df["demand_mwh"], color=color2, label="Demand (MWh)")
ax2.set_ylabel("Demand (MWh)", color=color2, fontsize=12)
ax2.tick_params(axis="y", labelcolor=color2)
# Date formatting
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
fig.autofmt_xdate()
# Combined legends
lines = a1 + a2
labels = [l.get_label() for l in lines]
ax.legend(lines, labels, loc="upper left")
ax.set_title("Daily Temperature vs. Power Demand – 2023")
fig.tight_layout()
plt.show()
Problem: Viewers cannot instantly tell which y-axis belongs to which line.
Fix: Match the spine, tick, and label colors to the data line via ax.spines["left"].set_color(color1)
and ax2.spines["right"].set_color(color2)
.
Problem: Default y-limits exaggerate minor fluctuations.
Fix: Set explicit limits or use ax.margins()
and ax2.margins()
to add proportional padding.
Problem: Overlapping or excessive date ticks.
Fix: Use mdates.AutoDateLocator
or manually set monthly/quarterly locators and call fig.autofmt_xdate()
.
While Galaxy is primarily a modern SQL editor, engineering and analytics teams often export query results to Python for advanced visualizations like dual-axis plots. Galaxy’s AI copilot can write the SQL needed to produce the temperature and demand dataset above. After executing and downloading the CSV inside Galaxy, you can move directly into a Python environment to craft the dual-axis chart with Matplotlib, bridging fast data extraction with insightful visualization.
Dual-axis time-series plots condense two complementary stories into a single, elegant figure. With Matplotlib’s twinx()
method, careful styling, and attention to axis alignment, you can deliver persuasive visual narratives without overwhelming your audience.
Analysts frequently need to compare two metrics over time, each with different units—think revenue (USD) and web traffic (visits). Plotting them on the same chart with separate y-axes saves space, highlights correlations, and accelerates decision-making. Matplotlib’s twin axis functionality is therefore a staple skill for data engineers and scientists who craft dashboards, reports, and anomaly detections.
It is a method that creates a new y-axis sharing the same x-axis as the original. This allows you to overlay a second dataset with its own scale on the same time axis.
Technically yes by repeatedly calling twinx()
and adjusting spines, but it is discouraged because it quickly becomes unreadable. Stick to two y-axes for clarity.
Manually set y-limits of both axes so that 0 is positioned at the same pixel height. Use the ratio (0 - ymin) / (ymax - ymin)
to compute matching limits.
Galaxy focuses on query editing and collaboration. While it does not render dual-axis plots within the SQL editor today, you can export query results and use Matplotlib (or other Python tools) to build the visualization.