Cartesian zoom with polar plot in python - python

I am trying to plot some data in polar coordinates (I am currently using the polar projection):
The code I am using is the following:
import matplotlib.pyplot as plt
import numpy as np
# Create radial and angular array
r = np.linspace(1.0,10,11)
t = np.linspace(0.0,0.5*np.pi,101)
# Define the quantity that I want to plot
z = np.zeros((len(t),len(r)))
for yval in range(len(r)):
z[:,yval] = np.cos(16.0*t)/r[yval]
#Create the figure
f = plt.figure(figsize=(13,8))
ax = plt.subplot(111, projection='polar')
ax.set_rorigin(-1)
#Plot the data
pcm = ax.pcolormesh(t,r,z.T,cmap = 'hot',shading='gouraud')
ax.set_xlim([0.0,0.5*np.pi])
ax.set_ylim([1.0,10.0])
#Add colorbar and show
bar = f.colorbar(pcm)
plt.show()
So far I have no problem, but I would like to zoom on a particular region of this plot.
However, when I set the axes range the axes is still polar, therefore I cannot zoom on a "cartesian" region of the domain (i.e. a square box).
A possible option would be to transform the data into cartesian coordinates, but when I do it I lose a lot of resolution in the inner part of the domain, which is something that I should absolutely avoid.
How can I select a rectangular zone of a plot in polar coordinates without transforming by hand the data? And in case I have to switch to cartesian coordinates, is there any matplotlib or python function that does it while taking care of the resolution in the inner regions of the domain?
Thanks in advance

You can create an X, Y mesh yourself that is has a higher resolution on the inner part of the domain and use that with ax.pcolormesh()
# Create radial and angular array
r = np.linspace(1.0,10,11)
t = np.linspace(0.0,0.5*np.pi,101)
# Define the quantity that I want to plot
z = np.zeros((len(t),len(r)))
for yval in range(len(r)):
z[:,yval] = np.cos(16.0*t)/r[yval]
#Create the figure, bigger figsize to make the resulting plot square
f = plt.figure(figsize=(13,10))
ax = plt.subplot(111) # Drop back to XY coordinates
# Generate the XY corners of the colormesh
X = np.array([[ri*np.cos(j) for j in t] for ri in r])
Y = np.array([[ri*np.sin(j) for j in t] for ri in r])
#Plot the data
pcm = ax.pcolormesh(X,Y,z.T,cmap = 'hot',shading='gouraud')
#Add colorbar and show
bar = f.colorbar(pcm)
plt.show()
The figure from the question
The figure generated by code above

