Matplotlib step function: How to extend the first and last steps

Question:

I am using a step and fill_between functions in Matplotlib and want the steps to be centred on the x points.

Code

import matplotlib.pyplot as plt
import numpy as np

xpoints=np.array([1,2,3,4])
ypoints=np.array([4,6,5,2])
ypoints_std=np.array([0.5,0.3,0.4,0.2])
plt.step(xpoints,ypoints,where='mid')
plt.fill_between(xpoints,ypoints+ypoints_std,ypoints-ypoints_std,step='mid',alpha=0.2)
plt.show()

Current plot:

At the moment, the step centred on 1 is only 0.5 wide, whereas the step centred on 2 is 1 wide.

Wanted

I actually want the step-width of 1 for all steps and also for the fill. This should include first and last step, so that they are extended compared to the current plot.

enter image description here

Of course I can pad the data, but that is getting messy in my actual code.

Questions

  1. Is there a way to make the first and last steps the same size as the middle ones?
  2. Or is there a way to produce a similar graph using histogram ? i.e. showing an error the size of the full width of the bar, centred on the y position of the graph?
Asked By: user12938030

||

Answers:

You could use pyplot.margins(0) to at least let your graph touch the axis on all 4 sides (left/right and bottom/top).

Either use two positional arguments for x and y, or use one to be applied for both:

import matplotlib.pyplot as plt
import numpy as np

xpoints=np.array([1,2,3,4])
ypoints=np.array([4,6,5,2])
ypoints_std=np.array([0.5,0.3,0.4,0.2])

fig, ax = plt.subplots()

ax.step(xpoints,ypoints,where='mid')
ax.fill_between(xpoints,ypoints+ypoints_std,ypoints-ypoints_std,step='mid',alpha=0.2)
ax.margins(0)  # default margins are 0.5 for x-axis and y-axis

plt.show()

Output:

plot with zero margins

Answered By: hc_dev

Using a bar plot at a height

The error bands could be shown via a bar plot with a bottom at ypoints - ypoints_std and a height of 2*ypoints_std.

import matplotlib.pyplot as plt
import numpy as np

xpoints = np.array([1, 2, 3, 4])
ypoints = np.array([4, 6, 5, 2])
ypoints_std = np.array([0.5, 0.3, 0.4, 0.2])

plt.bar(xpoints, ypoints, width=1, facecolor='none', edgecolor='dodgerblue')
plt.bar(xpoints, height=2 * ypoints_std, bottom=ypoints - ypoints_std, width=1, color='dodgerblue', alpha=0.2)

plt.xticks(xpoints)
plt.show()

using plt.bar to show error bands

Using zero-height bars

To only have horizontal lines, you could replace the first bar plot with zero-height bars. Adding the original plt.step with the same color will create the connecting lines

plt.gca().use_sticky_edges = False # prevent bars from "sticking" to the bottom
plt.step(xpoints, ypoints, where='mid', color='dodgerblue')
plt.bar(xpoints, height=0, bottom=ypoints, width=1, facecolor='none', edgecolor='dodgerblue')
plt.bar(xpoints, height=2 * ypoints_std, bottom=ypoints - ypoints_std, width=1, color='dodgerblue', alpha=0.2)

combining zero-height bars with step plot

Extending the points

You could add dummy values to repeat the first and last point. And then use plt.xlim(...) to limit the plot between 0.5 and 4.5.

import matplotlib.pyplot as plt
import numpy as np

xpoints = np.array([1, 2, 3, 4])
ypoints = np.array([4, 6, 5, 2])
ypoints_std = np.array([0.5, 0.3, 0.4, 0.2])

xpoints = np.concatenate([[xpoints[0] - 1], xpoints, [xpoints[-1] + 1]])
ypoints = np.pad(ypoints, 1, mode='edge')
ypoints_std = np.pad(ypoints_std, 1, mode='edge')

plt.step(xpoints, ypoints, where='mid')
plt.fill_between(xpoints, ypoints + ypoints_std, ypoints - ypoints_std, step='mid', alpha=0.2)
plt.xlim(xpoints[0] + 0.5, xpoints[-1] - 0.5)
plt.show()

extending step where='mid'

Answered By: JohanC
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.