matplotlib等高线图标签重叠轴

Ale*_*x Z 8 python matplotlib

我正在制作一些contour标有通过标记的等高线图clabel.问题是轮廓标签倾向于与轴重叠: 在此输入图像描述

(其他一些标签很乱,请忽略它).对于左图,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)
Run Code Online (Sandbox Code Playgroud)

有没有什么方法clabel可以更好地表现它的位置?

And*_*eak 7

考虑到文档的例子患有相同的疾病,这表明解决这个问题并不是一件容易的事情.看起来你必须忍受自动的,使用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)
Run Code Online (Sandbox Code Playgroud)

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

之前 后

我提到的另一个解决方案是采取违规标签,查看Path各自CS.collections数据的s ,并尝试找到更接近图的内部的点.由于将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)
Run Code Online (Sandbox Code Playgroud)

通过明确地使用两个轴都是对数的,主要的想法是将上面的最后一个调用替换为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)
Run Code Online (Sandbox Code Playgroud)

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

2之前 2之后

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

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