trazar diferentes colores para diferentes niveles categóricos usando matplotlib


102

Tengo este marco de datos diamondsque se compone de variables como (carat, price, color), y quiero dibujar un diagrama de dispersión de pricea caratpara cada una color, lo que significa que diferente colortiene un color diferente en el diagrama.

Esto es fácil Rcon 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)

ingrese la descripción de la imagen aquí

Me pregunto cómo se podría hacer esto en Python usando matplotlib.

PD:

Sé acerca de los paquetes de trazado auxiliares, como seaborny ggplot for python, y no los prefiero, solo quiero saber si es posible hacer el trabajo usando matplotlibsolo,; P


1
Sería muy bueno tener algo como esto integrado en matplotlib, pero parece que no será fácil. Discusión aquí: github.com/matplotlib/matplotlib/issues/6214
naught101

Respuestas:


156

Puede pasar plt.scatterun cargumento que le permitirá seleccionar los colores. El siguiente código define un colorsdiccionario para asignar los colores de su diamante a los colores de trazado.

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]) mapea eficazmente los colores de "diamante" a "trazado".

(Perdóname por no poner otra imagen de ejemplo, creo que 2 es suficiente: P)

Con seaborn

Puede usar seaborncuál es un envoltorio matplotlibque lo hace lucir más bonito por defecto (más bien basado en opiniones, lo sé: P) pero también agrega algunas funciones de trazado.

Para esto, puede usar seaborn.lmplotcon fit_reg=False(lo que evita que automáticamente haga alguna regresión).

El siguiente código utiliza un conjunto de datos de ejemplo. Al seleccionar, hue='color'le dice a seaborn que divida su marco de datos en función de sus colores y luego trace cada uno.

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()

ingrese la descripción de la imagen aquí

Sin seabornusarpandas.groupby

Si no desea usar seaborn, puede usar pandas.groupbypara obtener los colores solo y luego trazarlos usando solo matplotlib, pero tendrá que asignar colores manualmente a medida que avanza, agregué un ejemplo a continuación:

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 asume el mismo DataFrame que el anterior y luego lo agrupa en función de color. Luego itera sobre estos grupos, trazando para cada uno. Para seleccionar un color, he creado un colorsdiccionario que puede asignar el color del diamante (por ejemplo D) a un color real (por ejemplo red).

ingrese la descripción de la imagen aquí


Gracias, pero solo quiero saber cómo hacer el trabajo solo con matplotlib.
aguacate

Sí, a través de groupbymí podría hacer eso, por lo que existe una característica matplotlibque puede dibujar automáticamente para diferentes niveles de una categoría usando diferentes colores, ¿verdad?
aguacate

@loganecolss Ok, veo :) Lo he editado de nuevo y agregué un ejemplo muy simple que usa un diccionario para mapear los colores, de manera similar al groupbyejemplo.
Ffisegydd

1
@Ffisegydd Usando el primer método, que es ax.scatter, ¿cómo le agregarías leyendas? Estoy tratando de usar label=df['color']y luego plt.legend()sin éxito.
ahoosh

1
Sería mejor cambiar ax.scatter(df['carat'], df['price'], c=df['color'].apply(lambda x: colors[x]))aax.scatter(df['carat'], df['price'], c=df['color'].map(colors)
Dawei

33

Aquí hay una solución sucinta y genérica para usar una paleta de colores marina.

Primero busque una paleta de colores que le guste y, opcionalmente, visualícela:

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

Entonces puedes usarlo para matplotlibhacer esto:

# 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))

2
Me gusta tu enfoque. Dado el ejemplo anterior, por supuesto, también puede asignar los valores a nombres de colores simples como este: 1) definir los colores colors = {'D': 'red', 'E': 'blue', 'F': 'green ',' G ':' black '} 2) mapearlos como lo hizo: ax.scatter (df [' quilate '], df [' price '], c = df [' color ']. Map (colors))
Stefan

1
Sin embargo, ¿cómo agregaría una etiqueta por color en este caso?
François Leblanc

2
Para agregar algo más de abstracción, puede reemplazar el 8in sns.color_palette("Set2", 8)por len(color_labels).
Swier

Esto es genial, pero seaborn debería hacerlo automáticamente. Tener que usar un mapa para variables categóricas cada vez que desee trazar algo rápidamente es increíblemente complicado. Sin mencionar la idea idiota de eliminar la capacidad de mostrar estadísticas en la trama. Seaborn, desafortunadamente, está disminuyendo como paquete debido a estas razones
persecución

8

Tenía la misma pregunta y me he pasado todo el día probando diferentes paquetes.

Originalmente había usado matlibplot: y no estaba contento con la asignación de categorías a colores predefinidos; o agrupando / agregando luego iterando a través de los grupos (y aún teniendo que mapear colores). Simplemente sentí que era una implementación deficiente del paquete.

Seaborn no funcionaría en mi caso, y Altair SOLO funciona dentro de un Jupyter Notebook.

La mejor solución para mí fue PlotNine, que "es una implementación de una gramática de gráficos en Python y está basada en ggplot2".

A continuación se muestra el código plotnine para replicar su ejemplo de R en 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)

ejemplo de diamantes de plotnine

Tan limpio y simple :)


Pregunta realizada por matplotlib
Chuck

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')

ingrese la descripción de la imagen aquí


Pregunta realizada por matplotlib
Chuck

5

Aquí una combinación de marcadores y colores de un mapa de colores cualitativo en 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);

ingrese la descripción de la imagen aquí


En mpl.cm.Dark2.colors- mplno parece estar definido en su código y Dark2no tiene atributo colors.
Shovalt

@Shovalt Gracias por la revisión. Debería haber importado matplotlibcomo mpl, he corregido mi código usando pltque también contiene cm. Al menos en la matplotlibversión que estoy usando 2.0.0 Dark2tiene atributocolors
Pablo Reyes

1
Tarde, pero si no tiene el atributo de colores: iter (plt.cm.Dark2 (np.linspace (0,1, N)))
Geoff Lentsch

3

Con df.plot ()

Normalmente, cuando trazo rápidamente un DataFrame, uso pd.DataFrame.plot(). Esto toma el índice como el valor x, el valor como el valor y y traza cada columna por separado con un color diferente. Un DataFrame en esta forma se puede lograr usando set_indexy 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')

trama

Con este método no es necesario especificar los colores manualmente.

Este procedimiento puede tener más sentido para otras series de datos. En mi caso, tengo datos de series de tiempo, por lo que MultiIndex consta de fecha y hora y categorías. También es posible utilizar este enfoque para colorear más de una columna, pero la leyenda se está complicando.


0

Normalmente lo hago usando Seaborn, que está construido sobre matplotlib

import seaborn as sns
iris = sns.load_dataset('iris')
sns.scatterplot(x='sepal_length', y='sepal_width',
              hue='species', data=iris); 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.