How to Create a Step Plot in Matplotlib
Step plots visualize data as a series of horizontal and vertical segments, creating a staircase pattern. Unlike line plots that interpolate smoothly between points, step plots maintain constant...
Key Insights
- Step plots display data as discrete changes rather than continuous transitions, making them ideal for representing values that remain constant between measurements like temperature readings, stock prices, or digital signals
- The
whereparameter (‘pre’, ‘post’, ‘mid’) controls step positioning and fundamentally changes how your data appears—use ‘post’ for values that change at measurement time, ‘pre’ for values that apply until the next measurement - Step plots prevent the misleading interpolation that line plots create with discrete data, showing the true nature of your measurements without suggesting smooth transitions that don’t exist
Introduction to Step Plots
Step plots visualize data as a series of horizontal and vertical segments, creating a staircase pattern. Unlike line plots that interpolate smoothly between points, step plots maintain constant values between data points, making them essential for representing discrete changes.
You’ll need step plots when working with data that doesn’t transition gradually. Temperature readings from a thermostat, digital signal processing, cumulative distribution functions, and inventory levels all involve values that jump from one state to another. Using a standard line plot for this data implies smooth transitions that don’t exist in reality.
Matplotlib provides the plt.step() function specifically for this purpose. It’s part of the standard pyplot interface and shares many parameters with plt.plot(), making it familiar if you’ve worked with basic plotting before.
Basic Step Plot Creation
The fundamental syntax is straightforward: plt.step(x, y). The function draws horizontal lines at each y-value and vertical lines connecting them.
import matplotlib.pyplot as plt
import numpy as np
# Temperature readings throughout a day (hourly)
hours = np.array([0, 6, 12, 18, 24])
temperature = np.array([15, 14, 22, 20, 16])
plt.figure(figsize=(10, 6))
plt.step(hours, temperature, linewidth=2)
plt.xlabel('Hour of Day', fontsize=12)
plt.ylabel('Temperature (°C)', fontsize=12)
plt.title('Daily Temperature Changes', fontsize=14)
plt.grid(True, alpha=0.3)
plt.xticks(range(0, 25, 3))
plt.tight_layout()
plt.show()
This creates a step plot showing how temperature remains constant between measurements. The horizontal segments represent the actual measured values, while vertical segments show the instantaneous changes when new measurements occur.
Step Plot Styles and Where Parameter
The where parameter is crucial—it determines where the step occurs relative to your data points. This isn’t just aesthetic; it represents fundamentally different interpretations of your data.
- ‘post’: Steps occur after each x-value (default). Use when the value changes at the measurement time.
- ‘pre’: Steps occur before each x-value. Use when the value applies until the next measurement.
- ‘mid’: Steps occur at the midpoint between x-values. Use for centered representations.
import matplotlib.pyplot as plt
import numpy as np
x = np.array([1, 2, 3, 4, 5])
y = np.array([1, 3, 2, 4, 3])
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# Post: step after the point
axes[0].step(x, y, where='post', linewidth=2, color='blue')
axes[0].plot(x, y, 'o', color='blue', markersize=8)
axes[0].set_title("where='post' (default)", fontsize=12)
axes[0].grid(True, alpha=0.3)
axes[0].set_xlabel('X')
axes[0].set_ylabel('Y')
# Pre: step before the point
axes[1].step(x, y, where='pre', linewidth=2, color='green')
axes[1].plot(x, y, 'o', color='green', markersize=8)
axes[1].set_title("where='pre'", fontsize=12)
axes[1].grid(True, alpha=0.3)
axes[1].set_xlabel('X')
# Mid: step at midpoint
axes[2].step(x, y, where='mid', linewidth=2, color='red')
axes[2].plot(x, y, 'o', color='red', markersize=8)
axes[2].set_title("where='mid'", fontsize=12)
axes[2].grid(True, alpha=0.3)
axes[2].set_xlabel('X')
plt.tight_layout()
plt.show()
Choose ‘post’ for most real-world scenarios like sensor readings or price changes where the new value takes effect immediately. Use ‘pre’ when representing states that persist until the next measurement, like scheduled events or inventory levels.
Customizing Step Plots
Step plots accept the same styling parameters as regular plots. Customize colors, line styles, markers, and more to make your visualizations clearer and more professional.
import matplotlib.pyplot as plt
import numpy as np
# Stock price data (simplified)
days = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
price = np.array([100, 102, 101, 105, 103, 107, 106, 110, 108, 112])
plt.figure(figsize=(12, 6))
# Styled step plot
plt.step(days, price, where='post',
color='#2E86AB', # Custom hex color
linewidth=2.5,
linestyle='--',
label='Daily Closing Price',
marker='o', # Add markers at data points
markersize=8,
markerfacecolor='#A23B72',
markeredgecolor='white',
markeredgewidth=2)
# Add horizontal reference line
plt.axhline(y=105, color='red', linestyle=':', linewidth=1.5,
alpha=0.7, label='Target Price')
plt.xlabel('Trading Day', fontsize=13, fontweight='bold')
plt.ylabel('Price ($)', fontsize=13, fontweight='bold')
plt.title('Stock Price Movement - Step Plot', fontsize=15, fontweight='bold')
plt.legend(loc='upper left', fontsize=11)
plt.grid(True, alpha=0.3, linestyle='--')
plt.tight_layout()
plt.show()
The combination of dashed lines and prominent markers makes individual data points stand out while maintaining the step pattern that accurately represents discrete price changes.
Practical Applications
Step plots excel in specific real-world scenarios. Here are two common applications.
Cumulative Distribution Function (CDF):
import matplotlib.pyplot as plt
import numpy as np
# Generate random data
np.random.seed(42)
data = np.random.normal(100, 15, 200)
# Calculate CDF
sorted_data = np.sort(data)
cumulative = np.arange(1, len(sorted_data) + 1) / len(sorted_data)
plt.figure(figsize=(10, 6))
plt.step(sorted_data, cumulative, where='post', linewidth=2, color='darkblue')
plt.xlabel('Value', fontsize=12)
plt.ylabel('Cumulative Probability', fontsize=12)
plt.title('Cumulative Distribution Function', fontsize=14)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Digital Signal Representation:
import matplotlib.pyplot as plt
import numpy as np
# Digital signal (binary)
time = np.array([0, 1, 1, 2, 2, 4, 4, 5, 5, 7, 7, 8])
signal = np.array([0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1])
plt.figure(figsize=(12, 4))
plt.step(time, signal, where='post', linewidth=2.5, color='darkgreen')
plt.ylim(-0.5, 1.5)
plt.yticks([0, 1], ['LOW', 'HIGH'])
plt.xlabel('Time (ms)', fontsize=12)
plt.ylabel('Signal State', fontsize=12)
plt.title('Digital Signal Over Time', fontsize=14)
plt.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
These examples show step plots in their element—representing data where intermediate values literally don’t exist.
Step Plots vs Line Plots
Understanding when to use each plot type prevents misrepresenting your data. Line plots suggest continuous change; step plots show discrete states.
import matplotlib.pyplot as plt
import numpy as np
# Discrete measurement data
time = np.array([0, 2, 4, 6, 8, 10])
inventory = np.array([100, 85, 90, 70, 75, 60])
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Line plot (misleading for discrete data)
ax1.plot(time, inventory, marker='o', linewidth=2,
markersize=8, color='orange')
ax1.set_title('Line Plot (Misleading)', fontsize=13, fontweight='bold')
ax1.set_xlabel('Hour', fontsize=11)
ax1.set_ylabel('Inventory Count', fontsize=11)
ax1.grid(True, alpha=0.3)
ax1.set_ylim(50, 110)
# Step plot (accurate representation)
ax2.step(time, inventory, where='post', linewidth=2, color='green')
ax2.plot(time, inventory, 'o', markersize=8, color='darkgreen')
ax2.set_title('Step Plot (Accurate)', fontsize=13, fontweight='bold')
ax2.set_xlabel('Hour', fontsize=11)
ax2.set_ylabel('Inventory Count', fontsize=11)
ax2.grid(True, alpha=0.3)
ax2.set_ylim(50, 110)
plt.tight_layout()
plt.show()
The line plot incorrectly suggests inventory levels transition smoothly between measurements. The step plot accurately shows that inventory remains constant between checks—you can’t have 87.5 items in stock.
Use line plots for truly continuous data like temperature sensors with high sampling rates. Use step plots when your data represents discrete states, counts, or measurements taken at intervals where intermediate values are meaningless.
Conclusion
Step plots are the correct choice for discrete data visualization. They prevent the misinterpretation that line plots create when applied to data that doesn’t change continuously.
Master the where parameter—it’s not optional styling but a fundamental decision about what your data represents. Use ‘post’ for most scenarios, ‘pre’ for forward-looking states, and ‘mid’ sparingly for special cases.
Combine step plots with appropriate styling to create professional visualizations. Add markers to emphasize measurement points, use colors that align with your domain (red/green for signals, blue for distributions), and always include clear labels.
Remember that choosing between step and line plots isn’t about preference—it’s about accurately representing the nature of your data. When your measurements represent discrete states or counts, step plots are the only honest choice.