Advertencia para el usuario: FixedFormatter solo debe usarse junto con FixedLocator

7 minutos de lectura

avatar de usuario
Racional-IM

He usado durante mucho tiempo pequeñas subrutinas para formatear los ejes de los gráficos que estoy trazando. Un par de ejemplos:

def format_y_label_thousands(): # format y-axis tick labels formats
    ax = plt.gca()
    label_format="{:,.0f}"
    ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])

def format_y_label_percent(): # format y-axis tick labels formats
    ax = plt.gca()
    label_format="{:.1%}"
    ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])

Sin embargo, después de una actualización de matplotlib ayer, recibo la siguiente advertencia al llamar a cualquiera de estas dos funciones:

UserWarning: FixedFormatter should only be used together with FixedLocator
  ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])

¿Cuál es la razón de tal advertencia? No pude resolverlo mirando la documentación de matplotlib.

  • Igual aquí. Solo déjame comentar que no es un error sino una advertencia. Estoy seguro de que la gente lo mencionará en sus respuestas.

    – fra_pero

    3 sep 2020 a las 14:25

  • ¿Has leído el documento en matplotlib.org/3.3.0/api/ticker_api.html? Dice “FixedFormatter solo debe usarse junto con FixedLocator. De lo contrario, las etiquetas pueden terminar en posiciones inesperadas”. ¿Esto puede ayudar?

    – fra_pero

    3 de septiembre de 2020 a las 14:28


  • Gracias @fra_pero por el detalle de advertencia vs error. Acabo de corregirlo. Vi el material que señaló en el enlace anterior, pero no pude usar la información allí para corregir mi código. Vote la pregunta: con suerte, atraerá más atención.

    – Racional-IM

    3 sep 2020 a las 22:37

  • Parece ser un error en la última versión de matplotlib (3.3.1). encontré este informe de error en la página de github de pandas, donde descubren que la solución es emitir el set_xticks método antes de la set_xticklabels. Probablemente debería funcionar con el y dirección también.

    -Hampus Larsson

    4 sep 2020 a las 21:45

  • Problema en matplotlib: “Eso es a propósito. También debe especificar explícitamente las tres marcas para ir con la etiqueta y luego esta advertencia debería desaparecer. OTOH, estoy de acuerdo en que la advertencia es bastante misteriosa si solo está usando set_xticklabels”.

    – Hugo

    1 de noviembre de 2020 a las 8:43

avatar de usuario
Racional-IM

SOLUCIÓN ALTERNA:

La forma de evitar la advertencia es usar FixedLocator (que es parte de matplotlib.ticker). A continuación muestro un código para trazar tres gráficos. Formato sus ejes de diferentes maneras. Tenga en cuenta que “set_ticks” silencia la advertencia, pero cambia las ubicaciones/etiquetas reales de las marcas (me tomó un tiempo darme cuenta de que FixedLocator usa la misma información pero mantiene intactas las ubicaciones de las marcas). Puede jugar con las x/y para ver cómo cada solución podría afectar la salida.

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker

mpl.rcParams['font.size'] = 6.5

x = np.array(range(1000, 5000, 500))
y = 37*x

fig, [ax1, ax2, ax3] = plt.subplots(1,3)

ax1.plot(x,y, linewidth=5, color="green")
ax2.plot(x,y, linewidth=5, color="red")
ax3.plot(x,y, linewidth=5, color="blue")

label_format="{:,.0f}"

# nothing done to ax1 as it is a "control chart."

# fixing yticks with "set_yticks"
ticks_loc = ax2.get_yticks().tolist()
ax2.set_yticks(ax1.get_yticks().tolist())
ax2.set_yticklabels([label_format.format(x) for x in ticks_loc])

# fixing yticks with matplotlib.ticker "FixedLocator"
ticks_loc = ax3.get_yticks().tolist()
ax3.yaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
ax3.set_yticklabels([label_format.format(x) for x in ticks_loc])

# fixing xticks with FixedLocator but also using MaxNLocator to avoid cramped x-labels
ax3.xaxis.set_major_locator(mticker.MaxNLocator(3))
ticks_loc = ax3.get_xticks().tolist()
ax3.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
ax3.set_xticklabels([label_format.format(x) for x in ticks_loc])

fig.tight_layout()
plt.show()

GRÁFICOS DE SALIDA:

Gráficos de muestra

Obviamente, tener un par de líneas de código inactivas como la de arriba (básicamente obtengo los yticks o xticks y los configuro nuevamente) solo agrega ruido a mi programa. Preferiría que se eliminara la advertencia. Sin embargo, mire algunos de los “informes de errores” (de los enlaces en los comentarios arriba/abajo; el problema no es realmente un error: es una actualización que está generando algunos problemas), y los contribuyentes que administran matplotlib tienen sus razones para mantener la advertencia.

