trazar diferentes colores para diferentes niveles categóricos

7 minutos de lectura

avatar de usuario de aguacate
palta

Tengo este marco de datos diamonds que se compone de variables como (carat, price, color)y quiero dibujar un diagrama de dispersión de price a carat para cada colorque significa diferente color tiene diferente color en la trama.

Esto es fácil en R con 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 podría hacerse esto en Python usando matplotlib ?

PD:

Conozco paquetes auxiliares de trazado, como seaborn y ggplot for pythony no los prefiero, solo quiero saber si es posible hacer el trabajo usando matplotlib solo, ;P

Avatar de usuario de Ffisegydd
Ffisegydd

Importaciones y marco de datos de muestra

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns  # for sample data
from matplotlib.lines import Line2D  # for legend handle

# DataFrame used for all options
df = sns.load_dataset('diamonds')

   carat      cut color clarity  depth  table  price     x     y     z
0   0.23    Ideal     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1   0.21  Premium     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2   0.23     Good     E     VS1   56.9   65.0    327  4.05  4.07  2.31

Con matplotlib

Puedes pasar plt.scatter a c argumento, que le permite seleccionar los colores. El siguiente código define un colors diccionario para asignar los colores de los diamantes a los colores de trazado.

fig, ax = plt.subplots(figsize=(6, 6))

colors = {'D':'tab:blue', 'E':'tab:orange', 'F':'tab:green', 'G':'tab:red', 'H':'tab:purple', 'I':'tab:brown', 'J':'tab:pink'}

ax.scatter(df['carat'], df['price'], c=df['color'].map(colors))

# add a legend
handles = [Line2D([0], [0], marker="o", color="w", markerfacecolor=v, label=k, markersize=8) for k, v in colors.items()]
ax.legend(title="color", handles=handles, bbox_to_anchor=(1.05, 1), loc="upper left")

plt.show()

df['color'].map(colors) mapea efectivamente los colores de “diamante” a “trazado”.

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

Con seaborn

Puedes usar seaborn que es un envoltorio alrededor matplotlib eso hace que se vea más bonito por defecto (más bien basado en opiniones, lo sé: P) pero también agrega algunas funciones de trazado.

Para esto podrías usar seaborn.lmplot con fit_reg=False (lo que evita que automáticamente haga alguna regresión).

  • sns.scatterplot(x='carat', y='price', data=df, hue="color", ec=None) también hace lo mismo.

Seleccionando hue="color" le dice a seaborn que divida y trace los datos en función de los valores únicos en el 'color' columna.

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

ingrese la descripción de la imagen aquí

Con pandas.DataFrame.groupby & pandas.DataFrame.plot

Si no quieres usar seaborn, usa pandas.groupby para obtener los colores solos y luego trazarlos usando solo matplotlib, pero tendrá que asignar colores manualmente a medida que avanza, he agregado un ejemplo a continuación:

fig, ax = plt.subplots(figsize=(6, 6))

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 colors diccionario, que puede mapear el color del diamante (por ejemplo D) a un color real (por ejemplo tab:blue).

ingrese la descripción de la imagen aquí

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

Primero encontrar una paleta de colores te gusta y opcionalmente visualízalo:

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

Entonces puedes usarlo con matplotlib haciendo 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))

  • Gracias por proporcionar una solución genérica.

    – lcrmorin

    11 abr a las 21:38

Tenía la misma pregunta y pasé 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 agrupar/agregar y luego iterar a través de los grupos (y aún así tener que mapear colores). Simplemente sentí que era una mala implementación 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 plotnine diamantes

Tan limpio y simple 🙂

Avatar de usuario de Pablo Reyes
pablo reyes

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

La forma más fácil es simplemente pasar una matriz de niveles de categoría de enteros al plt.scatter() parámetro de color

import pandas as pd
import matplotlib.pyplot as plt 

df = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/diamonds.csv')

plt.scatter(df['carat'], df['price'], c=pd.factorize(df['color'])[0],)
plt.gca().set(xlabel="Carat", ylabel="Price", title="Carat vs. Price")

ingrese la descripción de la imagen aquí

Esto crea una trama sin leyenda, utilizando el valor predeterminado mapa de colores “viridis”. En este caso, “viridis” no es una buena opción predeterminada porque los colores parecen implicar un orden secuencial en lugar de categorías puramente nominales.

Para elegir el tuyo mapa de colores y agrega un leyendael enfoque más simple es este:

import matplotlib.patches

levels, categories = pd.factorize(df['color'])
colors = [plt.cm.tab10(i) for i in levels] # using the "tab10" colormap
handles = [matplotlib.patches.Patch(color=plt.cm.tab10(i), label=c) for i, c in enumerate(categories)]

plt.scatter(df['carat'], df['price'], c=colors)
plt.gca().set(xlabel="Carat", ylabel="Price", title="Carat vs. Price")
plt.legend(handles=handles,  title="Color")

ingrese la descripción de la imagen aquí

Elegí el mapa de colores discreto (también conocido como cualitativo) “tab10”, que hace un mejor trabajo al indicar que el factor de color es una variable categórica nominal.

Crédito adicional:
En el primer gráfico, los colores predeterminados se eligen pasando min-max escalado valores de la matriz de enteros de nivel de categoría pd.factorize(iris['species'])[0] hacia llamar metodo de la plt.cm.viridis objeto de mapa de colores.

Avatar de usuario de Nipun Batra
Nipun Batra

Usando altaïr.

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í

Avatar de usuario de Simon
Simón

Con df.plot()

Normalmente, cuando trazo rápidamente un DataFrame, uso pd.DataFrame.plot(). Esto toma el índice como el valor de x, el valor como el valor de y y traza cada columna por separado con un color diferente. Un DataFrame en este formulario se puede lograr usando set_index y 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 tiene que especificar manualmente los colores.

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

¿Ha sido útil esta solución?