matplotlib等高线图标签重叠轴

时间:2014-09-16 16:13:14

标签: python matplotlib

我制作了一些contour的轮廓图,这些图通过clabel标记。问题是轮廓标签倾向于与轴重叠: enter image description here

(其他一些标签很乱,请忽略它)。对于左图,10 ^ -3和10是有问题的。在右边,10 ^ 3是唯一的问题。以下是生成其中一个的代码:

fig = plt.figure(figsize=(6,3))
ax = fig.add_subplot(121)
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xlabel(r'$T_e$ (eV)', fontsize=10)
ax.set_ylabel(r'$n_e$ (1/cm$^3$)', fontsize=10)
ax.set_xlim(0.1, 1e4)
ax.set_ylim(1e16, 1e28)
CS = ax.contour(X, Y, Z, V, colors='k')
ax.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, colors='k', fontsize=8, fmt=fmt)

有没有办法让clabel对其展示位置有更好的表现?

1 个答案:

答案 0 :(得分:5)

考虑到the examples in the documentation遭受同样的疾病,这表明解决这个问题并不会无痛。 It would seem您必须使用自动播放器,使用manual放置,或亲自动手。

作为妥协,我会尝试两件事之一。两者都开始让matplotlib为你建议标签位置,然后处理那些太靠近轴的位置。

更简单的情况,也更安全,就是摆脱那些靠近边界的clabel,填充那些轮廓线:

# based on matplotlib.pyplot.clabel example:
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)


plt.figure()
CS = plt.contour(X, Y, Z)
CLS = plt.clabel(CS, inline=1, fontsize=10)

# now CLS is a list of the labels, we have to find offending ones
thresh = 0.05  # ratio in x/y range in border to discard

# get limits if they're automatic
xmin,xmax,ymin,ymax = plt.axis()
Dx = xmax-xmin
Dy = ymax-ymin

# check which labels are near a border
keep_labels = []
for label in CLS:
    lx,ly = label.get_position()
    if xmin+thresh*Dx<lx<xmax-thresh*Dx and ymin+thresh*Dy<ly<ymax-thresh*Dy:
        # inlier, redraw it later
        keep_labels.append((lx,ly))

# delete the original lines, redraw manually the labels we want to keep
# this will leave unlabelled full contour lines instead of overlapping labels

for cline in CS.collections:
    cline.remove()
for label in CLS:
    label.remove()

CS = plt.contour(X, Y, Z)
CLS = plt.clabel(CS, inline=1, fontsize=10, manual=keep_labels)

缺点是某些标签显然会丢失,当然5%的阈值需要手动调整以适合您的特定应用。以上结果与原版相比(观看顶部):

before after

我提到的另一个解决方案是采取违规标签,查看各自Path数据的CS.collections,并尝试找到更接近图的内部的点。由于将collections数据与标签配对并非易事(因为每个轮廓级别路径及其多个段对应于CS.collections的单个元素),所以可能并不值得付出努力。特别是你可能面对水平线那么短,以至于不可能在它们上面贴上标签,你也必须估计每个标签的大小。

考虑到在你的情况下轮廓线非常简单,你也可以尝试查看每个轮廓线,并找到最接近图形中心的点。

所以,这是为了演示目的重建数据集:

# guesstimated dummy data
X,Y = np.meshgrid(np.logspace(-3,7,200),np.logspace(13,31,200))
Z = X/Y*10**21
Vrange = range(-3,5)
V = [10**k for k in Vrange]
fmt = {lev: '$10^{%d}$'%k for (k,lev) in zip(Vrange,V)}


fig = plt.figure(figsize=(3,3))
ax = fig.add_subplot(111)
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xlabel(r'$T_e$ (eV)', fontsize=10)
ax.set_ylabel(r'$n_e$ (1/cm$^3$)', fontsize=10)
ax.set_xlim(0.1, 1e4)
ax.set_ylim(1e16, 1e28)

CS = ax.contour(X, Y, Z, V, colors='k')
ax.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, colors='k', fontsize=8, fmt=fmt)

通过明确使用两个轴都是对数的,主要的想法是将上面的最后一个调用替换为clabel

# get limits if they're automatic
xmin,xmax,ymin,ymax = plt.axis()
# work with logarithms for loglog scale
# middle of the figure:
logmid = (np.log10(xmin)+np.log10(xmax))/2, (np.log10(ymin)+np.log10(ymax))/2

label_pos = []
for line in CS.collections:
    for path in line.get_paths():
        logvert = np.log10(path.vertices)

        # find closest point
        logdist = np.linalg.norm(logvert-logmid, ord=2, axis=1)
        min_ind = np.argmin(logdist)
        label_pos.append(10**logvert[min_ind,:])

# draw labels, hope for the best
ax.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, colors='k', fontsize=8, fmt=fmt, manual=label_pos)

结果(右)与原始(左)相比:

before 2 after 2

我没有做太多努力使轴注释漂亮,所以请忽略这些细节。你可以看到标签确实很好地聚集在图的中间附近。根据您的应用程序,这可能是您想要的,也可能不是。

作为最后一点,标签沿轴对角线放置的原因是XY轴的缩放比例不同。这可能导致一些标签仍然伸出轴。最简单的解决方案是考虑[xmin,ymax] - [xmax,ymin](对数)线,并找到该线与每个path的交集。如果这是值得的话,你必须非常投资:你也可以完全手动放置标签。