2014-09-16 19 views
6

Ich mache einige Konturplots mit contour, die über clabel beschriftet sind. Das Problem ist, dass die Konturetiketten dazu neigen, mit den Achsen zu überlappen: enter image description herematplotlib Kontur Plot Etiketten überlappen Achsen

(einige der anderen Etiketten sind chaotisch, ignorieren Sie das). Für die linke Darstellung sind 10^-3 und 10 problematisch. Auf der rechten Seite ist 10^3 das einzige Problem. Hier ist der Code, der eine von ihnen erzeugt:

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) 

Gibt es eine Möglichkeit clabel, um besser über seine Platzierung artig zu sein?

Antwort

3

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

beforeafter

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

before 2after 2

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.

+0

Danke dafür. Benutzte es, um hässliche Randunterbrechungen in einem gekachelten Kartendienst (z. B. Luftdruckkonturen) zu stoppen. –