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 color
que 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)
Me pregunto cómo podría hacerse esto en Python usando matplotlib
?
PD:
Conozco paquetes auxiliares de trazado, como seaborn
y ggplot for python
y no los prefiero, solo quiero saber si es posible hacer el trabajo usando matplotlib
solo, ;P
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)
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
).
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)
Tan limpio y simple 🙂
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);
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")
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")
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.
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")
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')
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.