Equispacing of error bars in semilogx plot - matplotlib

When I plot using errorbar of matplotlib, I use the errorevery option to avoid crowding the error bars. However, if x-axis is set to log scale, then the error bars do not look equispaced anymore.
N = 40
xdata = np.arange(N)+1
ydata = (xdata/N)**2
fig, ax = plt.subplots()
ax.errorbar(xdata, ydata, yerr=0.1*np.ones(N), errorevery=4, elinewidth=0.5, ecolor='r')
ax.set_xscale("log")
Is there a way to keep the visual equispacing on log scale, which might look like the following (drawn roughly):

Related

Draw various plots in one figure

The image below shows, what i want, 3 different plots in one execution but using a function
enter image description here
enter image description here
I used the following code:
def box_hist_plot(data):
sns.set()
ax, fig = plt.subplots(1,3, figsize=(20,5))
sns.boxplot(x=data, linewidth=2.5, ax=fig[0])
plt.hist(x=data, bins=50, density=True, ax = fig[1])
sns.violinplot(x = data, ax=fig[2])
and i got this error:
inner() got multiple values for argument 'ax'
Besides the fact that you should not call a Figure object ax and an array of Axes object fig, your problem comes from the line plt.hist(...,ax=...). plt.hist() should not take an ax= parameter, but is meant to act on the "current" axes. If you want to specify which Axes you want to plot, you should use Axes.hist().
def box_hist_plot(data):
sns.set()
fig, axs = plt.subplots(1,3, figsize=(20,5))
sns.boxplot(x=data, linewidth=2.5, ax=axs[0])
axs[1].hist(x=data, bins=50, density=True)
sns.violinplot(x = data, ax=axs[2])

Polar axis in matplotlib

I'm trying to set the r-axis in a polar plot using Matplotlib. At this time, the best result I got is the following one :
I would like to modify three things :
draw a thicker line for the axis with labels,
add ticks to others r-axis,
move the legend outside of the plot.
I expect something like that :
Thanks for your help.
JD
'''
r = [0.07109986, 0.07186792, 0.07128804, 0.07093468, 0.11061314,\
0.11480423, 0.09913993, 0.13417775, 0.07485087, 0.07140557,\
0.08117919, 0.1235301 , 0.07109986]
theta = 2.0*np.pi*np.arange(len(r))/(len(r)-1)
titles = ['$a$','$\\alpha$','$b^{1}$','$b^{2}$',\
'$c^{1}_{1}$','$c^{1}_{2}$','$c^{1}_{3}$','$c^{1}_{4}$',\
'$c^{2}_{1}$','$c^{2}_{2}$','$c^{2}_{3}$','$c^{2}_{4}$']
fig = plt.figure()
ax = fig.add_subplot(111,polar='True')
ax.plot(theta,r)
ax.spines['polar'].set_visible(False)
ax.set_theta_zero_location(loc='N')
ax.set_xticks(np.arange(0,2.0*np.pi,2.0*np.pi/len(titles)))
ax.set_xticklabels(titles)
ax.yaxis.grid(False)
ax.set_rlabel_position(0)
plt.tick_params(axis='y',labelsize=12)
plt.show()
'''

subplots_adjust moves axes unpredictably?