A way to do this is to create an expanded polar plot and then clip a rectangle of it. A picture is worth a thousand words:
Here is a function that allows you to do so. The arguments are the original axes, the xlims and ylims of the region to be zoomed and the inset axes bounds (x0, y0, width, height) in the original axes coordinates. The function outputs a cartesian ax with the specified limits, a polar axes where you can plot and the rmax value you need to set AFTER plotting (if you do it before, it will change after plotting).
def create_polar_zoom_inset(ax, xlims, ylims, inset_bounds):
# Create cartesian axes for inset
ax_inset_cart = ax.inset_axes(inset_bounds)
ax_inset_cart.set_xlim(xlims)
ax_inset_cart.set_ylim(ylims)
# Calculate location of expanded polar inset
# Scale factor from data to axes coordinates
xscalefactor = inset_bounds[2]/(xlims[1] - xlims[0])
yscalefactor = inset_bounds[3]/(ylims[1] - ylims[0])
# Center of expanded polar inset
center_inset_polar = [
inset_bounds[0] - xlims[0]*xscalefactor,
inset_bounds[1] - ylims[0]*yscalefactor
]
# Max value of r in the inset
rmax_inset = 2*np.sqrt(np.power(xlims, 2).max() + np.power(ylims, 2).max())
# Size of the expanded polar inset
size_inset_polar = [2*rmax_inset*xscalefactor, 2*rmax_inset*yscalefactor]
# Create expanded polar inset
polar_inset_bounds = [
center_inset_polar[0] - 0.5*size_inset_polar[0],
center_inset_polar[1] - 0.5*size_inset_polar[1],
size_inset_polar[0],
size_inset_polar[1]
]
ax_inset_polar = ax.inset_axes(polar_inset_bounds, projection="polar")
ax_inset_polar.set_facecolor("None")
# Remove tick labels from expanded polar inset
ax_inset_polar.xaxis.set_ticklabels([])
ax_inset_polar.yaxis.set_ticklabels([])
# Clip elements of the expanded inset outside the cartesian inset
ax_inset_polar.patch = ax_inset_cart.patch
for axis in [ax_inset_polar.xaxis, ax_inset_polar.yaxis]:
axis.set_clip_path(ax_inset_cart.patch)
ax_inset_polar.spines['polar'].set_clip_path(ax_inset_cart.patch)
return ax_inset_cart, ax_inset_polar, rmax_inset
The code in your example is especially hard since the origin of the axes is not (0,0) but (-1,-1). That would need additional tinkering. But if we set rorigin to 0 (as it will be usually the case), the code would look as follows
# Create radial and angular array
r = np.linspace(1.0,10,11)
t = np.linspace(0.0,0.5*np.pi,101)
# Define the quantity that I want to plot
z = np.zeros((len(t),len(r)))
for yval in range(len(r)):
z[:,yval] = np.cos(16.0*t)/r[yval]
#Create the figure
f = plt.figure(figsize=(13,8))
ax = plt.subplot(111, projection='polar')
ax.set_rorigin(0)
#Plot the data
pcm = ax.pcolormesh(t,r,z.T,cmap = 'hot',shading='gouraud')
ax.set_xlim([0.0,0.5*np.pi])
ax.set_ylim([1.0,10.0])
#Add colorbar and show
bar = f.colorbar(pcm)
#Create inset
ax_c, ax_p, rmax_inset = create_polar_zoom_inset(
ax, xlims=[0., 2.], ylims=[1, 2], inset_bounds=[0.4, 0.3, 0.6, 0.3])
#Plot on inset
ax_p.pcolormesh(t,r,z.T,cmap = 'hot',shading='gouraud')
#Make rorigin and rmin coincide with the original plot
ax_p.set_rorigin(0)
ax_p.set_rmin(1)
#Set rmax
ax_p.set_rmax(rmax_inset)
plt.show()

Related

Wrong arrow length using quiver with cartopy projections

