matplotlib轮廓图标签重叠坐标轴
我正在制作一些轮廓图,通过clabel
标记contour
。 问题是轮廓标签倾向于与轴重叠:
(其他一些标签很混乱,忽略)。 对于左图,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
更好clabel
它的位置?
考虑到文档中的例子患有同样的疾病,这表明解决这个问题不会很轻松。 看起来你必须与自动生活,使用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%的阈值应该需要针对您的特定应用进行手动调整。 以上结果与原来的相比(观看顶部):
我提到的另一种解决方案是采取违规标签,查看各自CS.collections
数据的Path
,并尝试找到更接近图形内部的点。 由于将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)
结果(右)与原始(左)相比较:
我没有做很多努力来使轴标注漂亮,所以请忽略这些细节。 你可以看到标签确实很好地聚集在图的中间附近。 根据您的应用程序,这可能会或可能不是您想要的。
作为最后一点,标签不沿着轴的对角线放置的原因是沿着X
轴和Y
轴的缩放比例是不同的。 这可能会导致一些标签仍然伸出轴。 最简单的解决方案是考虑[xmin,ymax]
- [xmax,ymin]
(对数)线,并找出这条线与每条path
的交点。 如果这是值得的,你必须在这方面投入很多:你也可以完全手动地放置你的标签。