Cómo dar colores personalizados a un gráfico de barras de pandas / matplotlib

85

Acabo de comenzar a usar pandas / matplotlib como reemplazo de Excel para generar gráficos de barras apiladas. Me estoy encontrando con un problema

(1) solo hay 5 colores en el mapa de colores predeterminado, por lo que si tengo más de 5 categorías, los colores se repiten. ¿Cómo puedo especificar más colores? Idealmente, ¿un degradado con un color inicial y un color final, y una forma de generar dinámicamente n colores intermedios?

(2) los colores no son muy agradables visualmente. ¿Cómo especifico un conjunto personalizado de n colores? O bien, un degradado también funcionaría.

A continuación se muestra un ejemplo que ilustra ambos puntos anteriores:

  4 from matplotlib import pyplot
  5 from pandas import *
  6 import random
  7 
  8 x = [{i:random.randint(1,5)} for i in range(10)]
  9 df = DataFrame(x)
 10 
 11 df.plot(kind='bar', stacked=True)

Y el resultado es este:

ingrese la descripción de la imagen aquí

vasek1
fuente
Existe una forma bastante sencilla de obtener un mapa de colores parcial. Vea esta solución a continuación
Ted Petrou

Respuestas:

118

Puede especificar la coloropción como una lista directamente a la plotfunción.

from matplotlib import pyplot as plt
from itertools import cycle, islice
import pandas, numpy as np  # I find np.random.randint to be better

# Make the data
x = [{i:np.random.randint(1,5)} for i in range(10)]
df = pandas.DataFrame(x)

# Make a list by cycling through the colors you care about
# to match the length of your data.
my_colors = list(islice(cycle(['b', 'r', 'g', 'y', 'k']), None, len(df)))

# Specify this list of colors as the `color` option to `plot`.
df.plot(kind='bar', stacked=True, color=my_colors)

Para definir su propia lista personalizada, puede hacer algunas de las siguientes acciones, o simplemente buscar las técnicas de Matplotlib para definir un elemento de color por sus valores RGB, etc. Puede complicarse tanto como quiera con esto.

my_colors = ['g', 'b']*5 # <-- this concatenates the list to itself 5 times.
my_colors = [(0.5,0.4,0.5), (0.75, 0.75, 0.25)]*5 # <-- make two custom RGBs and repeat/alternate them over all the bar elements.
my_colors = [(x/10.0, x/20.0, 0.75) for x in range(len(df))] # <-- Quick gradient example along the Red/Green dimensions.

El último ejemplo produce el siguiente degradado simple de colores para mí:

ingrese la descripción de la imagen aquí

No jugué con él el tiempo suficiente para descubrir cómo forzar a la leyenda a recoger los colores definidos, pero estoy seguro de que puedes hacerlo.

Sin embargo, en general, un gran consejo es usar las funciones de Matplotlib directamente. Llamarlos desde Pandas está bien, pero creo que obtienes mejores opciones y rendimiento llamándolos directamente desde Matplotlib.

ely
fuente
3
Error menor: my_colors = [ciclo (['b', 'r', 'g', 'y', 'k']). Next () for i in range (len (df))] dará 'b' cada vez en Python 2.7. Debería usar list (islice (cycle (['b', 'r', 'g', 'y', 'k']), None, len (df))) en su lugar.
vkontori
Gracias, probablemente no me habría dado cuenta. Otra opción es crear el ciclo primero, luego simplemente llamar a su nextfunción dentro de la comprensión.
ely
Sip. it = ciclo (['b', 'r', 'g', 'y', 'k']); my_colors = [next (it) for i in xrange (len (df))] lo cortaría también ...
vkontori
1
Con pandas y matplotlib instalados hoy, el código anterior no genera nada para mí, aunque se ejecuta.
kakyo
@kakyo ¿Está ejecutando en el intérprete regular, IPython o desde el shell (o algo más)? Dependiendo del tipo de entorno en el que ejecute este código, es posible que deba activar el modo interactivo para matplotlib o configurarlo pylab.ion()para pylab interactivo.
ely
15

Para obtener una respuesta más detallada sobre cómo crear sus propios mapas de color, le sugiero que visite esta página

Si esa respuesta es demasiado trabajo, puede hacer rápidamente su propia lista de colores y pasarlos al colorparámetro. Todos los cmmapas de colores están en el módulo matplotlib. Obtengamos una lista de 30 valores de color RGB (más alfa) del mapa de colores inferno invertido. Para hacerlo, primero obtenga el mapa de colores y luego pásele una secuencia de valores entre 0 y 1. Aquí, usamos np.linspacepara crear 30 valores igualmente espaciados entre .4 y .8 que representan esa parte del mapa de colores.

from matplotlib import cm
color = cm.inferno_r(np.linspace(.4, .8, 30))
color

array([[ 0.865006,  0.316822,  0.226055,  1.      ],
       [ 0.851384,  0.30226 ,  0.239636,  1.      ],
       [ 0.832299,  0.283913,  0.257383,  1.      ],
       [ 0.817341,  0.270954,  0.27039 ,  1.      ],
       [ 0.796607,  0.254728,  0.287264,  1.      ],
       [ 0.775059,  0.239667,  0.303526,  1.      ],
       [ 0.758422,  0.229097,  0.315266,  1.      ],
       [ 0.735683,  0.215906,  0.330245,  1.      ],
       .....

Entonces podemos usar esto para trazar, usando los datos de la publicación original:

import random
x = [{i: random.randint(1, 5)} for i in range(30)]
df = pd.DataFrame(x)
df.plot(kind='bar', stacked=True, color=color, legend=False, figsize=(12, 4))

ingrese la descripción de la imagen aquí

Ted Petrou
fuente
2
Aquí está la documentación para otros mapas de color además de inferno_r: matplotlib.org/examples/color/colormaps_reference.html
tsando
1
Seguí este fragmento, pero mi matriz de colores siempre tiene los mismos valores.
FaCoffee