traçar cores diferentes para diferentes níveis categóricos usando matplotlib

102

Eu tenho esse quadro de dados diamondsque é composto de variáveis ​​como (carat, price, color), e quero desenhar um gráfico de dispersão de pricea caratpara cada um color, o que significa que diferente colortem cores diferentes no gráfico.

Isso é fácil Rcom ggplot:

ggplot(aes(x=carat, y=price, color=color),  #by setting color=color, ggplot automatically draw in different colors
       data=diamonds) + geom_point(stat='summary', fun.y=median)

insira a descrição da imagem aqui

Eu me pergunto como isso poderia ser feito em Python usando matplotlib?

PS:

Conheço pacotes auxiliares de plotagem, como seaborne ggplot for python, e não os prefiro, só quero saber se é possível fazer o trabalho usando matplotlibsozinho,; P

abacate
fonte
1
Seria muito bom ter algo assim integrado ao matplotlib, mas parece que não será fácil. Discussão aqui: github.com/matplotlib/matplotlib/issues/6214
naught101

Respostas:

156

Você pode passar plt.scatterum cargumento que lhe permitirá selecionar as cores. O código a seguir define um colorsdicionário para mapear as cores do diamante para as cores de plotagem.

import matplotlib.pyplot as plt
import pandas as pd

carat = [5, 10, 20, 30, 5, 10, 20, 30, 5, 10, 20, 30]
price = [100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600]
color =['D', 'D', 'D', 'E', 'E', 'E', 'F', 'F', 'F', 'G', 'G', 'G',]

df = pd.DataFrame(dict(carat=carat, price=price, color=color))

fig, ax = plt.subplots()

colors = {'D':'red', 'E':'blue', 'F':'green', 'G':'black'}

ax.scatter(df['carat'], df['price'], c=df['color'].apply(lambda x: colors[x]))

plt.show()

df['color'].apply(lambda x: colors[x]) mapeia efetivamente as cores de "diamante" para "plotagem".

(Perdoe-me por não colocar outra imagem de exemplo, acho que 2 é o suficiente: P)

Com seaborn

Você pode usar o seabornque é um invólucro matplotlibque o torna mais bonito por padrão (em vez de baseado em opinião, eu sei: P), mas também adiciona algumas funções de plotagem.

Para isso, você pode usar seaborn.lmplotcom fit_reg=False(o que o impede de fazer alguma regressão automaticamente).

O código a seguir usa um conjunto de dados de exemplo. Ao selecionar, hue='color'você diz ao seaborn para dividir seu dataframe com base em suas cores e, em seguida, plotar cada uma.

import matplotlib.pyplot as plt
import seaborn as sns

import pandas as pd

carat = [5, 10, 20, 30, 5, 10, 20, 30, 5, 10, 20, 30]
price = [100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600]
color =['D', 'D', 'D', 'E', 'E', 'E', 'F', 'F', 'F', 'G', 'G', 'G',]

df = pd.DataFrame(dict(carat=carat, price=price, color=color))

sns.lmplot('carat', 'price', data=df, hue='color', fit_reg=False)

plt.show()

insira a descrição da imagem aqui

Sem seabornusarpandas.groupby

Se você não quiser usar seaborn, você pode usar pandas.groupbypara obter as cores sozinhas e, em seguida, plotá-las usando apenas matplotlib, mas você terá que atribuir cores manualmente à medida que avança. Eu adicionei um exemplo abaixo:

fig, ax = plt.subplots()

colors = {'D':'red', 'E':'blue', 'F':'green', 'G':'black'}

grouped = df.groupby('color')
for key, group in grouped:
    group.plot(ax=ax, kind='scatter', x='carat', y='price', label=key, color=colors[key])

plt.show()

Este código assume o mesmo DataFrame acima e, a seguir, o agrupa com base em color. Em seguida, itera sobre esses grupos, traçando para cada um. Para selecionar uma cor, criei um colorsdicionário que pode mapear a cor do diamante (por exemplo D) para uma cor real (por exemplo red).

insira a descrição da imagem aqui

