matplotlib polar plot axis label position - python

I have been playing with polar plots for some time now, but can't figure out how I get my axis labels automatically placed in the correct spot.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[5, 5])
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection="polar")
r = np.random.normal(loc=50, scale=5, size=50)
theta = np.deg2rad(np.random.normal(loc=190, scale=2, size=50))
# Plot
ax.scatter(theta, r)
# Adjust limits
ax.set_rorigin(0)
ax.set_thetamin(180)
ax.set_thetamax(200)
ax.set_rmin(40)
ax.set_rmax(60)
# Labels
ax.set_xlabel("r")
ax.set_ylabel(r"$\theta$")
plt.show()
This produces such a plot:
https://ibb.co/geo4WK
As you can see, the "r" label does not appear on the top axis where the tick labels are and I have similar problem for other ranges of theta. Is there a way to always have the axis label appear with the axis that has tick labels? Or can I have the tick labels for the radii always at the bottom axis?
thanks!

You can use ax.xaxis.set_label_coords() to move "r" to the top center location. As the figure is set to be (5,5) and you have r limits adjusted, the "r" label should stay the same as you change theta.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[5, 5])
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection="polar")
r = np.random.normal(loc=50, scale=5, size=50)
theta = np.deg2rad(np.random.normal(loc=190, scale=2, size=50))
# Plot
ax.scatter(theta, r)
# Adjust limits
ax.set_rorigin(0)
ax.set_thetamin(180)
ax.set_thetamax(200)
ax.set_rmin(40)
ax.set_rmax(60)
# Labels
ax.set_xlabel("r")
ax.set_ylabel(r"$\theta$")
ax.xaxis.set_label_coords(0.5, 1.05)
plt.show()

Related

matplotlib polar plot tick/axis label position

I have been looking for a way to reliably position the tick and axis labels in a plot in polar coordinates. Please take a look at the following example:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[10, 5])
ax0 = fig.add_axes([0.05, 0.05, 0.4, 0.9], projection="polar")
ax1 = fig.add_axes([0.55, 0.05, 0.4, 0.9], projection="polar")
r0 = np.linspace(10, 12, 10)
theta0 = np.linspace(0, 0.1, 10)
ax0.quiver(theta0, r0, -0.1, 0.1)
ax1.quiver(theta0 + np.pi, r0, -0.1, 0.1)
ax0.set_thetamin(-2)
ax0.set_thetamax(10)
ax1.set_thetamin(178)
ax1.set_thetamax(190)
for ax in [ax0, ax1]:
# Labels
ax.set_xlabel("r")
ax.set_ylabel(r"$\theta$", labelpad=10)
# R range
ax.set_rorigin(0)
ax.set_rmin(9)
ax.set_rmax(13)
plt.show()
which results in this figure:
You can clearly see that
(a) the tick label position on the radial axis switches from bottom to top between the plots and the tick labels for theta switch from right to left.
(b) the axis label positions are fixed. I'd want the axis labels to also move with the tick labels. i.e. in the left plot, "theta" should be on the right, and in the right plot "r" should be on top.
How do I control the axis/tick labels in a way, so that they are positioned correctly? This even gets worse for e.g. a 90 degree shift, because then the theta axis is actually vertical and the tick labels are then totally off.
I think the most important bit is to become clear about how the usual notions of left, right, bottom, top translate into the polar axes in matplotlib.
The angular axis is the "x"-axis. The radial axis is the "y"-axis. The "bottom" is the outer ring. The "top" is the inner ring. "Left" is the radial axis at the start of the angular axis, "right" is the end of it.
This then allows to set the tick locations as usual, e.g.
ax.tick_params(labelleft=True, labelright=False,
labeltop=False, labelbottom=True)
for the case shown above.
The x and y labels (set_xlabel / set_ylabel) are not translated. Here left, right, top, bottom refer to the cartesian definition, just as with normal linear axes. This means that for certain positions, they cannot be used to label the axis, because they are just too far away. An alternative is to create a text at the desired position.
A complete example code:
import numpy as np
import matplotlib.pyplot as plt
fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(10,5),
subplot_kw=dict(projection="polar"))
ax0.set(thetamin=180, thetamax=230)
ax1.set(thetamin= 0, thetamax= 50)
plt.setp([ax0, ax1], rorigin=0, rmin=5, rmax=10)
ax0.tick_params(labelleft=False, labelright=True,
labeltop=True, labelbottom=False)
trans, _ , _ = ax1.get_xaxis_text1_transform(-10)
ax1.text(np.deg2rad(22.5), -0.18, "Theta Label", transform=trans,
rotation=22.5-90, ha="center", va="center")
plt.show()
To answer question (b):
ax0.yaxis.set_label_position('right')
ax1.xaxis.set_label_position('top')
In addition, I modified the ax.set_ylabel(r"$\theta$", labelpad=15)

