Estoy tratando de usar la conveniencia del método de trazado de un marco de datos de pandas mientras ajusto el tamaño de la figura producida. (Estoy guardando las figuras en un archivo y las estoy mostrando en línea en un cuaderno Jupyter). Encontré que el método a continuación es exitoso la mayor parte del tiempo, excepto cuando trazo dos líneas en el mismo gráfico, luego la figura vuelve al tamaño predeterminado.
Sospecho que esto podría deberse a las diferencias entre trazar en una serie y trazar en un marco de datos.
Código de ejemplo de configuración:
data = { 'A': 90 + np.random.randn(366), 'B': 85 + np.random.randn(366) } date_range = pd.date_range('2016-01-01', '2016-12-31') index = pd.Index(date_range, name='Date') df = pd.DataFrame(data=data, index=index)
Control: este código produce el resultado esperado (un gráfico amplio):
fig = plt.figure(figsize=(10,4)) df['A'].plot() plt.savefig("plot1.png") plt.show()
Resultado:
Trazado de dos líneas: el tamaño de la figura no es (10,4)
fig = plt.figure(figsize=(10,4)) df[['A', 'B']].plot() plt.savefig("plot2.png") plt.show()
Resultado:
¿Cuál es la forma correcta de hacer esto para que el tamaño de la figura sea coherente independientemente del número de series seleccionadas?
La razón de la diferencia entre los dos casos está un poco oculta dentro de la lógica de pandas.DataFrame.plot()
. Como se puede ver en la documentación , este método permite pasar muchos argumentos de modo que manejará todo tipo de casos diferentes.
Aquí, en el primer caso, crea una figura matplotlib a través de fig = plt.figure(figsize=(10,4))
y luego traza un DataFrame de una sola columna. Ahora, la lógica interna de la función de trazado de pandas es verificar si ya hay una figura presente en la máquina de estado de matplotlib y, de ser así, usar sus ejes actuales para trazar los valores de las columnas. Esto funciona como se esperaba.
Sin embargo, en el segundo caso, los datos constan de dos columnas. Hay varias opciones sobre cómo manejar un gráfico de este tipo, incluido el uso de diferentes subgráficos con ejes compartidos o no compartidos, etc. Para que los pandas puedan aplicar cualquiera de esos posibles requisitos, creará de forma predeterminada una nueva figura a la que puede agregar los ejes para trazar. La nueva figura no conocerá la figura ya existente y su tamaño, sino que tendrá el tamaño predeterminado, a menos que especifique el argumento figsize
.
En los comentarios, dice que una posible solución es usar df[['A', 'B']].plot(figsize=(10,4))
. Esto es correcto, pero luego debe omitir la creación de su figura inicial. De lo contrario, producirá 2 cifras, lo que probablemente no sea deseado. En un cuaderno esto no será visible, pero si lo ejecuta como un script de python habitual con plt.show()
al final, se abrirán dos ventanas de figuras.
Entonces, la solución que permite que los pandas se encarguen de la creación de figuras es
import pandas as pd import matplotlib.pyplot as plt df = pd.DataFrame({"A":[2,3,1], "B":[1,2,2]}) df[['A', 'B']].plot(figsize=(10,4)) plt.show()
Una forma de eludir la creación de una nueva figura es proporcionar el argumento ax
a la pandas.DataFrame.plot(ax=ax)
, donde ax
es un eje creado externamente. Estos ejes pueden ser los ejes estándar que obtiene a través plt.gca()
.
import pandas as pd import matplotlib.pyplot as plt df = pd.DataFrame({"A":[2,3,1], "B":[1,2,2]}) plt.figure(figsize=(10,4)) df[['A', 'B']].plot(ax = plt.gca()) plt.show()
Alternativamente, use la forma más orientada a objetos que se ve en la respuesta de PaulH .
Siempre opere explícita y directamente en sus objetos Figure
y Axes
. No confíe en la máquina de estado de pyplot
. En tu caso eso significa:
fig1, ax1 = plt.subplots(figsize=(10,4)) df['A'].plot(ax=ax1) fig1.savefig("plot1.png") fig2, ax2 = plt.figure(figsize=(10,4)) df[['A', 'B']].plot(ax=ax2) fig2.savefig("plot2.png") plt.show()