Como dar cores personalizadas a um gráfico de barras de pandas / matplotlib

86

Acabei de começar a usar o pandas / matplotlib como substituto do Excel para gerar gráficos de barras empilhadas. Estou tendo um problema

(1) existem apenas 5 cores no mapa de cores padrão, então se eu tiver mais de 5 categorias, as cores se repetem. Como posso especificar mais cores? Idealmente, um gradiente com uma cor inicial e uma cor final e uma maneira de gerar dinamicamente n cores entre elas?

(2) as cores não são muito agradáveis ​​visualmente. Como especifico um conjunto personalizado de n cores? Ou um gradiente também funcionaria.

Um exemplo que ilustra ambos os pontos acima está abaixo:

  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)

E a saída é esta:

insira a descrição da imagem aqui

vasok1
fonte
Existe uma maneira muito fácil de obter um mapa de cores parcial. Veja esta solução abaixo
Ted Petrou

Respostas:

120

Você pode especificar a coloropção como uma lista diretamente para a plotfunção.

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 sua própria lista personalizada, você pode fazer alguns dos seguintes, ou apenas procurar as técnicas Matplotlib para definir um item de cor por seus valores RGB, etc. Você pode complicar tanto quanto quiser com isso.

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.

O último exemplo produz o seguinte gradiente simples de cores para mim:

insira a descrição da imagem aqui

Não brinquei com isso o suficiente para descobrir como forçar a legenda a pegar as cores definidas, mas tenho certeza de que você pode fazer isso.

Em geral, porém, um grande conselho é apenas usar as funções do Matplotlib diretamente. Ligá-los do Pandas está OK, mas acho que você obtém melhores opções e desempenho ligando diretamente do Matplotlib.

ely
fonte
3
Pequeno bug: my_colors = [cycle (['b', 'r', 'g', 'y', 'k']). Next () para i no intervalo (len (df))] dará 'b' sempre no python 2.7. Você deve usar list (islice (cycle (['b', 'r', 'g', 'y', 'k']), None, len (df))).
vkontori
Obrigado, provavelmente não teria percebido. Outra opção é criar primeiro o ciclo, depois basta chamar sua nextfunção dentro da compreensão.
ely
Sim. it = ciclo (['b', 'r', 'g', 'y', 'k']); my_colors = [next (it) for i in xrange (len (df))] iria cortá-lo também ...
vkontori
1
Com o pandas e o matplotlib instalados hoje, o código acima não gera nada para mim, embora seja executado.
kakyo,
@kakyo Você está executando no interpretador regular, IPython, ou no shell (ou algo mais)? Dependendo do tipo de ambiente em que você executa este código, pode ser necessário ativar o modo interativo para matplotlib ou definir pylab.ion()para pylab interativo.
ely
54

Descobri que a maneira mais fácil é usar o colormapparâmetro .plot()com um dos gradientes de cor predefinidos:

df.plot(kind='bar', stacked=True, colormap='Paired')

insira a descrição da imagem aqui

Você pode encontrar uma grande lista de mapas de cores predefinidos aqui .

mapas de cores

Alexandre
fonte
19
no meu caso, isso produz apenas uma cor em todas as barras
tsando
15

Para uma resposta mais detalhada sobre como criar seus próprios mapas de cores, eu sugiro visitar esta página

Se essa resposta for muito trabalhosa, você pode fazer rapidamente sua própria lista de cores e passá-las para o colorparâmetro. Todos os mapas de cores estão no cmmódulo matplotlib. Vamos obter uma lista de 30 valores de cores RGB (mais alfa) do mapa de cores do inferno reverso. Para fazer isso, primeiro obtenha o mapa de cores e, em seguida, passe a ele uma sequência de valores entre 0 e 1. Aqui, usamos np.linspacepara criar 30 valores igualmente espaçados entre .4 e .8 que representam aquela parte do mapa de cores.

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.      ],
       .....

Então, podemos usar isso para plotar, usando os dados da postagem 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))

insira a descrição da imagem aqui

Ted Petrou
fonte
2
Aqui está a documentação para outros mapas de cores além de inferno_r: matplotlib.org/examples/color/colormaps_reference.html
tsando
1
Eu segui este trecho, mas minha matriz de cores sempre tem os mesmos valores.
FaCoffee