ti-enxame.com

criando mais de 20 cores de legenda exclusivas usando matplotlib

Estou plotando 20 linhas diferentes em um único gráfico usando o matplotlib. Eu uso um loop for para plotar e rotular cada linha com sua chave e, em seguida, uso a função legend

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

Mas dessa maneira, o gráfico repete muitas cores na legenda. Existe alguma maneira de garantir que uma cor exclusiva seja atribuída a cada linha usando matplotlib e mais de 20 linhas?

obrigado

44
msohail

A resposta para sua pergunta está relacionada a duas outras perguntas SO.

A resposta para Como escolher uma nova cor para cada linha plotada em uma figura no matplotlib? explica como definir a lista padrão de cores que é percorrida para escolher a próxima cor a ser plotada. Isso é feito com o Axes.set_color_cyclemétodo .

Porém, você deseja obter a lista correta de cores, e isso é feito com mais facilidade usando um mapa de cores, conforme explicado na resposta a esta pergunta: Crie um gerador de cores a partir do mapa de cores especificado no matplotlib . Lá, um mapa de cores assume um valor de 0 a 1 e retorna uma cor.

Portanto, para suas 20 linhas, você deseja alternar de 0 a 1 em etapas de 1/20. Especificamente, você deseja alternar do formulário de 0 a 19/20, porque 1 é mapeado de volta para 0.

Isso é feito neste exemplo:

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 é a figura resultante:

Yosemitebear Mountain Giant Double Rainbow 1-8-10

Solução alternativa melhor (discutível)

Existe uma maneira alternativa que usa um objeto ScalarMappable para converter um intervalo de valores em cores. A vantagem deste método é que você pode usar um Normalization não linear para converter do índice de linha para a cor real. O código a seguir produz o mesmo resultado exato:

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

Nota de suspensão
Nas versões mais recentes do mplib (1.5+), a função set_color_cycle Foi preterida em favor de ax.set_prop_cycle(color=[...]).

95
Yann

Eu tinha um gráfico com 12 linhas e achei difícil distinguir linhas com cores semelhantes quando tentei técnica de Yann . Minhas linhas também apareceram em pares, então usei a mesma cor para as duas linhas em cada par e usei duas larguras de linhas diferentes. Você também pode variar o estilo da linha para obter mais combinações.

Você poderia usar set_prop_cycle() , mas eu apenas modifiquei os objetos de linha depois de chamar plot().

Aqui está o exemplo de Yann com três larguras de linhas 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()

Example plot with line widths

Aqui está o mesmo exemplo com diferentes estilos de linha. Claro que você pode combinar os dois, se quiser.

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

Para criar resposta de Don Kirkby , se você estiver disposto a instalar/usar seaborn , poderá calcular as cores para você:

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

Além de poder usar as várias paletas de cores do mar, você pode obter uma lista de tuplas RGB que podem ser usadas/manipuladas posteriormente, se necessário. Obviamente, você pode calcular algo semelhante usando os mapas de cores do matplotlib, mas acho isso útil. seaborn husl color map with 20 colors

10
Alexander Von Moll