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:






