【问题标题】:Polar plot with a 'floating' radial axis具有“浮动”径向轴的极坐标图
【发布时间】:2014-06-27 17:18:49
【问题描述】:

我正在研究一个由网格中的大量极坐标图组成的图形,所有这些极坐标图在径向轴上都有一个共同的比例。每个绘图都需要非常小才能适合图形,但是当我缩小轴的尺寸时,径向轴的刻度标签看起来拥挤且难以辨认,并且模糊了我试图绘制的数据。

例如:

import numpy as np
from matplotlib import pyplot as plt

fig, axes = plt.subplots(1, 4, figsize=(9, 2), subplot_kw=dict(polar=True))

theta = np.r_[np.linspace(0, 2*np.pi, 12), 0]
for aa in axes.flat:
    x = np.random.rand(12)
    aa.plot(theta, np.r_[x, x[0]], '-sb')
    aa.set_rlim(0, 1)

fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.5)

我意识到可以通过减小字体大小和径向刻度数来部分缓解该问题,但我更愿意避免刻度标签与我的数据完全重叠。相反,我希望有一个位于绘图之外的“浮动”径向轴,如下所示:

对于正常的笛卡尔图,我只会使用 ax.spine['left'].set_position(...),但 PolarAxesSubplot 只有一个无法偏移的 u'polar' 脊椎。有没有一种“好”的方法来为极坐标图创建一个浮动径向轴,理想情况下,它的比例和限制会更新以匹配极坐标图本身径向轴的任何变化?

【问题讨论】:

标签: python matplotlib polar-coordinates


【解决方案1】:

这不是您想要的,但它可能会提示您如何准确定位极轴的标签:

import numpy as np
from matplotlib import pyplot as plt

fig, axes = plt.subplots(1, 4, figsize=(9, 2), subplot_kw=dict(polar=True))

theta = np.r_[np.linspace(0, 2*np.pi, 12), 0]
for aa in axes.flat:
    x = np.random.rand(12)
    aa.plot(theta, np.r_[x, x[0]], '-sb')
    aa.set_rlim(0, 1)

plt.draw()
ax = axes[-1]
for r, t in zip(ax.yaxis.get_ticklocs(), ax.yaxis.get_ticklabels()):
    ax.text(np.pi/2, r, '$\cdot$'*20 + t.get_text(), ha='left', va='center',
            fontsize=10, color='0.25')
for ax in axes:
    ax.yaxis.set_ticklabels([])

fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.5)
fig.savefig('test.png', bbox_inches='tight')

【讨论】:

  • +1 非常有创意地滥用轴 text!这有点丑陋,但与@CTZhu 的回答相比,它确实具有重要优势,即当重新缩放图形时,标签将相对于极坐标图的径向轴保持在正确的位置。
【解决方案2】:

也许我们可以在上面叠加另一个图:

fig, axes = plt.subplots(1, 4, figsize=(9, 2), subplot_kw=dict(polar=True))

for aa in axes.flat:
    aa.plot(theta, r, '-sb')
    aa.set_rlim(0, 1)
    aa.set_yticklabels([])
    
box=axes[0].get_position()
axl=fig.add_axes([box.xmin/2, #put it half way between the edge of the 1st subplot and the left edge of the figure
                  0.5*(box.ymin+box.ymax), #put the origin at the same height of the origin of the polar plots
                  box.width/40, #Doesn't really matter, we will set everything invisible, except the y axis
                  box.height*0.4], #fig.subplots_adjust will not adjust this axis, so we will need to manually set the height to 0.4 (half of 0.9-0.1)
                 axisbg=None) #transparent background.
axl.spines['top'].set_visible(False)
axl.spines['right'].set_visible(False)
axl.spines['bottom'].set_visible(False)
axl.yaxis.set_ticks_position('both')
axl.xaxis.set_ticks_position('none')
axl.set_xticklabels([])
axl.set_ylim(0,1)
axl.set_ylabel('$R$\t', rotation=0)

fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.5)

编辑

原来subplots_adjust 也会影响叠加轴。如果我们检查fig 中的轴列表,叠加轴就在那里(如果您有疑问,请检查 site-packages\matplotlib\figure.py):

In [27]:

fig.axes
Out[27]:
[<matplotlib.axes.PolarAxesSubplot at 0x9714650>,
 <matplotlib.axes.PolarAxesSubplot at 0x9152730>,
 <matplotlib.axes.PolarAxesSubplot at 0x9195b90>,
 <matplotlib.axes.PolarAxesSubplot at 0x91878b0>,
 <matplotlib.axes.Axes at 0x9705a90>]

真正的问题是wspace=0.5 不仅会影响极坐标图的宽度,还会影响高度(因此纵横比保持不变)。但对于非极性叠加轴,它只影响宽度。因此,需要额外修改宽度,解决方法是:

fig, axes = plt.subplots(1, 4, figsize=(10, 2), subplot_kw=dict(polar=True))

for aa in axes.flat:
    aa.plot(theta, r, '-sb')
    aa.set_rlim(0, 1)
    aa.set_yticklabels([])

#fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.5)

box=axes[0].get_position()
axl=fig.add_axes([box.xmin/2, 
                  0.5*(box.ymin+box.ymax),
                  box.width/40,
                  box.height*0.5],
                 axisbg=None)
#fig.add_axes([box.xmin, box.ymin, box.width, box.height])
axl.spines['top'].set_visible(False)
axl.spines['right'].set_visible(False)
axl.spines['bottom'].set_visible(False)
axl.yaxis.set_ticks_position('both')
axl.xaxis.set_ticks_position('none')
axl.set_xticklabels([])
axl.set_ylim(0,1)
axl.set_ylabel('$R$\t', rotation=0)

w_pre_scl=box.width

fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.5)
ratio=axes[0].get_position().width/w_pre_scl

axlb=axl.get_position()
axl.set_position([axlb.xmin, axlb.ymin, axlb.width, axlb.height*ratio])

如果没有wspace=0.5,最后几行没有实际影响:

fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9)
#ratio=axes[0].get_position().width/w_pre_scl

#axlb=axl.get_position()
#axl.set_position([axlb.xmin, axlb.ymin, axlb.width, axlb.height*ratio])

【讨论】:

  • 没错,这看起来比 Saullo 的解决方案好很多,但正如您所说,它依赖于手动设置 box.height,一旦重新缩放图形就会中断。
  • 也许我在这里遗漏了一些东西,但是在绘图后重新调整图形的高度仍然会导致浮动轴的高度与极坐标图的径向轴之间的不匹配。重新缩放图形后再次调用最后 3 行并不能纠正不匹配。同样,如果我在绘图前将图形高度从 2 英寸更改为 3 英寸,那么浮动轴不再与极坐标图的径向轴匹配。
【解决方案3】:

基于 Saullo 的回答,这里有一个看起来更漂亮的 hack,它涉及在数据坐标中绘制刻度,然后在 x 中应用固定平移:

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import transforms

theta = np.linspace(0, 2 * np.pi, 13, endpoint=True)
fig, axes = plt.subplots(1, 4, figsize=(5, 2), subplot_kw=dict(polar=True))

for aa in axes.flat:
    aa.hold(True)
    r = np.random.rand(12)
    r = np.r_[r, r[0]]
    aa.plot(theta, r, '-sb')
    aa.set_rlim(0, 1)
    aa.set_yticklabels([])

factor = 1.1
d = axes[0].get_yticks()[-1] * factor
r_tick_labels = [0] + axes[0].get_yticks()
r_ticks = (np.array(r_tick_labels) ** 2 + d ** 2) ** 0.5
theta_ticks = np.arcsin(d / r_ticks) + np.pi / 2
r_axlabel = (np.mean(r_tick_labels) ** 2 + d ** 2) ** 0.5
theta_axlabel = np.arcsin(d / r_axlabel) + np.pi / 2

# fixed offsets in x
offset_spine = transforms.ScaledTranslation(-100, 0, axes[0].transScale)
offset_ticklabels = transforms.ScaledTranslation(-10, 0, axes[0].transScale)
offset_axlabel = transforms.ScaledTranslation(-40, 0, axes[0].transScale)

# apply these to the data coordinates of the line/ticks
trans_spine = axes[0].transData + offset_spine
trans_ticklabels = trans_spine + offset_ticklabels
trans_axlabel = trans_spine + offset_axlabel

# plot the 'spine'
axes[0].plot(theta_ticks, r_ticks, '-_k', transform=trans_spine,
             clip_on=False)

# plot the 'tick labels'
for ii in xrange(len(r_ticks)):
    axes[0].text(theta_ticks[ii], r_ticks[ii], "%.1f" % r_tick_labels[ii],
                 ha="right", va="center", clip_on=False,
                 transform=trans_ticklabels)

# plot the 'axis label'
axes[0].text(theta_axlabel, r_axlabel, '$r$', fontsize='xx-large',
             ha='right', va='center', clip_on=False, transform=trans_axlabel)

fig.savefig('test.png', bbox_inches='tight')

同样,这样做的好处是,当图形大小发生变化时,刻度的 y 位置相对于极坐标图的径向轴将保持正确。但是(在@SaulloCastro 更新之前),由于 x 偏移量以点为单位指定,并且是固定的,因此当图形大小发生变化时,浮动轴将无法正确定位,并且最终可能与极坐标图重叠:

【讨论】:

  • +1 定义text 坐标以使其依赖于半径等并不难,因此它可以随图形大小缩放...
  • @SaulloCastro 我已经考虑过了。理想情况下,我想将比例尺的 x 位置设置为轴边界框边缘左侧一些固定数量的点。我不太确定如何在保持 y 的正确缩放比例的同时做到这一点。
  • 我已经用text() 坐标更新了你的答案,包括这个想法......现在你可以以任何方式重新调整数字以保持比例......
  • @SaulloCastro 太棒了!我刚刚又进行了一次调整以正确定位轴标签。如果我真的在吹毛求疵,如果浮动轴与绘图边缘保持固定数量的点而不是绘图半径的固定比例,那会更好,但这可能和它一样好得到。
猜你喜欢
  • 2017-08-22
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 2021-02-16
  • 1970-01-01
相关资源
最近更新 更多