matplotlib 等高线图标签重叠轴 [英] matplotlib contour plot labels overlap axes

查看:43
本文介绍了matplotlib 等高线图标签重叠轴的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 contour 绘制一些轮廓图,这些轮廓图通过 clabel 进行了标记.问题是轮廓标签往往与轴重叠:

(其他一些标签比较杂乱,请忽略该标签).对于左图,10^-3 和 10 是有问题的.在右边,10 ^ 3是唯一的问题之一.这是生成其中之一的代码:

  fig = plt.figure(figsize =(6,3))斧= 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, 颜色='k')ax.clabel(CS,inline = True,inline_spacing = 3,rightside_up = True,colors ='k',fontsize = 8,fmt = fmt)

有什么方法可以使 clabel 更好地表现其位置?

解决方案

考虑到

我提到的另一种解决方案是获取有问题的标签,查看它们各自的 CS.collections 数据的 Path ,然后尝试找到一个点更接近图的内部.由于将 collections 数据与标签配对并非易事(因为每个轮廓级路径及其多个片段都对应于 CS.collections 的单个元素),因此可能不会一切都是值得的.尤其是您可能面对的水平线很短,以至于无法在上面放置标签,而且您还必须估计每个标签的大小.

<小时>

考虑到在您的情况下轮廓线相当简单,您还可以尝试查看每条轮廓线,并找到最接近图形中心的点.

因此,这是出于演示目的对数据集的重构:

# 猜测的虚拟数据X,Y = np.meshgrid(np.logspace(-3,7,200),np.logspace(13,31,200))Z = X/Y*10**21Vrange = 范围(-3,5)V = [V范围中k的10 ** k]fmt = {lev:'$ 10 ^ {%d} $'%k for zip(Vrange,V)中的(k,lev)}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, 颜色='k')ax.clabel(CS,inline = True,inline_spacing = 3,rightside_up = True,colors ='k',fontsize = 8,fmt = fmt)

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

 #自动获得限制xmin,xmax,ymin,ymax = plt.axis()# 使用对数来表示对数刻度#图的中间:logmid =(np.log10(xmin)+ np.log10(xmax))/2,(np.log10(ymin)+ np.log10(ymax))/2label_pos = []对于 CS.collections 中的行:对于line.get_paths()中的路径:logvert = np.log10(path.vertices)# 找到最近的点logdist = np.linalg.norm(logvert-logmid,ord = 2,轴= 1)min_ind = np.argmin(logdist)label_pos.append(10 ** logvert [min_ind ,:])#绘制标签,希望最好ax.clabel(CS,inline = True,inline_spacing = 3,rightside_up = True,colors ='k',fontsize = 8,fmt = fmt,manual = label_pos)

结果(第二个)与原始(第一个)比较:

我并没有付出很多努力来使轴注释变得漂亮,因此请忽略这些细节.您可以看到标签确实很好地聚集在图的中间.根据您的应用程序,这可能不是您想要的.

最后一点,标签沿轴的对角线放置的原因是沿XY<的缩放不同/code>轴.这可能会导致一些标签仍然伸出轴.最简单的解决方案是考虑 [xmin,ymax] - [xmax,ymin] (对数)线,并找到该线与每条线的交点 path s.如果值得,您必须在这方面投入大量资金:您不妨完全手动放置标签.

I'm making some contour plots with contour which are labeled via clabel. The problem is that the contour labels tend to overlap with the axes:

(some of the other labels are messy, ignore that). For the left plot, 10^-3 and 10 are problematic. On the right, 10^3 is the only problem one. Here is the code that generates one of them:

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)

Is there any way to get clabel to be better behaved about its placement?

解决方案

Considering that the examples in the documentation suffer from the same illness suggests that it won't be painless to solve this. It would seem that you have to live with the automatic ones, use manual placement, or get your hands dirty.

As a compromise, I'd try one of two things. Both start with letting matplotlib suggest label positions for you, then handling those which are too close to an axes.

The simpler case, which is also safer, is to just get rid of those clabels which are close to a border, filling those contour lines:

# 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)

The downside is that some labels will obviously be missing, and of course the 5% threshold should need manual tweaking for your specific application. Result of the above compared to the original (watch the top):

The other solution I mentioned would be to take the offending labels, look at the Paths of their respective CS.collections data, and try to find a point which is closer to the insides of the figure. Since it's not trivial to pair the collections data with the labels (since each contour level path with its multiple segments corresponds to a single element of CS.collections), it might not all be worth the effort. Especially that you could be facing level lines so short that it's impossible to place a label on them, and you'd also have to estimate the size of each label as well.


Considering that in your case the contour lines are fairly simple, you could also try looking at each contour line, and finding that point which is closest to the center of the figure.

So, here's a reconstruction of your data set for demonstration purposes:

# 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)

By making explicit use of that both of your axes are logarithmic, the main idea is to replace the last call above to clabel with:

# 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)

Result (second) compared to the original (first):

I didn't make much of an effort to make the axes annotations pretty, so please ignore these details. You can see that the labels are indeed nicely gathered near the middle of the figure. Depending on your application, this might or might not be what you want.

As a final note, the reason the labels are not placed along the diagonal of the axes is that the scaling is different along the X and Y axes. This could cause some of the labels to reach out of the axes still. The most foolproof solution would be to consider the [xmin,ymax]--[xmax,ymin] (logarithmic) line, and to find the intersection of this line with each of the paths. You have to be very invested in this if this is worth it: you might as well place your labels fully manually.

这篇关于matplotlib 等高线图标签重叠轴的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