How to Plot Time Series Data in Python: matplotlib, pandas, and plotly

Time series data—sensor readings, climate measurements, stock prices, patient vitals—needs special handling in matplotlib because the x-axis carries dates rather than plain numbers. Matplotlib understands Python datetime objects and pandas DatetimeIndex natively, but formatting the axis cleanly requires a few extra lines.
Date formatting
mdates.DateFormatter controls tick labels. fig.autofmt_xdate() rotates them automatically.
Rolling average
Overlay a rolling mean to show the trend without hiding the raw signal. Keep both lines on the same axis.
Dual axes
Use ax.twinx() only when two variables share the same time axis but have incompatible scales or units.
1. Basic time series plot
Plot the series with ax.plot(dates, values). Apply mdates.MonthLocator to place one tick per month and DateFormatter to control the label text.
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
dates = pd.date_range("2024-01-01", periods=90, freq="D")
values = pd.Series(range(90), index=dates) + pd.Series(
[i * 0.3 for i in range(90)], index=dates
)
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(dates, values, lw=1.5, color="#6b21a8")
ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
fig.autofmt_xdate()
ax.set_ylabel("Measurement")
ax.set_title("Daily Time Series")
plt.tight_layout()2. Rolling averages
Use Series.rolling(window).mean() from pandas. Plot the raw signal at low alpha and the rolling means at full opacity so the trend is readable.
Try it
Try it now: plot your own time series
Upload temporal data and generate a clean time-series figure with trend smoothing and readable date formatting.
Generate my time-series chart →Newsletter
Get a weekly Python plotting tip
One concise tip each week for cleaner, faster scientific figures. Built for researchers who publish.
import pandas as pd
import matplotlib.pyplot as plt
dates = pd.date_range("2024-01-01", periods=180, freq="D")
rng = pd.Series(range(180), index=dates).add(
pd.Series([i ** 0.5 for i in range(180)], index=dates) * 0.5
)
rolling_7 = rng.rolling(7).mean()
rolling_30 = rng.rolling(30).mean()
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(dates, rng, alpha=0.25, color="#6b21a8", lw=1, label="Raw")
ax.plot(dates, rolling_7, color="#f59e0b", lw=1.5, label="7-day mean")
ax.plot(dates, rolling_30, color="#10b981", lw=2, label="30-day mean")
ax.legend(frameon=False)
ax.set_ylabel("Measurement")
fig.autofmt_xdate()
plt.tight_layout()3. Dual y-axis (two variables, shared x)
Use a second axis sparingly — only when two datasets share a meaningful time relationship but have incompatible units. Colour-code both the lines and the axis labels to make the mapping unambiguous.
import pandas as pd
import matplotlib.pyplot as plt
dates = pd.date_range("2024-01-01", periods=60, freq="D")
temp = pd.Series([20 + i * 0.3 for i in range(60)], index=dates)
precip = pd.Series([5 - i * 0.05 for i in range(60)], index=dates)
fig, ax1 = plt.subplots(figsize=(9, 4))
ax2 = ax1.twinx()
ax1.plot(dates, temp, color="#6b21a8", lw=2, label="Temperature (°C)")
ax2.bar(dates, precip, color="#f59e0b", alpha=0.4, width=0.8, label="Precipitation (mm)")
ax1.set_ylabel("Temperature (°C)", color="#6b21a8")
ax2.set_ylabel("Precipitation (mm)", color="#f59e0b")
fig.autofmt_xdate()
plt.tight_layout()Date tick locator reference
| Locator | Use when |
|---|---|
| HourLocator | Data spans hours or a single day |
| DayLocator | Data spans days to a few weeks |
| WeekdayLocator | Weekly business data |
| MonthLocator | Data spans months to a few years |
| YearLocator | Data spans many years or decades |
| AutoDateLocator | Let matplotlib choose automatically (default) |
Common mistakes
- Overlapping tick labels — call
fig.autofmt_xdate()or rotate manually withplt.xticks(rotation=45). - Plotting integers instead of dates — ensure your x-axis is a
DatetimeIndexordatetimearray, not a string column. - Hiding raw data with only a smoothed line— show the underlying signal at low opacity alongside the rolling average.
- Dual axes without colour coding — readers cannot tell which line belongs to which axis without clear colour cues.
Upload a CSV with a date column and describe the time series plot you need — Plotivy generates the code with correct date formatting, rolling averages, and publication-ready styling.
Plot your time series in PlotivyRelated chart guides
Apply this tutorial directly in the chart gallery with ready-to-run prompts and examples.
Technique guides scientists read next
scipy.signal.find_peaks guide
Tune prominence and width parameters for robust peak extraction.
Savitzky-Golay smoothing
Reduce noise while preserving peak shape and position.
PCA visualization workflow
Move from high-dimensional measurements to interpretable components.
ANOVA with post-hoc brackets
Add statistically correct pairwise significance annotations.
Found this helpful? Share it with your network.
Experimental Physicist & Photonics Researcher
Hands-on experience in silicon photonics, semiconductor fabrication (DRIE/ICP-RIE), optical simulation, and data-driven analysis. Built Plotivy to help researchers focus on discoveries instead of data struggles.
More about the authorVisualize your own data
Apply the techniques from this article to your own datasets. Upload CSV, Excel, or paste data directly.