Создание более 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()
Вот итоговая цифра:
Альтернативное, лучшее (дискуссионное) решение
Есть альтернативный способ, который использует 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, но я считаю, что это удобно.
Эти ответы казались более сложными, чем нужно. Если вы просматриваете список для построения линий, просто выполните перечисление в списке и назначьте цвет какой-либо точке на палитре. Скажем, вы просматриваете ColList из фрейма данных pandas:
Это работает, потому что cm - это просто итеративный словарь цветных чисел. Умножив их на какой-то коэффициент, вы продвинетесь дальше в палитре (большая разница в цвете).