python - outside - no handles with labels found to put in legend.
creando más de 20 colores de leyenda únicos usando matplotlib (3)
Estoy trazando 20 líneas diferentes en una sola parcela usando matplotlib. Uso un bucle for para trazar y etiquetar cada línea con su clave y luego uso la función de leyenda
for key in dict.keys():
plot(x,dict[key], label = key)
graph.legend()
Pero de esta manera, el gráfico repite muchos colores en la leyenda. ¿Hay alguna forma de garantizar que se asigna un color único a cada línea utilizando matplotlib y más de 20 líneas?
Gracias
La respuesta a su pregunta está relacionada con otras dos preguntas de SO.
La respuesta a ¿Cómo elegir un nuevo color para cada línea trazada dentro de una figura en matplotlib? explica cómo definir la lista predeterminada de colores que se recorre para elegir el siguiente color para trazar. Esto se hace con el method Axes.set_color_cycle
.
Sin embargo, desea obtener la lista correcta de colores, y esto se hace más fácilmente utilizando un mapa de colores, como se explica en la respuesta a esta pregunta: Cree un generador de color a partir de un mapa de colores dado en matplotlib . Allí, un mapa de color toma un valor de 0 a 1 y devuelve un color.
Entonces, para tus 20 líneas, quieres pasar de 0 a 1 en pasos de 1/20. Específicamente desea cambiar de 0 a 19/20, porque 1 se asigna de nuevo a 0.
Esto se hace en este ejemplo:
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()
Esta es la figura resultante:
Alternativa, mejor solución (discutible):
Hay una forma alternativa que utiliza un objeto ScalarMappable
para convertir un rango de valores a colores. La ventaja de este método es que puede usar una Normalization
no lineal para convertir el índice de línea al color real. El siguiente código produce el mismo resultado exacto:
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()
Para aprovechar la respuesta de Don Kirkby , si está dispuesto a instalar / usar el seaborn , entonces puede calcular los colores para usted:
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()
Además de poder usar las diversas paletas de colores de Seaborn, puede obtener una lista de tuplas RGB que se pueden usar / manipular más adelante si es necesario. Obviamente, podrías calcular algo similar utilizando los mapas de colores de matplotlib, pero esto me parece útil.
Tuve una trama con 12 líneas, y me resultó difícil distinguir las líneas con colores similares cuando probé la técnica de Yann . Mis líneas también aparecieron en pares, así que usé el mismo color para las dos líneas en cada par, y usé dos anchos de línea diferentes. También puede variar el estilo de línea para obtener más combinaciones.
Podría usar set_prop_cycle()
, pero modifiqué los objetos de línea después de llamar a plot()
.
Aquí está el ejemplo de Yann con tres anchos de línea diferentes:
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()
Aquí está el mismo ejemplo con diferentes estilos de línea. Por supuesto que podrías combinar los dos si quisieras.
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()