VERSIÓN ANTERIOR DE MATPLOTLIB:
Si usa su consola para controlar las salidas críticas de su código (como lo hago yo), los mensajes de advertencia pueden ser problemáticos. Por lo tanto, una forma de retrasar el tener que lidiar con el problema es degradar matplotlib a la versión 3.2.2. Uso Anaconda para administrar mis paquetes de Python, y aquí está el comando que se usa para degradar matplotlib:

conda install matplotlib=3.2.2

Es posible que no todas las versiones enumeradas estén disponibles. Por ejemplo, no se pudo instalar matplotlib 3.3.0 aunque aparece en la página de lanzamientos de matplotlib: https://github.com/matplotlib/matplotlib/releases

  • Feliz de ayudar. Considere aceptar su propia respuesta a esta pregunta por ahora, debido a que resuelve su problema en este momento. Si encuentra una solución futura, puede actualizar esta publicación con la nueva solución.

    -Hampus Larsson

    5 sep 2020 a las 15:12


  • ¿Se ha informado de un error contra matplotlib?

    – Ilia

    30 oct 2020 a las 19:13

  • PD: Aparentemente no. Haciéndolo ahora.

    – Ilia

    30 oct 2020 a las 19:30

  • Hola @Ilya: estoy asumiendo un informe de error reciente en Github fuiste tu. Había agregado un comentario a un informe de error anterior (ver comentarios en la publicación original anterior) que se fusionó con otro problema. Con suerte, lo revisarán pronto (agregaré una nota aquí si hay una solución). Sin embargo, agregué un comentario adicional en su informe de errores. Espero que la decisión sea eliminar la advertencia cuando solo se da formato a las etiquetas de ticks existentes: de lo contrario, tendré que incluir una línea de código ‘inactiva’ para evitar la advertencia (que estropea los resultados de mi consola).

    – Racional-IM

    1 de noviembre de 2020 a las 20:15


  • Sí, ese era yo.

    – Ilia

    2 de noviembre de 2020 a las 16:36

Si alguien viene aquí usando la función axes.xaxis.set_ticklabels() (o equivalente de yaxis), no necesita usar FixedLocator, puede evitar esta advertencia usando axes.xaxis.set_ticks(values_list) ANTES DE axes.xaxis.set_ticklabels(labels_list).

avatar de usuario
ermaure

De acuerdo con este matplotlib página

# FixedFormatter should only be used together with FixedLocator. 
# Otherwise, one cannot be sure where the labels will end up.

Esto significa que uno debe hacer

positions = [0, 1, 2, 3, 4, 5]
labels = ['A', 'B', 'C', 'D', 'E', 'F']
ax.xaxis.set_major_locator(ticker.FixedLocator(positions))
ax.xaxis.set_major_formatter(ticker.FixedFormatter(labels))

Pero el problema también persistió con el ticker.LogLocator incluso si las etiquetas fueron pasadas a ticker.FixedFormatter. Así que la solución en este caso fue

  1. Definir una función de formateador

    # FuncFormatter can be used as a decorator
    @ticker.FuncFormatter
    def major_formatter(x, pos):
        return f'{x:.2f}'
    
  2. y pasar la función del formateador al FixedFormatter

    ax.xaxis.set_major_locator(ticker.LogLocator(base=10, numticks=5))
    ax.xaxis.set_major_formatter(major_formatter)
    

Vea el enlace de arriba para más detalles.

  • además: from matplotlib import ticker

    -Peter Ehrlich

    14 de enero a las 16:08

Tuve el mismo problema cuando traté de rotar las etiquetas de marca en el eje X con marcas de fecha ubicadas:

ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
ax.xaxis.set_major_locator(dates.DayLocator())

Funcionó usando el método ‘tick_params()’:

ax.tick_params(axis="x", labelrotation = 45)

avatar de usuario
espía_ruso

La solución más simple es suprimir las advertencias (esto incluye UserWarning):

import warnings
warnings.filterwarnings("ignore")

El caso de uso sería si no desea que su cuaderno jupyter en github se vea destrozado con mensajes de advertencia. A diferencia de la mayoría de las advertencias, esta se sigue repitiendo si estás en un bucle (python 3.7).

avatar de usuario
Alfredo Vázquez

Lo resolví simplemente agregando esta oración:

ax.set_xticks([1,2,3])
ax.set_xtickslabels(['Label1', 'Label2', 'Label3'])

avatar de usuario
Ahmad Asmndr

Solo usa axis.set_xticks([labels])

¿Ha sido útil esta solución?