web-dev-qa-db-fra.com

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

Je trace 20 lignes différentes sur une seule parcelle en utilisant matplotlib. J'utilise une boucle for pour tracer et étiqueter 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 de cette façon, le graphique répète beaucoup de couleurs dans la légende. Existe-t-il un moyen de garantir qu'une couleur unique est attribuée à chaque ligne en utilisant matplotlib et plus de 20 lignes?

merci

44
msohail

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

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

Cependant, vous voulez obtenir la bonne liste de couleurs, et cela est plus facile à faire en utilisant une carte de couleurs, comme expliqué dans la réponse à cette question: Créez 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.

Donc, pour vos 20 lignes, vous voulez passer de 0 à 1 par pas de 1/20. Plus précisément, vous souhaitez faire défiler les formulaires de 0 à 19/20, car 1 correspond à 0.

Cela se 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 le résultat:

Yosemitebear Mountain Giant Double Rainbow 1-8-10

Alternative, meilleure solution (discutable)

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

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

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

95
Yann

J'avais un tracé avec 12 lignes, et j'ai eu du mal à distinguer les lignes avec des couleurs similaires lorsque j'ai essayé technique de Yann . Mes lignes sont également apparues 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 plus de combinaisons.

Vous pouvez utiliser set_prop_cycle() , mais je viens de modifier les objets 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 différents styles de ligne. 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

17
Don Kirkby

Pour construire à partir de réponse de Don Kirkby , si vous êtes prêt à installer/utiliser seaborn , alors vous pouvez avoir des couleurs calculées 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()

En plus de pouvoir 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. De toute évidence, vous pouvez calculer quelque chose de similaire en utilisant les cartes de couleurs de matplotlib, mais je trouve cela pratique. seaborn husl color map with 20 colors

10
Alexander Von Moll