Matplotlib colorbar with consistent size for multiple subplots

I am trying to create a figure with several subplots that have a common colorbar. The subplots have to have an equal aspect ratio and the colorbar has to have the same height as the subplots. However, I don't manage to get a narrow colorbar with the same height as the other subplots.
I am using this recipe to generate a colorbar with a range suitable for all subplots; hence this issue is not addressed in the MWE.
When using the axes divider recipe to attach the colorbar, the height of the subplot changes due to the aspect ratio.
Here's the MWE
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import itertools as it
import numpy as np
mean = [0, 0]
cov = [[1, 0.5],
[0.5, 4]]
n_samples = 10000
hrange = [[-5,5],[-5,5]]
bins = 20
# RANDOM DATA
Z_random = np.random.multivariate_normal(mean, cov, size=n_samples)
Z, xedges, yedges = np.histogram2d(Z_random[:,0], Z_random[:,1], bins=bins, range=hrange, normed=True)
X, Y = np.meshgrid(xedges, yedges)
# PLOT PCOLORMESHS
fig, axes = plt.subplots(2,3, subplot_kw=dict(aspect="equal"))
axes = dict(enumerate(fig.get_axes(),1))
for i,ax in axes.items():
if i==6:
break
pcm = ax.pcolormesh(X,Y,Z)
# PLOT COLORBAR
divider = make_axes_locatable(axes[6])
cax = divider.append_axes("left", size="15%", pad=0.0)
fig.colorbar(pcm, cax=cax, label=r"Colorbar label")
I can plot the colorbar over the complete subplot, in which case the height is correct, but it's much to wide to be appealing.
Does anybody have a "robust" solution, i.e. without manually fiddling around with the dimension of the subplots holding the colorbar?
Thanks in advance!
EDIT: Increased width of colorbar to emphasize that it becomes smaller in height.
If the only aim is to get the height of the colorbar correctly aligned with its horizontal neighbor, the last solution from this answer would help.
If however you also want the colorbar to be left-aligned with the plot on top of it, the solution is probably more complicated.
You may use a callback to set the position of the colorbar explicitely as follows:
from matplotlib import pyplot as plt
from matplotlib.transforms import Bbox
import numpy as np
mean = [0, 0]
cov = [[1, 0.5],
[0.5, 4]]
n_samples = 10000
hrange = [[-5,5],[-5,5]]
bins = 20
# RANDOM DATA
Z_random = np.random.multivariate_normal(mean, cov, size=n_samples)
Z, xedges, yedges = np.histogram2d(Z_random[:,0], Z_random[:,1], bins=bins, range=hrange, normed=True)
X, Y = np.meshgrid(xedges, yedges)
# PLOT PCOLORMESHS
fig, axes = plt.subplots(2,3, subplot_kw=dict(aspect="equal"))
for i,ax in enumerate(axes.flat):
if i==5:
break
pcm = ax.pcolormesh(X,Y,Z)
# PLOT COLORBAR
cax = fig.add_axes([0.6,0.01,0.1,0.4])
fig.colorbar(pcm, cax=cax, label=r"Colorbar label")
def align_cbar(cax, hax, vax):
hpos = hax.get_position()
vpos = vax.get_position()
bb = Bbox.from_extents(vpos.x0, hpos.y0, vpos.x0+vpos.width*.05,hpos.y1)
if cax.get_position() != bb:
cax.set_position(bb)
fig.canvas.draw_idle()
align_cbar(cax, axes[1,1], axes[0,2])
fig.canvas.mpl_connect("draw_event", lambda x: align_cbar(cax, axes[1,1], axes[0,2]))
plt.show()

How to Label patch in matplotlib

I am plotting rectangular patches in matplotlib in interactive mode. I want to add text to each patch. I do not want to annotate them as it decreases the speed. I am using 'label' property of patch but it is not working. Ayone know how to add 1 string to patch.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
plt.ion()
plt.show()
x = y = 0.1
fig1 = plt.figure()
ax1 = fig1.add_subplot(111, aspect='equal')
patch = ax1.add_patch(patches.Rectangle((x, y), 0.5, 0.5,
alpha=0.1,facecolor='red',label='Label'))
plt.pause(0)
plt.close()
You already know where the patch is, so you can calculate where the center is and add some text there:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
x=y=0.1
fig1 = plt.figure()
ax1 = fig1.add_subplot(111, aspect='equal')
patch= ax1.add_patch(patches.Rectangle((x, y), 0.5, 0.5,
alpha=0.1,facecolor='red',label='Label'))
centerx = centery = x + 0.5/2 # obviously use a different formula for different shapes
plt.text(centerx, centery,'lalala')
plt.show()
The coordinates for plt.text determine where the text begins, so you can nudge it a bit in the x direction to get the text to be more centered e.g. centerx - 0.05. obviously #JoeKington's suggestion is the proper way of achieving this

How to add colorbar to a histogram?

I have a histogram like this (just like a normal histogram):
In my situation, there are 20 bars always (spanning x axis from 0 to 1) and the color of the bar is defined based on the value on the x axis.
What I want is to add a color spectrum like one of those in http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps at the bottom of the histogram but I don't know how to add it.
Any help would be appreciated!
You need to specify the color of the faces from some form of colormap, for example if you want 20 bins and a spectral colormap,
nbins = 20
colors = plt.cm.spectral(np.linspace(nbins))
You can then use this to specify the color of the bars, which is probably easiest to do by getting histogram data first (using numpy) and plotting a bar chart. You can then add the colorbar to a seperate axis at the bottom.
As a minimal example,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
nbins = 20
minbin = 0.
maxbin = 1.
data = np.random.normal(size=10000)
bins = np.linspace(minbin,maxbin,20)
cmap = plt.cm.spectral
norm = mpl.colors.Normalize(vmin=data.min(), vmax=data.max())
colors = cmap(bins)
hist, bin_edges = np.histogram(data, bins)
fig = plt.figure()
ax = fig.add_axes([0.05, 0.2, 0.9, 0.7])
ax1 = fig.add_axes([0.05, 0.05, 0.9, 0.1])
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap=cmap,
norm=norm,
orientation='horizontal')
ax.bar(bin_edges[:-1], hist, width=0.051, color=colors, alpha=0.8)
ax.set_xlim((0., 1.))
plt.show()
Which yields,

How to draw a line outside of an axis in matplotlib (in figure coordinates)

Matplotlib has a function that writes text in figure coordinates (.figtext())
Is there a way to do the same but for drawing lines?
In particular my goal is to draw lines to group some ticks on the y-axis together.
Tested in python 3.8.12, matplotlib 3.4.3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
x = np.linspace(0,10,100)
y = np.sin(x)*(1+x)
fig, ax = plt.subplots()
ax.plot(x,y,label='a')
# new clear axis overlay with 0-1 limits
ax2 = plt.axes([0,0,1,1], facecolor=(1,1,1,0))
x,y = np.array([[0.05, 0.1, 0.9], [0.05, 0.5, 0.9]])
line = Line2D(x, y, lw=5., color='r', alpha=0.4)
ax2.add_line(line)
plt.show()
But if you want to align with ticks, then why not use plot coordinates?

Categories