matplotlib contorno trazar tags se superponen ejes

Estoy haciendo algunos trazados de contour con contour que están etiquetados a través de clabel . El problema es que las tags de contorno tienden a superponerse con los ejes: introduzca la descripción de la imagen aquí

(Algunas de las otras tags están desordenadas, ignórelas). Para el gráfico de la izquierda, 10 ^ -3 y 10 son problemáticos. A la derecha, 10 ^ 3 es el único problema. Aquí está el código que genera uno de ellos:

 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) 

¿Hay alguna manera de conseguir que clabel se comporte mejor sobre su ubicación?

Teniendo en cuenta que los ejemplos en la documentación sufren la misma enfermedad, sugiere que no será fácil resolver esto. Parece que tiene que vivir con los automáticos, usar manual colocación manual o ensuciarse las manos.

Como compromiso, probaría una de dos cosas. Ambos comienzan con dejar que matplotlib sugiera posiciones de tags para usted, y luego manejar aquellas que están demasiado cerca de un eje.

El caso más simple, que también es más seguro, es simplemente deshacerse de los clabel que están cerca de un borde, rellenando esas líneas de contorno:

 # 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 

La desventaja es que obviamente faltarán algunas tags, y por supuesto, el umbral del 5% debería necesitar ajustes manuales para su aplicación específica. Resultado de lo anterior comparado con el original (ver la parte superior):

antes de después

La otra solución que mencioné sería tomar las tags ofensivas, observar los Path de sus respectivos datos de CS.collections datos de CS.collections e intentar encontrar un punto que esté más cerca del interior de la figura. Dado que no es trivial emparejar los datos de las collections con las tags (ya que cada ruta de nivel de contorno con sus múltiples segmentos corresponde a un solo elemento de CS.collections ), puede que no valga la pena. Especialmente, podría estar frente a líneas de nivel tan cortas que es imposible colocarles una etiqueta, y también tendría que estimar el tamaño de cada etiqueta.


Teniendo en cuenta que, en su caso, las líneas de contorno son bastante simples, también puede intentar observar cada una de ellas y encontrar el punto más cercano al centro de la figura.

Por lo tanto, aquí está una reconstrucción de su conjunto de datos para fines de demostración:

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

Al hacer un uso explícito de que ambos ejes son logarítmicos, la idea principal es reemplazar la última llamada anterior a clabel con:

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

Resultado (derecha) comparado con el original (izquierda):

antes del 2 despues de 2

No hice un gran esfuerzo para que las anotaciones de los ejes fueran bonitas, así que ignora estos detalles. Puedes ver que las tags están bien reunidas cerca del centro de la figura. Dependiendo de su aplicación, esto podría o no ser lo que desea.

Como nota final, la razón por la que las tags no se colocan a lo largo de la diagonal de los ejes es que la escala es diferente a lo largo de los ejes X e Y Esto podría hacer que algunas de las tags se salgan de los ejes todavía. La solución más infalible sería considerar la línea [xmin,ymax] - [xmax,ymin] (logarítmica), y encontrar la intersección de esta línea con cada una de las path s. Debe valer la pena invertir en esto si merece la pena: también puede colocar sus tags de forma totalmente manual.