I want to plot a vector field with vectors representing a displacement between one point to another on the map with cartopy.
My code works as expected when using the PlateCarree() transformation, but arrow length is several orders of magnitude off for all the other projections I tested.
Here is a MWE that should illustrate quite clearly the issue:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy
# Want to test for several different projections
projections = [
ccrs.PlateCarree(),
ccrs.EqualEarth(),
ccrs.Mollweide(),
ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()
# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()
# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)
# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()
for i, projection in enumerate(projections):
# Replace the existing ax with an ax with the desired projection.
ax = axes[i]
fig.delaxes(ax)
ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
# Make the plot readable.
ax.set_global()
ax.gridlines(draw_labels="x")
# Non pertubed points are plotted in black.
ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
# Perturbed points are plotted in red.
ax.plot(
lon + lon_perturbation,
lat + lat_perturbation,
"r.",
ms=5,
transform=coordinate_ccrs,
)
# We try to draw arrows from a given black dot to its corresponding
# red dot.
ax.quiver(
lon,
lat,
lon_perturbation,
lat_perturbation,
transform=coordinate_ccrs,
# From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
# look at the documentation of the "scale_unit" parameter.
# The next 3 parameters are what matplotlib tell us to do. From
# matplotlib documentation:
# To plot vectors in the x-y plane, with u and v having the same units
# as x and y, use angles='xy', scale_units='xy', scale=1.
angles="xy",
scale_units="xy",
scale=1,
# Simply make the arrows nicer, removing these last 3 parameters do not
# solve the issue.
minshaft=2,
minlength=0.5,
width=0.002,
)
# Show everything
plt.show()
which display on the screen the following image:
The PlateCarree transformation is the only one displaying arrows. In fact, there are arrows in the other 3 projections, but I order to see them I need to scale them by 10000 with scale=0.00001 in the call to quiver, which gives:
Did I make a mistake when using cartopy API, is this expected behaviour and I missed something in the documentation, or is this a bug?
while there's quite some debate on github about cartopy's implementation of quiver-plot transformations GitHub-issues there is in fact a way on how to get your plot look as you want it to look...
However, while thinking about this... I noticed that there's a thing that you might want to consider when using projected quiver-plots...
As I see it, the re-projected arrows would would most probably need to be curved to really visualize the same direction as provided in the original data!
(in the input-crs the arrow points as a straight line from point A to B, but if you re-project the points, the "straight line" that connected A and B is now in general a curved line, and so if the original direction was correct, I think the new direction should be indicated as a curved arrow...)
That being said, you could achieve what you want by transforming the points manually instead of letting cartopy do the job:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy
# Want to test for several different projections
projections = [
ccrs.PlateCarree(),
ccrs.EqualEarth(),
ccrs.Mollweide(),
ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()
# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()
# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)
# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()
for i, projection in enumerate(projections):
# Replace the existing ax with an ax with the desired projection.
ax = axes[i]
fig.delaxes(ax)
ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
# Make the plot readable.
ax.set_global()
ax.gridlines(draw_labels="x")
# Non pertubed points are plotted in black.
ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
# Perturbed points are plotted in red.
ax.plot(
lon + lon_perturbation,
lat + lat_perturbation,
"r.",
ms=5,
transform=coordinate_ccrs,
)
xy_start = projection.transform_points(coordinate_ccrs, lon, lat)[:,:-1].T
xy_end = projection.transform_points(coordinate_ccrs, lon + lon_perturbation,
lat + lat_perturbation)[:,:-1].T
# We try to draw arrows from a given black dot to its corresponding
# red dot.
ax.quiver(
*xy_start,
*(xy_end - xy_start),
# From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
# look at the documentation of the "scale_unit" parameter.
# The next 3 parameters are what matplotlib tell us to do. From
# matplotlib documentation:
# To plot vectors in the x-y plane, with u and v having the same units
# as x and y, use angles='xy', scale_units='xy', scale=1.
angles="xy",
scale_units="xy",
scale=1,
# Simply make the arrows nicer, removing these last 3 parameters do not
# solve the issue.
minshaft=2,
minlength=0.5,
width=0.002,
)
# Show everything
plt.show()

how to change specific value's color in pcolormesh, such as -1,-2,-3, not NaN

I know there is a func cmap.set_bad to change NaN's color but I want to change color of more values such as -1,-2,-3, how to do it?
this plot is base on basemap
and I want to hold the color of the other area so I can't use the colormap built by myself
##### plot IMERG ####
latcorners = ([20.05, 54.051])
loncorners = ([73.05, 136.05])
m = Basemap(projection='cyl',llcrnrlat=latcorners[0],urcrnrlat=latcorners[1],llcrnrlon=loncorners[0],urcrnrlon=loncorners[1])
# Draw coastlines, state and country boundaries, edge of map.
m.drawcoastlines(color='darkgray')
m.drawstates(color='darkgray')
m.drawcountries(color='darkgray')
# Define the latitude and longitude data
X,Y = np.float32(np.meshgrid(theLons, theLats))
X1,Y1 = np.float16(np.meshgrid(lons_resized, lats_resized))
# Mask the values less than 0 because there is no data to plot.
masked_array = np.ma.masked_where(precip < 0,precip)
# Plot every masked value as grey
cmap = plt.get_cmap('gnuplot').copy()
cmap.set_bad('darkslategray', 1.)
# Draw filled contours.
clevs = np.arange(0,5,0.2)
m.readshapefile(r'../beijing/beijing', 'Shape_Leng', color='darkgrey')
# Plot the data,resized_precip is an ndarray which contains precipitation data, with shape(3401,6301), X1,Y1 are lon/lat of these points
cs = m.pcolormesh(X1,Y1,resized_precip, cmap=cmap, vmin=0, vmax=10)
parallels = np.arange(-90.,91,5.)
m.drawparallels(parallels,labels=[True,False,True,False],color='darkgray')
meridians = np.arange(-180.,180.,10.)
m.drawmeridians(meridians,labels=[False,False,False,True],color='darkgray')
# Add colorbar
cbar = m.colorbar(cs,location='right',pad="5%")
cbar.set_label('mm/h')
plt.show()
plt.close()
this is a program which is used to plot some precipitation data.
I want to plot these precipitation data properly,but I want to highlight some specific area with special color, and these points are flaged by special value.so how can I highlight these area by the same way as pcolormesh but not scatter?

Reducing axis length while maintaining equal aspect ratio in 3D plot

I am trying to create a 3-D plot and a 2-D plot side-by-side in python. I need equal aspect ratios for both plots, which I managed using code provided by this answer: https://stackoverflow.com/a/31364297/125507. The problem I'm having now is how to effectively "crop" the 3-D plot so it doesn't take up so much white space. That is to say, I want to reduce the length of the X and Y axes while maintaining equal scale to the (longer) Z-axis. Here is a sample code and plot:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
def set_axes_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca().
'''
x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()
x_range = abs(x_limits[1] - x_limits[0])
x_middle = np.mean(x_limits)
y_range = abs(y_limits[1] - y_limits[0])
y_middle = np.mean(y_limits)
z_range = abs(z_limits[1] - z_limits[0])
z_middle = np.mean(z_limits)
# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.
plot_radius = 0.5*max([x_range, y_range, z_range])
ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
ax = [None]*2
fig = plt.figure()
ax[0] = fig.add_subplot(121, projection='3d', aspect='equal')
ax[1] = fig.add_subplot(122, aspect='equal')
nn = 30
phis = np.linspace(0,np.pi, nn).reshape(1,nn)
psis = np.linspace(0,np.pi*2,nn).reshape(nn,1)
ones = np.ones((nn,1))
el_h = np.linspace(-5, 5, nn).reshape(1,nn)
x_sph = np.sin(phis)*np.cos(psis)
y_sph = np.sin(phis)*np.sin(psis)
z_sph = np.cos(phis)*ones
x_elp = np.sin(phis)*np.cos(psis)*.25
y_elp = np.sin(phis)*np.sin(psis)*.25
z_elp = el_h*ones
ax[0].scatter(x_sph, y_sph, z_sph)
ax[0].scatter(x_elp, y_elp, z_elp)
ax[1].scatter(y_sph, z_sph)
ax[1].scatter(y_elp, z_elp)
for ii in range(2):
ax[ii].set_xlabel('X')
ax[ii].set_ylabel('Y')
ax[0].set_zlabel('Z')
set_axes_equal(ax[0])
plt.savefig('SphereElipse.png', dpi=300)
And here is its image output:
3-D and 2-D sphere and ellipse side-by-side
Clearly the 2D plot automatically modifies the length of the axes while maintaining the scale, but the 3D plot doesn't, leading to a tiny representation which does not well use the space allotted to its subplot. Is there any way to do this? This question is similar to an earlier unanswered question How do I crop an Axes3D plot with square aspect ratio?, except it adds the stipulation of multiple subplots, which means the answers provided there do not work.

Relative positioning issue with the bar3d plot in matplotlib

I'm new to Stack Overflow, so my image attachment is not previewed, but you can see it here.
Basically, I'm using matplotlib's bar3d function to plot a probability distribution at different points of time. The issue is that the first 'slice' is positioned above the remaining slices, which obviously is not right. The second and higher numbered slices are positioned correctly with respect to their neighbors.
The only way to avoid this issue is to change the view angle so that there is no intersection between the first and other slices, but that view doesn't capture the point I'm trying to make.
Do you have any suggestions on how to fix this issue? Or is this some kind of a bug that bar3d has?
Thanks!
EDIT: Here's the code
def bar_plot (data, n_slices, dx = 1, dy = 1, z_max = 1, x_label = 'x',
y_label='y', z_label='z', elev_angle = 30, azim_angle = 115):
"""
Makes a 3d bar plot of the data given as a 2d numpy array.
Parameters
----------
data: 2d-array
Two-dimensional numpy array of z-values
n_slices: int
Number of 'slices' in y-directions to be used in the 3D plot
dx: float
Distance between neighboring x-positions
dy: float
Distance between neighboring y-positions
x_label: str
Label of the x-axis
y_label: str
Label of the y-axis
z_lable: str
Label of the z-axis
elev_angle: int
Alevation viewing angle
azim_angle: int
Azimuthal viewing angle
z_max: float
Default limit to the z-axis
Returns
-------
fig: pyplot figure object
Figure of the 3d-plot
ax: pyplot axes object
Axes object that contains the figure elements
"""
# Initialize the figure object
fig = plt.figure(figsize = [10, 8])
ax = fig.add_subplot(111, projection='3d')
# Colors to indicate variation in y-axis
colors = sns.color_palette('viridis', n_colors=n_slices+1)
# Dimensions of the 2d-array
x_length, y_length = data.shape
# Initial index of the slice
i_slice = 0
# Iterate through each slice and add bar plots
for y in np.arange(0, y_length, y_length//n_slices):
# x-, y- and z-positions
x_pos = np.arange(x_length)*dx
y_pos = y*np.ones(x_length)*dy
z_pos = np.zeros(x_length)
# Horizontal dimensions of the bars
delta_x = dx*np.ones(x_length)
delta_y = 2*dy*np.ones(x_length)
# Heights in the z-direction
delta_z = p[:,y]
ax.bar3d(x_pos, y_pos, z_pos, delta_x, delta_y, delta_z,
color = colors[i_slice])
i_slice = i_slice + 1;
# Add axis labels
ax.set_xlabel(x_label)
ax.set_ylabel(y_label)
ax.set_zlabel(z_label)
# Adjust the 3d viewing angle of the plot
ax.view_init(elev_angle, azim_angle)
# Set the z-limit of the plot
z_max = np.min([z_max, np.max(data)])
ax.set_zlim([0, z_max])
return fig, ax

2D histogram of events is misaligned with 1D bar charts of event probability x and y axes using python and matplotlib

I would like to plot a 2d histogram using matplotlib in order to visualize the influence of two variables on the occurrence of an event.
In my test case, the event is “wish coming true” and the variable x is the number of falling stars and y is the involvement of a fairy godmother. What I would like to do is to plot a 2d histogram of wishes coming true for bins of falling stars and fairy godmothers. Then next to each axis, I would like to show the probability of a wish coming true, event/(event+nonevent), for each bin of falling stars and fairy godmothers (1D bar chart containing probabilities for each histogram bin). The bar chart bins should correspond to and be aligned with the 2d histogram bins. However, there seems to be a slight misalignment between the bar charts and the histogram bins.
For aligning the bar chart correctly, will the settings of the limits of the axis corresponding to the first and last bin edges do the trick ? Once these limits are set, can I feed bin centers into plt.bar() as locations on the axis as opposed to indices ?
My code and the resulting images are as follows :
import numpy as np
import matplotlib.pyplot as plt
from numpy import linspace
import cubehelix
# Create random events and non-events
x_noneve = 3.*np.random.randn(10000) +22.
np.random.seed(seed=41)
y_noneve = np.random.randn(10000)
np.random.seed(seed=45)
x_eve = 3.*np.random.randn(1000) +22.
np.random.seed(seed=33)
y_eve = np.random.randn(1000)
x_all = np.concatenate((x_eve,x_noneve),axis=0)
y_all = np.concatenate((y_eve,y_noneve),axis=0)
# Set up default x and y limits
xlims = [min(x_all),max(x_all)]
ylims = [min(y_all),max(y_all)]
# Set up your x and y labels
xlabel = 'Falling Star'
ylabel = 'Fairy Godmother'
# Define the locations for the axes
left, width = 0.12, 0.55
bottom, height = 0.12, 0.55
bottom_h = left_h = left+width+0.03
# Set up the geometry of the three plots
rect_wishes = [left, bottom, width, height] # dimensions of wish plot
rect_histx = [left, bottom_h, width, 0.25] # dimensions of x-histogram
rect_histy = [left_h, bottom, 0.25, height] # dimensions of y-histogram
# Set up the size of the figure
fig = plt.figure(1, figsize=(9.5,9))
fig.suptitle('Wishes coming true', fontsize=18, fontweight='bold')
cx1 = cubehelix.cmap(startHue=240,endHue=-300,minSat=1,maxSat=2.5,minLight=.3,maxLight=.8,gamma=.9)
# Make the three plots
axWishes = plt.axes(rect_wishes) # wishes plot
axStarx = plt.axes(rect_histx) # x bar chart
axFairy = plt.axes(rect_histy) # y bar chart
# Define the number of bins
nxbins = 50
nybins = 50
nbins = 100
xbins = linspace(start = xlims[0], stop = xlims[1], num = nxbins)
ybins = linspace(start = ylims[0], stop = ylims[1], num = nybins)
xcenter = (xbins[0:-1]+xbins[1:])/2.0
ycenter = (ybins[0:-1]+ybins[1:])/2.0
delx = np.around(xbins[1]-xbins[0], decimals=2,out=None)
dely = np.around(ybins[1]-ybins[0], decimals=2,out=None)
H, xedges,yedges = np.histogram2d(y_eve,x_eve,bins=(ybins,xbins))
X = xcenter
Y = ycenter
H = np.where(H==0,np.nan,H) # Remove 0's from plot
# Plot the 2D histogram
cax = (axWishes.imshow(H, extent=[xlims[0],xlims[1],ylims[0],ylims[1]],
interpolation='nearest', origin='lower',aspect="auto",cmap=cx1))
#Plot the axes labels
axWishes.set_xlabel(xlabel,fontsize=14)
axWishes.set_ylabel(ylabel,fontsize=14)
#Set up the plot limits
axWishes.set_xlim(xlims)
axWishes.set_ylim(ylims)
#Set up the probability bins
x_eve_hist, xoutbins = np.histogram(x_eve, bins=xbins)
y_eve_hist, youtbins = np.histogram(y_eve, bins=ybins)
x_noneve_hist, xoutbins = np.histogram(x_noneve, bins=xbins)
y_noneve_hist, youtbins = np.histogram(y_noneve, bins=ybins)
probax = [eve/(eve+noneve+0.0) if eve+noneve>0 else 0 for eve,noneve in zip(x_eve_hist,x_noneve_hist)]
probay = [eve/(eve+noneve+0.0) if eve+noneve>0 else 0 for eve,noneve in zip(y_eve_hist,y_noneve_hist)]
probax = probax/np.sum(probax)
probay = probay/np.sum(probay)
probax = np.round(probax*100., decimals=0, out=None)
probay = np.round(probay*100., decimals=0, out=None)
#Plot the bar charts
#Set up the limits
axStarx.set_xlim( xlims[0], xlims[1])
axFairy.set_ylim( ylims[0], ylims[1])
axStarx.bar(xcenter, probax, align='center', width =delx, color = 'royalblue')
axFairy.barh(ycenter,probay,align='center', height=dely, color = 'mediumorchid')
#Show the plot
plt.show()
resulting image
hex version
While my original code was functional, the limits of the 2D histo and bar chart were not defined using the histogram bins. Thus any changes to the bins resulted in a poorly-aligned graph. To ensure that the limits of the graph always correspond to the limits of the histogram bins, I changed
cax = (axWishes.imshow(H, extent=[xmin,xmax,ymin,ymax],
interpolation='nearest', origin='lower',aspect="auto",cmap=cx1))
to
cax = (axWishes.imshow(H, extent=[xbins[0],xbins[-1],ybins[0],ybins[-1]],
interpolation='nearest', origin='lower',aspect="auto",cmap=cx1))
and
axStarx.set_xlim( xlims[0], xlims[1])
axFairy.set_ylim( ylims[0], ylims[1])
to
axStarx.set_xlim(axWishes.get_xlim())
axFairy.set_ylim(axWishes.get_ylim())
For information, bar chart can accept either indices or values along the axis as bar locations. When the bars correspond to bins and not categorical variables, it is important to set axis limits and correctly define bar width. These are done automatically with histo. However, if you wish to explore a variable other than the number of members by bin, you must use a bar chart and define the limits by hand.

Categories