Создание более 20 уникальных цветов легенды с помощью matplotlib

Я строю 20 разных линий на одном графике, используя matplotlib. Я использую цикл for для построения графиков и обозначаю каждую строку своим ключом, а затем использую функцию легенды

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

Но, используя этот способ, график повторяет много цветов в легенде. Есть ли способ гарантировать, что уникальный цвет назначается каждой строке, используя matplotlib и более 20 строк?

Спасибо

4 ответа

Решение

Ответ на ваш вопрос связан с двумя другими вопросами SO.

Ответ на вопрос Как выбрать новый цвет для каждой построенной линии на рисунке в matplotlib? объясняет, как определить список цветов по умолчанию, который циклически проходит, чтобы выбрать следующий цвет для построения. Это сделано с Axes.set_color_cycle метод.

Однако вы хотите получить правильный список цветов, и это проще всего сделать с помощью карты цветов, как объясняется в ответе на этот вопрос: создайте генератор цветов из данной карты цветов в matplotlib. Там цветовая карта принимает значение от 0 до 1 и возвращает цвет.

Таким образом, для ваших 20 строк вы хотите переключаться с 0 на 1 с шагом 1/20. В частности, вы хотите циклически изменить форму от 0 до 19/20, потому что 1 отображается обратно в 0.

Это сделано в этом примере:

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

Вот итоговая цифра:

Yosemitebear Mountain Giant Двойная радуга 1-8-10

Альтернативное, лучшее (дискуссионное) решение

Есть альтернативный способ, который использует ScalarMappable Объект для преобразования диапазона значений в цвета. Преимущество этого метода в том, что вы можете использовать нелинейный Normalization преобразовать из индекса строки в фактический цвет. Следующий код дает тот же точный результат:

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

Амортизационная записка
В более поздних версиях mplib (1.5+) set_color_cycle функция устарела в пользу ax.set_prop_cycle(color=[...]),

У меня был сюжет с 12 линиями, и мне было трудно различать линии с похожими цветами, когда я попробовал технику Янна. Мои линии также появлялись парами, поэтому я использовал один и тот же цвет для двух линий в каждой паре и использовал две разные ширины линии. Вы также можете изменить стиль линии, чтобы получить больше комбинаций.

Вы могли бы использовать set_prop_cycle(), но я просто изменил строковые объекты после вызова plot(),

Вот пример Янна с тремя разными значениями ширины линии:

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

Пример графика с шириной линии

Вот тот же пример с разными стилями линий. Конечно, вы можете объединить их, если хотите.

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

Пример графика со стилями линий

Если исходить из ответа Дона Киркби, если вы хотите установить / использовать seaborn, то вы можете рассчитать цвета для себя:

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

Помимо возможности использовать различные цветовые палитры Seaborn, вы можете получить список кортежей RGB, которые можно использовать / манипулировать позже, если это будет необходимо. Очевидно, что вы можете вычислить что-то похожее, используя цветовые карты matplotlib, но я считаю, что это удобно. Цветовая карта морского побережья с 20 цветами

Эти ответы казались более сложными, чем нужно. Если вы просматриваете список для построения линий, просто выполните перечисление в списке и назначьте цвет какой-либо точке на палитре. Скажем, вы просматриваете ColList из фрейма данных pandas:

Это работает, потому что cm - это просто итеративный словарь цветных чисел. Умножив их на какой-то коэффициент, вы продвинетесь дальше в палитре (большая разница в цвете).

Другие вопросы по тегам