tracer différentes couleurs pour différents niveaux catégoriels en utilisant matplotlib

102

J'ai cette trame de données diamondsqui est composé de variables telles que (carat, price, color), et je veux dessiner un diagramme de dispersion priceà caratchaque color, ce qui signifie différent colora une couleur différente dans la parcelle.

C'est facile Ravec 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)

entrez la description de l'image ici

Je me demande comment cela pourrait être fait en Python en utilisant matplotlib?

PS:

Je connais les packages de traçage auxiliaires, tels que seabornet ggplot for python, et je ne les préfère pas, je veux juste savoir s'il est possible de faire le travail en utilisant matplotlibseul,; P

Avocat
la source
1
Ce serait vraiment bien d'avoir quelque chose comme ça intégré à matplotlib, mais il semble que ce ne sera pas facile. Discussion ici: github.com/matplotlib/matplotlib/issues/6214
naught101

Réponses:

156

Vous pouvez passer plt.scatterun cargument qui vous permettra de sélectionner les couleurs. Le code ci-dessous définit un colorsdictionnaire pour mapper vos couleurs de diamant aux couleurs de traçage.

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]) mappe efficacement les couleurs du «diamant» au «tracé».

(Pardonnez-moi de ne pas mettre une autre image d'exemple, je pense que 2 suffit: P)

Avec seaborn

Vous pouvez utiliser seabornun wrapper matplotlibqui le rend plus joli par défaut (plutôt basé sur l'opinion, je sais: P) mais ajoute également des fonctions de traçage.

Pour cela, vous pouvez utiliser seaborn.lmplotavec fit_reg=False(ce qui l'empêche de faire automatiquement une régression).

Le code ci-dessous utilise un exemple de jeu de données. En sélectionnant, hue='color'vous dites à seaborn de diviser votre trame de données en fonction de vos couleurs, puis de tracer chacune d'elles.

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

entrez la description de l'image ici

Sans seabornutiliserpandas.groupby

Si vous ne souhaitez pas utiliser seaborn, vous pouvez utiliser pandas.groupbypour obtenir les couleurs seules, puis les tracer en utilisant uniquement matplotlib, mais vous devrez attribuer manuellement les couleurs au fur et à mesure, j'ai ajouté un exemple ci-dessous:

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

Ce code suppose le même DataFrame que ci-dessus, puis le regroupe en fonction de color. Il itère ensuite sur ces groupes, en traçant pour chacun d'eux. Pour sélectionner une couleur, j'ai créé un colorsdictionnaire qui peut mapper la couleur du diamant (par exemple D) à une couleur réelle (par exemple red).

entrez la description de l'image ici

Ffisegydd
la source
Merci, mais je veux juste savoir comment faire le travail avec matplotlib seul.
avocat
Oui, via groupbyje pourrais le faire, donc il y a une telle fonctionnalité matplotlibqui peut automatiquement dessiner pour différents niveaux d'un catégoriel en utilisant une couleur différente, non?
avocat
@loganecolss Ok je vois :) Je l'ai à nouveau édité et ajouté un exemple très simple qui utilise un dictionnaire pour mapper les couleurs, comme dans l' groupbyexemple.
Ffisegydd
1
@Ffisegydd En utilisant la première méthode qui est ax.scatter, comment y ajouteriez-vous des légendes? J'essaye d'utiliser label=df['color']et puis plt.legend()sans succès.
ahoosh
1
Il serait préférable de changer ax.scatter(df['carat'], df['price'], c=df['color'].apply(lambda x: colors[x]))pourax.scatter(df['carat'], df['price'], c=df['color'].map(colors)
Dawei
33

Voici une solution succincte et générique pour utiliser une palette de couleurs marine.

Trouvez d' abord une palette de couleurs que vous aimez et visualisez-la éventuellement:

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

Ensuite, vous pouvez l'utiliser pour matplotlibfaire ceci:

# 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
la source
2
J'aime votre approche. Compte tenu de l'exemple ci-dessus, vous pouvez bien sûr également mapper les valeurs sur des noms de couleurs simples comme celui-ci: 1) définir les couleurs colors = {'D': 'red', 'E': 'blue', 'F': 'green ',' G ':' black '} 2) mappez-les comme vous l'avez fait: ax.scatter (df [' carat '], df [' price '], c = df [' color ']. Map (colors))
Stefan
1
Mais comment ajouteriez-vous une étiquette par couleur dans ce cas?
François Leblanc
2
Pour ajouter plus d'abstraction, vous pouvez remplacer 8in sns.color_palette("Set2", 8)par len(color_labels).
Swier
C'est génial, mais cela devrait être fait automatiquement par seaborn. Le fait de devoir utiliser une carte pour les variables catégorielles chaque fois que vous voulez tracer quelque chose rapidement est extrêmement gênant. Sans parler de l'idée idiote de supprimer la possibilité d'afficher des statistiques sur l'intrigue. Seaborn est malheureusement en train de décliner en tant que package pour ces raisons
poursuite
8

J'ai eu la même question et j'ai passé toute la journée à essayer différents packages.

J'avais utilisé à l'origine matlibplot: et je n'étais pas satisfait de la correspondance des catégories avec des couleurs prédéfinies; ou regroupement / agrégation puis itération à travers les groupes (et toujours à mapper les couleurs). Je pensais juste que c'était une mauvaise mise en œuvre du package.

Seaborn ne fonctionnerait pas sur mon cas, et Altair fonctionne UNIQUEMENT à l'intérieur d'un Jupyter Notebook.

La meilleure solution pour moi était PlotNine, qui "est une implémentation d'une grammaire graphique en Python, et basée sur ggplot2".

Vous trouverez ci-dessous le code plotnine pour répliquer votre exemple 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)

exemple de plotnine diamants

Si propre et simple :)

déprekate
la source
Question posée pour matplotlib
Chuck
6

Utilisation d' Altair .

from altair import *
import pandas as pd

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

entrez la description de l'image ici

Nipun Batra
la source
Question posée pour matplotlib
Chuck
5

Voici une combinaison de marqueurs et de couleurs à partir d'une palette de couleurs qualitative 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);

entrez la description de l'image ici

Pablo Reyes
la source
In mpl.cm.Dark2.colors- mplne semble pas être défini dans votre code et Dark2n'a pas d'attribut colors.
Shovalt
@Shovalt Merci pour l'examen. J'aurais dû importer matplotlibcomme mpl, j'ai corrigé mon code en utilisant pltqui contient également cm. Au moins dans la matplotlibversion que j'utilise 2.0.0 Dark2a un attributcolors
Pablo Reyes
1
En retard, mais si vous n'avez pas l'attribut colors: iter (plt.cm.Dark2 (np.linspace (0,1, N)))
Geoff Lentsch
3

Avec df.plot ()

Normalement, lorsque je trace rapidement un DataFrame, j'utilise pd.DataFrame.plot(). Cela prend l'indice comme valeur x, la valeur comme valeur y et trace chaque colonne séparément avec une couleur différente. Un DataFrame sous cette forme peut être réalisé en utilisant set_indexet 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')

terrain

Avec cette méthode, vous n'avez pas à spécifier manuellement les couleurs.

Cette procédure peut avoir plus de sens pour d'autres séries de données. Dans mon cas, j'ai des données de séries temporelles, donc le MultiIndex se compose de datetime et de catégories. Il est également possible d'utiliser cette approche pour colorier plus d'une colonne, mais la légende se dégrade.

Simon
la source
0

Je le fais généralement en utilisant Seaborn qui est construit sur 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
la source