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.
Of course I can pad the data, but that is getting messy in my actual code.
Questions
- Is there a way to make the first and last steps the same size as the middle ones?
- 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?
CodePudding user response:
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 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)
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()
CodePudding user response:
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: