80 votes

création de plus de 20 couleurs de légende uniques à l'aide de matplotlib

Je trace 20 lignes différentes sur un seul graphique en utilisant matplotlib. J'utilise une boucle for pour le tracé et j'étiquette chaque ligne avec sa clé, puis j'utilise la fonction de légende.

for key in dict.keys():
    plot(x,dict[key], label = key)
graph.legend()

Mais en utilisant cette méthode, le graphique répète beaucoup de couleurs dans la légende. Existe-t-il un moyen de s'assurer qu'une couleur unique est attribuée à chaque ligne en utilisant matplotlib et plus de 20 lignes ?

merci

0 votes

Il se trouve que la légende n'a rien à voir avec les couleurs. Il y aurait des répétitions dans la couleur, que vous ayez une légende ou non.

9 votes

C'est assez fou pour moi que matplotlib par défaut réutilise les couleurs si facilement

135voto

Yann Points 6909

La réponse à votre question est liée à deux autres questions sur les SO.

La réponse à Comment choisir une nouvelle couleur pour chaque ligne tracée dans une figure en matplotlib ? explique comment définir la liste de couleurs par défaut qui est parcourue pour choisir la couleur suivante à tracer. Ceci est fait avec la fonction Axes.set_color_cycle méthode .

Vous voulez cependant obtenir la liste correcte des couleurs, ce qui est plus facile à faire en utilisant une carte de couleurs, comme expliqué dans la réponse à cette question : Créer un générateur de couleurs à partir d'une carte de couleurs donnée dans matplotlib . Là, une carte de couleur prend une valeur de 0 à 1 et renvoie une couleur.

Ainsi, pour vos 20 lignes, vous souhaitez effectuer un cycle de 0 à 1 par pas de 1/20. Plus précisément, vous souhaitez effectuer un cycle de 0 à 19/20, car 1 renvoie à 0.

C'est ce qui est fait dans cet exemple :

import matplotlib.pyplot as plt
import numpy as np

NUM_COLORS = 20

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_color_cycle([cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
for i in range(NUM_COLORS):
    ax.plot(np.arange(10)*(i+1))

fig.savefig('moreColors.png')
plt.show()

Voici la figure qui en résulte :

Yosemitebear Mountain Giant Double Rainbow 1-8-10

Solution alternative, meilleure (discutable)

Il existe une autre méthode qui utilise un ScalarMappable pour convertir une gamme de valeurs en couleurs. L'avantage de cette méthode est que vous pouvez utiliser une méthode non linéaire pour convertir des valeurs en couleurs. Normalization pour convertir l'indice de ligne en couleur réelle. Le code suivant produit exactement le même résultat :

import matplotlib.pyplot as plt
import matplotlib.cm as mplcm
import matplotlib.colors as colors
import numpy as np

NUM_COLORS = 20

cm = plt.get_cmap('gist_rainbow')
cNorm  = colors.Normalize(vmin=0, vmax=NUM_COLORS-1)
scalarMap = mplcm.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
ax = fig.add_subplot(111)
# old way:
#ax.set_color_cycle([cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
# new way:
ax.set_color_cycle([scalarMap.to_rgba(i) for i in range(NUM_COLORS)])
for i in range(NUM_COLORS):
    ax.plot(np.arange(10)*(i+1))

fig.savefig('moreColors.png')
plt.show()

Note de dépréciation
Dans les versions plus récentes de mplib (1.5+), la fonction set_color_cycle a été supprimée au profit de la fonction ax.set_prop_cycle(color=[...]) .

0 votes

J'ai supprimé sa déclaration dans la boucle et le code semble fonctionner correctement...

17 votes

ax.set_color_map() est déprécié dans matplotlib v1.5. Utilisez ax.set_prop_cycle(color=[cm...]) à la place.

3 votes

Une liste des cartes en couleurs disponibles est disponible ici : matplotlib.org/examples/color/colormaps_reference.html

27voto

Don Kirkby Points 12671

J'avais un graphique avec 12 lignes, et j'ai eu du mal à distinguer les lignes avec des couleurs similaires lorsque j'ai essayé La technique de Yann . Mes lignes apparaissaient également par paires, j'ai donc utilisé la même couleur pour les deux lignes de chaque paire, et j'ai utilisé deux largeurs de ligne différentes. Vous pouvez également varier le style de ligne pour obtenir d'autres combinaisons.

Vous pourriez utiliser set_prop_cycle() mais j'ai juste modifié les objets de la ligne après avoir appelé plot() .

Voici l'exemple de Yann avec trois largeurs de ligne différentes :

import matplotlib.pyplot as plt
import numpy as np

NUM_COLORS = 20

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(NUM_COLORS):
    lines = ax.plot(np.arange(10)*(i+1))
    lines[0].set_color(cm(i//3*3.0/NUM_COLORS))
    lines[0].set_linewidth(i%3 + 1)

fig.savefig('moreColors.png')
plt.show()

Example plot with line widths

Voici le même exemple avec des styles de lignes différents. Bien sûr, vous pouvez combiner les deux si vous le souhaitez.

import matplotlib.pyplot as plt
import numpy as np

NUM_COLORS = 20
LINE_STYLES = ['solid', 'dashed', 'dashdot', 'dotted']
NUM_STYLES = len(LINE_STYLES)

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(NUM_COLORS):
    lines = ax.plot(np.arange(10)*(i+1))
    lines[0].set_color(cm(i//NUM_STYLES*float(NUM_STYLES)/NUM_COLORS))
    lines[0].set_linestyle(LINE_STYLES[i%NUM_STYLES])

fig.savefig('moreColors.png')
plt.show()

Example plot with line styles

0 votes

Ne serait-il pas plus agréable de faire défiler différents styles de lignes (tirets, pointillés, double tirets, tirets-pointillés, ...) pour chaque couleur ? Au cas où vous auriez besoin de faire référence aux lignes dans une légende, vous auriez du mal avec la largeur des lignes ("la ligne orange moyennement épaisse" ?). Mais, d'un autre côté, il en serait de même pour une solution avec 20 couleurs différentes comme cela a été demandé.

0 votes

Bien sûr, @NichtJens, c'est pourquoi j'ai mentionné le style de ligne comme une alternative. La largeur de ligne m'est venue à l'esprit en premier, c'est tout.

0 votes

Compris. Je voulais surtout dire que vous pourriez l'ajouter comme deuxième exemple à votre réponse pour la rendre plus complète :)

23voto

Pour se baser sur La réponse de Don Kirkby si vous êtes prêt à installer/utiliser seaborn alors vous pouvez faire calculer les couleurs pour vous :

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

NUM_COLORS = 20
LINE_STYLES = ['solid', 'dashed', 'dashdot', 'dotted']
NUM_STYLES = len(LINE_STYLES)

sns.reset_orig()  # get default matplotlib styles back
clrs = sns.color_palette('husl', n_colors=NUM_COLORS)  # a list of RGB tuples
fig, ax = plt.subplots(1)
for i in range(NUM_COLORS):
    lines = ax.plot(np.arange(10)*(i+1))
    lines[0].set_color(clrs[i])
    lines[0].set_linestyle(LINE_STYLES[i%NUM_STYLES])

fig.savefig('moreColors.png')
plt.show()

Outre la possibilité d'utiliser les différentes palettes de couleurs de seaborn, vous pouvez obtenir une liste de tuples RVB qui peuvent être utilisés/manipulés plus tard si nécessaire. Évidemment, vous pourriez calculer quelque chose de similaire en utilisant les cartes de couleurs de Matplotlib, mais je trouve que c'est très pratique. seaborn husl color map with 20 colors

0 votes

Merci ! Pour tous ceux qui veulent échantillonner les couleurs et les styles de lignes de façon unique : clrs = sns.color_palette('muted', n_colors=num_colors) product(['solid', 'dashed', 'dashdot', 'dotted'], clrs)

4voto

GoPackGo Points 129

Ces réponses semblaient plus compliquées que nécessaire. Si vous parcourez une liste en boucle pour tracer des lignes, il suffit d'énumérer la liste et d'attribuer une couleur à un point de la carte des couleurs. Disons que vous parcourez en boucle toutes les colonnes d'un cadre de données pandas :

fig, ax = plt.subplots()
cm = plt.get_cmap('gist_rainbow')
 for count, col in enumerate(df.columns):
    ax.plot(df[col], label = col, linewidth = 2, color = cm(count*20))

Cela fonctionne parce que cm est juste un dictionnaire itérable de couleurs numériques. En les multipliant par un certain facteur, on avance dans la carte des couleurs (plus de différence de couleur).

0 votes

Qu'est-ce que ColList ? Et pourquoi ne pas utiliser snail_case en Python ?

0 votes

J'ai modifié mon commentaire - ColList était censé être une liste de colonnes dans un cadre de données pandas. df.columns serait plus clair. J'utilise pandas mais vous pouvez itérer sur les données que vous voulez. Je ne suis pas familier avec snail_case.

1 votes

Très simple, merci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X