I'm working on a python module that creates a matplotlib figure with an on_resize listener. The listener forces the height of the lower axes to a specific number of pixels (rather than scaling relative to figure size). It works. However, if (in matplotlib interactive mode) after creating the plot the user calls fig.subplots_adjust() it messes up subplot sizes. Here's a radically simplified version of what the module does:
import matplotlib.pyplot as plt
plt.ion()
def make_plot():
fig = plt.figure()
gs = plt.GridSpec(10, 1, figure=fig)
ax_upper = fig.add_subplot(gs[:-1])
ax_lower = fig.add_subplot(gs[-1])
ax_upper.plot([0, 1])
ax_lower.plot([0, 1])
fig.canvas.mpl_connect('resize_event', on_resize)
return fig
def on_resize(event):
fig = event.canvas.figure
# get the current position
ax_lower_pos = list(fig.axes[1].get_position().bounds) # L,B,W,H
# compute desired height in figure-relative coords
desired_height_px = 40
xform = fig.transFigure.inverted()
desired_height_rel = xform.transform([0, desired_height_px])[1]
# set the new height
ax_lower_pos[-1] = desired_height_rel
fig.axes[1].set_position(ax_lower_pos)
# adjust ax_upper accordingly
ax_lower_top = fig.axes[1].get_position().extents[-1] # L,B,R,T
ax_upper_pos = list(fig.axes[0].get_position().bounds) # L,B,W,H
# new bottom
new_upper_bottom = ax_lower_top + desired_height_rel
ax_upper_pos[1] = new_upper_bottom
# new height
ax_upper_top = fig.axes[0].get_position().extents[-1] # L,B,R,T
new_upper_height = ax_upper_top - new_upper_bottom
ax_upper_pos[-1] = new_upper_height
# set the new position
fig.axes[0].set_position(ax_upper_pos)
fig.canvas.draw()
Here's the output if the user calls fig = make_plot():
Now if the user calls fig.subplots_adjust, the bottom axis is squished and the space between bottom and top axes is even more squished (the on_resize listener had set them both to 40px):
fig.subplots_adjust(top=0.7)
At this point, grabbing the corner of the window and dragging even a tiny bit is enough to trigger the on_resize listener and restore what I want (fixed pixel height for bottom axes and space between axes) while keeping the newly-added wide top margin intact:
How can I get that result without having to manually trigger a resize event? As far as I can tell, subplots_adjust does not fire off any events that I could listen for.
I think the problem lies in ax.update_params() updating the axes position with a figbox taken from the underlying subplotspec (which as far as I can tell doesn't get updated after initial figure creation?). (note: update_params is called from within subplots_adjust, see here).
The underlying problem seems to be to make an axes with a specific height in pixels. An easy solution to this is to use mpl_toolkits.axes_grid1's make_axes_locatable.
This allows to get rid of any callback and hence of the complete problem of the race condition in the events.
A note: The plot seems to be part of a bigger library. Since it is always nice not to patronize the users of such packages, one would usually allow them to specify the axes to plot to, such that they can put the plot into a bigger figure with other elements. The below solution makes this particularly easy.
Of course, also calling plt.subplots_adjust is still possible at any time.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
desired_height_px = 40 #pixel
def make_plot(ax=None):
if not ax:
fig, ax = plt.subplots()
else:
fig = ax.figure
div = make_axes_locatable(ax)
cax = div.append_axes("bottom", desired_height_px/fig.dpi, pad=0.25)
sc1 = ax.scatter([2,1,3], [2,3,1], c=[1,2,3])
sc2 = cax.scatter([3,2,1],[2,3,1], c=[3,1,2])
return fig, ax, cax, (sc1, sc2)
fig, (ax1, ax2) = plt.subplots(1,2)
make_plot(ax=ax1)
#user plot on ax2
ax2.plot([1,3])
fig.subplots_adjust(top=0.7)
plt.show()

how to add variable error bars to scatter plot points with shared axes in python matplotlib

I have generated a plot that shows a topographic profile with points along the profile that represent dated points. However, these dated points also have symmetric uncertainty values/error bars (that typically vary for each point).
In this example, I treat non-dated locations as 'np.nan'. I would like to add uncertainty values to the y2 axis (Mean Age) with defined uncertainty values as y2err.
Everytime I use the ax2.errorbar( ... ) line, my graph is squeezed and distorted.
import numpy as np
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
#Longitude = x; Elevation = y
x = (-110.75696,-110.75668,-110.75640,-110.75612,-110.75584,-110.75556,-110.75528)
y = (877,879,878,873,871,872,872)
ax1.plot(x, y)
ax1.set_xlabel('Longitude')
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_ylabel('Elevation', color='b')
ax1.tick_params('y', colors='b')
ax2 = ax1.twinx()
# Mean Age, np.nan = 0.0
y2 = (np.nan,20,np.nan,np.nan,np.nan,np.nan,np.nan)
y2err = (np.nan,5,np.nan,np.nan,np.nan,np.nan,np.nan)
ax2.scatter(x, y2, color='r')
#add error bars to scatter plot points
# (??????) ax2.errorbar(x, y, y2, y2err, capsize = 0, color='black')
ax2.set_ylim(10,30)
ax2.set_ylabel('Mean Age', color='r')
ax2.tick_params('y', colors='r')
fig.tight_layout()
plt.show()
If I do not apply the ax2.errorbar... line my plot looks like the first image, which is what I want but with the points showing uncertainty values (+/- equal on both side of point in the y-axis direction).
Plot of Elevation vs Age without error bars
When I use the ax2.errorbar line it looks like the second image:
Plot when using ax2.errorbar line
Thanks!

Polar plot in Matplotlib using contourf plots incorrect range

I'm plotting some hydrodynamical simulation data run in spherical coordinates and sometimes prefer to use contourf over pcolormesh because it looks nice and smooth instead of pixelated. However, I notice that contourf always extends my data to r=0 in a polar plot, yet my data never includes r=0. I have reproduced this issue with the simple example below:
from pylab import *
fig = figure(figsize=(6, 6))
ax = fig.add_subplot(111,projection='polar')
# generate some data
Nt,Nr = 150,150
r_axis = np.linspace(0.5,1.,Nr)
t_axis = np.linspace(0.,0.5*np.pi,Nt)
r_grid, t_grid = np.meshgrid(r_axis,t_axis)
data = np.zeros((Nt,Nr))
sin_theta = np.sin(t_axis)
for i in range(Nr):
data[:,i] = sin_theta
if 1: # polar plot using contourf - plots incorrectly from r = 0
scale = np.linspace(0.,1.,100)
polar = ax.contourf(t_grid,r_grid,data,scale,cmap='Spectral')
else: # correctly plots the data
polar = ax.pcolormesh(t_grid,r_grid,data,cmap='Spectral')
show()
Is there a quick fix? Thanks
One can set the axes limits. The radial scale is set as y, therefore
ax.set_ylim(0,1)
will set the origin to 0.