Ffisegydd
fonte
Obrigado, mas eu só quero descobrir como fazer o trabalho apenas com matplotlib.
abacate de
Sim, groupbyeu poderia fazer isso, então existe um recurso matplotlibque pode desenhar automaticamente para diferentes níveis de uma categoria usando cores diferentes, certo?
abacate de
@loganecolss Ok, entendo :) Eu editei novamente e adicionei um exemplo bem simples que usa um dicionário para mapear as cores, semelhante ao groupbyexemplo.
Ffisegydd
1
@Ffisegydd Usando o primeiro método, que é ax.scatter, como você adicionaria legendas a ele? Estou tentando usar label=df['color']e depois plt.legend()sem sucesso.
ahoosh
1
Seria melhor mudar ax.scatter(df['carat'], df['price'], c=df['color'].apply(lambda x: colors[x]))paraax.scatter(df['carat'], df['price'], c=df['color'].map(colors)
Dawei
33

Aqui está uma solução sucinta e genérica para usar uma paleta de cores do mar.

Primeiro, encontre uma paleta de cores de que goste e, opcionalmente, visualize-a:

sns.palplot(sns.color_palette("Set2", 8))

Em seguida, você pode usá-lo com matplotlibisso:

# Unique category labels: 'D', 'F', 'G', ...
color_labels = df['color'].unique()

# List of RGB triplets
rgb_values = sns.color_palette("Set2", 8)

# Map label to RGB
color_map = dict(zip(color_labels, rgb_values))

# Finally use the mapped values
plt.scatter(df['carat'], df['price'], c=df['color'].map(color_map))
Rems
fonte
2
Eu gosto da sua abordagem. Dado o exemplo acima, você também pode mapear os valores para nomes de cores simples como este: 1) definir as cores cores = {'D': 'vermelho', 'E': 'azul', 'F': 'verde ',' G ':' black '} 2) mapeie-os como você fez: ax.scatter (df [' carat '], df [' preço '], c = df [' color ']. Map (cores))
Stefan
1
Como você adicionaria um rótulo por cor neste caso?
François Leblanc
2
Para adicionar mais abstração, você pode substituir o 8em sns.color_palette("Set2", 8)por len(color_labels).
Swier de
Isso é ótimo, mas deve ser feito automaticamente por um nativo do mar. Ter que usar um mapa para variáveis ​​categóricas toda vez que você deseja traçar algo rapidamente é incrivelmente difícil. Sem mencionar a ideia idiota de eliminar a capacidade de exibir estatísticas no enredo. Seaborn está, infelizmente, diminuindo como um pacote devido a essas razões
veja
6

Usando Altair .

from altair import *
import pandas as pd

df = datasets.load_dataset('iris')
Chart(df).mark_point().encode(x='petalLength',y='sepalLength', color='species')

insira a descrição da imagem aqui

Nipun Batra
fonte
Pergunta feita para matplotlib
Chuck
6

Eu tive a mesma pergunta e passei o dia todo testando diferentes pacotes.

Eu tinha usado originalmente matlibplot: e não estava feliz com nenhuma das categorias de mapeamento para cores predefinidas; ou agrupando / agregando e iterando através dos grupos (e ainda tendo que mapear cores). Eu apenas senti que era uma implementação ruim do pacote.

Seaborn não funcionaria no meu caso, e Altair SÓ funciona dentro de um Notebook Jupyter.

A melhor solução para mim foi PlotNine, que "é uma implementação de uma gramática de gráficos em Python, e baseada em ggplot2".

Abaixo está o código plotnine para replicar seu exemplo R em Python:

from plotnine import *
from plotnine.data import diamonds

g = ggplot(diamonds, aes(x='carat', y='price', color='color')) + geom_point(stat='summary')
print(g)

exemplo de diamantes de enredo

Tão limpo e simples :)

depreciar
fonte
Pergunta feita para matplotlib
Chuck
5

Aqui está uma combinação de marcadores e cores de um mapa de cores qualitativo em matplotlib:

import itertools
import numpy as np
from matplotlib import markers
import matplotlib.pyplot as plt

m_styles = markers.MarkerStyle.markers
N = 60
colormap = plt.cm.Dark2.colors  # Qualitative colormap
for i, (marker, color) in zip(range(N), itertools.product(m_styles, colormap)):
    plt.scatter(*np.random.random(2), color=color, marker=marker, label=i)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0., ncol=4);

insira a descrição da imagem aqui

Pablo Reyes
fonte
In mpl.cm.Dark2.colors- mplnão parece estar definido em seu código e Dark2não tem atributo colors.
Shovalt
@Shovalt Obrigado pela revisão. Devia ter importado matplotlibcomo mpl, corrigi meu código usando o pltque também contém cm. Pelo menos na matplotlibversão que estou usando 2.0.0 Dark2tem atributocolors
Pablo Reyes
1
Atrasado, mas se você não tiver o atributo de cores: iter (plt.cm.Dark2 (np.linspace (0,1, N)))
Geoff Lentsch
3

Com df.plot ()

Normalmente, quando traço um DataFrame rapidamente, eu uso pd.DataFrame.plot(). Isso leva o índice como o valor x, o valor como o valor y e plota cada coluna separadamente com uma cor diferente. Um DataFrame neste formulário pode ser obtido usando set_indexe unstack.

import matplotlib.pyplot as plt
import pandas as pd

carat = [5, 10, 20, 30, 5, 10, 20, 30, 5, 10, 20, 30]
price = [100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600]
color =['D', 'D', 'D', 'E', 'E', 'E', 'F', 'F', 'F', 'G', 'G', 'G',]

df = pd.DataFrame(dict(carat=carat, price=price, color=color))

df.set_index(['color', 'carat']).unstack('color')['price'].plot(style='o')
plt.ylabel('price')

enredo

Com esse método, você não precisa especificar as cores manualmente.

Este procedimento pode fazer mais sentido para outras séries de dados. No meu caso, tenho dados de série temporal, portanto, o MultiIndex consiste em data e hora e categorias. Também é possível usar essa abordagem para mais de uma coluna para colorir, mas a legenda está ficando uma bagunça.

Simon
fonte
0

Eu normalmente faço isso usando o Seaborn, que é construído em cima do matplotlib

import seaborn as sns
iris = sns.load_dataset('iris')
sns.scatterplot(x='sepal_length', y='sepal_width',
              hue='species', data=iris); 
VICTOR RODEÑO SANCHEZ
fonte