In Anbetracht, dass the examples in the documentation von der gleichen Krankheit leiden schlägt vor, dass es nicht schmerzlos sein wird, dies zu lösen. It would seem, dass Sie mit den automatischen leben müssen, verwenden Sie manual
Platzierung, oder bekommen Sie Ihre Hände schmutzig.
Als Kompromiss würde ich eines von zwei Dingen versuchen. Beide beginnen damit, matplotlib
Etikettenpositionen für Sie vorschlagen zu lassen und dann diejenigen zu behandeln, die zu nah an einer Achse liegen.
Je einfacher Fall, der auch sicherer ist, ist nur dieser clabel
s loszuwerden, die zu einer Grenze nahe, diese Konturlinien Füllung:
# 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)
Der Nachteil ist, dass einige Etiketten offensichtlich sein wird, fehlt, und natürlich sollte die 5% -Schwelle manuell für Ihre spezifische Anwendung optimiert werden. Ergebnis der oben im Vergleich zum ursprünglichen (beobachten Sie die oben):
Die andere Lösung, die ich erwähnt wäre die beanstandeten Etiketten zu nehmen, sehen Sie sich die Path
s ihrer jeweiligen CS.collections
Daten und versuchen, um einen Punkt zu finden, der näher am Inneren der Figur liegt. Da es nicht trivial ist, die collections
Daten mit den Labels zu paaren (da jeder Pfad der Konturebene mit seinen mehreren Segmenten einem einzelnen Element von CS.collections
entspricht), ist es möglicherweise nicht die Mühe wert. Vor allem, dass Sie sich mit Ebenen konfrontiert sehen, die so kurz sind, dass es unmöglich ist, ein Etikett darauf zu platzieren, und Sie müssten auch die Größe jedes Etiketts schätzen.
Bedenkt man, dass in Ihrem Fall die Konturlinien ziemlich einfach sind, könnten Sie auch die bei jeder Konturlinie versuchen suchen, und diesen Punkt zu finden, die am nächsten an der Mitte der Figur ist.
So, hier ist eine Rekonstruktion der Daten zu Demonstrationszwecken eingestellt:
# 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)
Durch die explizite Verwendung zu machen, dass Ihre beiden Achsen sind logarithmisch, die Hauptidee ist, den letzten Anruf oben clabel
ersetzen :
# 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)
Ergebnis (rechts) im Vergleich zum Original (links):
Ich habe mich nicht viel Mühe gegeben, die Achsenanmerkungen schön zu machen, also bitte ignorieren Sie diese Details.Sie können sehen, dass die Etiketten tatsächlich in der Mitte der Figur schön gesammelt sind. Abhängig von Ihrer Anwendung ist dies möglicherweise nicht das, was Sie möchten.
Als abschließende Bemerkung ist der Grund, die Etiketten nicht entlang der Diagonalen der Achsen angeordnet ist, dass die Skalierung entlang der X
und Y
Achsen unterschiedlich ist. Dies könnte dazu führen, dass einige der Etiketten noch immer aus den Achsen herausragen. Die narrensicherste Lösung wäre, die [xmin,ymax]
- [xmax,ymin]
(logarithmische) Linie zu betrachten und den Schnittpunkt dieser Linie mit jedem der path
s zu finden. Hier muss man sehr investiert sein, wenn es sich lohnt: Sie können Ihre Etiketten auch vollständig manuell platzieren.
Danke dafür. Benutzte es, um hässliche Randunterbrechungen in einem gekachelten Kartendienst (z. B. Luftdruckkonturen) zu stoppen. –