Получение отдельных цветов из цветовой карты в matplotlib
Если у вас есть Colormap из
cmap = matplotlib.cm.get_cmap('Spectral')
Как вы можете получить определенный цвет от 0 до 1, где 0.0 - первый цвет на карте, а 1.0 - последний цвет на карте?
В идеале я бы смог получить средний цвет на карте, выполнив:
>>> do_some_magic(cmap, 0.5) # Return an RGBA tuplet
(0.1, 0.2, 0.3, 1.0)
10 ответов
Вы можете сделать это с помощью приведенного ниже кода, и код в вашем вопросе на самом деле был очень близок к тому, что вам нужно, все, что вам нужно сделать, это вызвать cmap
объект у вас есть.
import matplotlib
cmap = matplotlib.cm.get_cmap('Spectral')
rgba = cmap(0.5)
print(rgba) # (0.99807766255210428, 0.99923106502084169, 0.74602077638401709, 1.0)
Для значений за пределами диапазона [0.0, 1.0] он вернет нижний и верхний цвета (соответственно). По умолчанию это минимальный и максимальный цвет в диапазоне (то есть 0,0 и 1,0). Это значение по умолчанию можно изменить с помощью cmap.set_under()
а также cmap_set_over()
,
Для "специальных" номеров, таких как np.nan
а также np.inf
по умолчанию используется значение 0.0, это можно изменить с помощью cmap.set_bad()
так же, как под и над, как указано выше.
Наконец, вам может понадобиться нормализовать ваши данные так, чтобы они соответствовали диапазону [0.0, 1.0]
, Это можно сделать с помощью matplotlib.colors.Normalize
просто, как показано в небольшом примере ниже, где аргументыvmin
а такжеvmax
опишите, какие числа должны быть сопоставлены с 0,0 и 1,0 соответственно.
import matplotlib
norm = matplotlib.colors.Normalize(vmin=10.0, vmax=20.0)
print(norm(15.0)) # 0.5
Логарифмический нормализатор ( matplotlib.colors.LogNorm) также доступен для диапазонов данных с большим диапазоном значений.
(Спасибо Joe Kington tacaswell и tacaswell за предложения о том, как улучшить ответ.)
Чтобы получить целочисленное значение rgba вместо значения с плавающей запятой, мы можем сделать
rgba = cmap(0.5,bytes=True)
Таким образом, чтобы упростить код на основе ответа от Ffisegydd, код будет выглядеть так:
#import colormap
from matplotlib import cm
#normalize item number values to colormap
norm = matplotlib.colors.Normalize(vmin=0, vmax=1000)
#colormap possible values = viridis, jet, spectral
rgba_color = cm.jet(norm(400),bytes=True)
#400 is one of value between 0 and 1000
Однажды я столкнулся с подобной ситуацией, когда мне нужно «n» нет. цветов из палитры, чтобы я мог назначить каждый цвет своим данным. Я скомпилировал код для этого в пакете под названием « mycolorpy ». Вы можете установить его, используя:
pip install mycolorpy
Затем вы можете сделать:
from mycolorpy import colorlist as mcp
import numpy as np
Пример: создать список из 5 шестигранных строк из лагеря "зима".
color1=mcp.gen_color(cmap="winter",n=5)
print(color1)
Выход:
['#0000ff', '#0040df', '#0080bf', '#00c09f', '#00ff80']
Другой пример создания списка из 16 цветов из лагеря bwr:
color2=mcp.gen_color(cmap="bwr",n=16)
print(color2)
Выход:
['#0000ff', '#2222ff', '#4444ff', '#6666ff', '#8888ff', '#aaaaff', '#ccccff', '#eeeeff', '#ffeeee', '#ffcccc', '#ffaaaa', '#ff8888', '#ff6666', '#ff4444', '#ff2222', '#ff0000']
Существует на блокнотPython с примерами использования, чтобы лучше это наглядно представить.
Допустим, вы хотите сгенерировать список цветов из cmap, нормализованный по заданным данным. Вы можете сделать это, используя:
a=random.randint(1000, size=(200))
a=np.array(a)
color1=mcp.gen_color_normalized(cmap="seismic",data_arr=a)
plt.scatter(a,a,c=color1)
Вы также можете изменить цвет, используя:
color1=mcp.gen_color_normalized(cmap="seismic",data_arr=a,reverse=True)
plt.scatter(a,a,c=color1)
У меня была именно эта проблема, но мне нужно было, чтобы последовательные графики имели очень контрастный цвет. Я также делал графики с общим подграфиком, содержащим справочные данные, поэтому я хотел, чтобы последовательность цветов была постоянно повторяемой.
Сначала я пробовал просто генерировать цвета случайным образом, повторно заполняя ГСЧ перед каждым графиком. Это работало нормально (закомментировано в коде ниже), но могло генерировать почти неразличимые цвета. Мне нужны были очень контрастные цвета, в идеале взятые из палитры, содержащей все цвета.
У меня могло быть до 31 серии данных на одном графике, поэтому я разделил цветовую карту на такое количество шагов. Затем я прошел по ступеням в таком порядке, который гарантировал, что очень скоро не вернусь в окрестности определенного цвета.
Мои данные находятся в очень нерегулярном временном ряду, поэтому я хотел видеть точки и линии, причем точка имеет «противоположный» цвет линии.
Учитывая все вышесказанное, проще всего было сгенерировать словарь с соответствующими параметрами для построения отдельных серий, а затем расширить его как часть вызова.
Вот мой код. Возможно, не красиво, но функционально.
from matplotlib import cm
cmap = cm.get_cmap('gist_rainbow') #('hsv') #('nipy_spectral')
max_colors = 31 # Constant, max mumber of series in any plot. Ideally prime.
color_number = 0 # Variable, incremented for each series.
def restart_colors():
global color_number
color_number = 0
#np.random.seed(1)
def next_color():
global color_number
color_number += 1
#color = tuple(np.random.uniform(0.0, 0.5, 3))
color = cmap( ((5 * color_number) % max_colors) / max_colors )
return color
def plot_args(): # Invoked for each plot in a series as: '**(plot_args())'
mkr = next_color()
clr = (1 - mkr[0], 1 - mkr[1], 1 - mkr[2], mkr[3]) # Give line inverse of marker color
return {
"marker": "o",
"color": clr,
"mfc": mkr,
"mec": mkr,
"markersize": 0.5,
"linewidth": 1,
}
Мой контекст - это JupyterLab и Pandas, поэтому вот пример кода графика:
restart_colors() # Repeatable color sequence for every plot
fig, axs = plt.subplots(figsize=(15, 8))
plt.title("%s + T-meter"%name)
# Plot reference temperatures:
axs.set_ylabel("°C", rotation=0)
for s in ["T1", "T2", "T3", "T4"]:
df_tmeter.plot(ax=axs, x="Timestamp", y=s, label="T-meter:%s" % s, **(plot_args()))
# Other series gets their own axis labels
ax2 = axs.twinx()
ax2.set_ylabel(units)
for c in df_uptime_sensors:
df_uptime[df_uptime["UUID"] == c].plot(
ax=ax2, x="Timestamp", y=units, label="%s - %s" % (units, c), **(plot_args())
)
fig.tight_layout()
plt.show()
Результирующий сюжет может быть не лучшим примером, но он становится более актуальным при интерактивном увеличении.
Цветовые карты имеют собственный метод нормализации, поэтому, если у вас уже есть график, вы можете получить доступ к цвету по определенному значению.
import matplotlib.pyplot as plt
import numpy as np
cmap = plt.cm.viridis
cm = plt.pcolormesh(np.random.randn(10, 10), cmap=cmap)
print(cmap(cm.norm(2.2)))
Чтобы использовать решения от Ffisegydd и amaliammr, вот пример, в котором мы создаем CSV-представление для настраиваемой цветовой карты:
#! /usr/bin/env python3
import matplotlib
import numpy as np
vmin = 0.1
vmax = 1000
norm = matplotlib.colors.Normalize(np.log10(vmin), np.log10(vmax))
lognum = norm(np.log10([.5, 2., 10, 40, 150,1000]))
cdict = {
'red':
(
(0., 0, 0),
(lognum[0], 0, 0),
(lognum[1], 0, 0),
(lognum[2], 1, 1),
(lognum[3], 0.8, 0.8),
(lognum[4], .7, .7),
(lognum[5], .7, .7)
),
'green':
(
(0., .6, .6),
(lognum[0], 0.8, 0.8),
(lognum[1], 1, 1),
(lognum[2], 1, 1),
(lognum[3], 0, 0),
(lognum[4], 0, 0),
(lognum[5], 0, 0)
),
'blue':
(
(0., 0, 0),
(lognum[0], 0, 0),
(lognum[1], 0, 0),
(lognum[2], 0, 0),
(lognum[3], 0, 0),
(lognum[4], 0, 0),
(lognum[5], 1, 1)
)
}
mycmap = matplotlib.colors.LinearSegmentedColormap('my_colormap', cdict, 256)
norm = matplotlib.colors.LogNorm(vmin, vmax)
colors = {}
count = 0
step_size = 0.001
for value in np.arange(vmin, vmax+step_size, step_size):
count += 1
print("%d/%d %f%%" % (count, vmax*(1./step_size), 100.*count/(vmax*(1./step_size))))
rgba = mycmap(norm(value), bytes=True)
color = (rgba[0], rgba[1], rgba[2])
if color not in colors.values():
colors[value] = color
print ("value, red, green, blue")
for value in sorted(colors.keys()):
rgb = colors[value]
print("%s, %s, %s, %s" % (value, rgb[0], rgb[1], rgb[2]))
Я нашел довольно короткий способ вернуть списокN
шестнадцатеричные цвета с matplotlib:
import matplotlib
# Choose colormap
cmap = plt.cm.get_cmap('terrain', N)
colors = [matplotlib.colors.to_hex(cmap(i)) for i in range(N)]
Надеюсь, поможет!
Вот решение, которое дает дискретное количество значений цвета. Среднюю точку можно определить путем деления дискретной карты цветов пополам.
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Choose a colormap from Matplotlib
colormap = plt.cm.viridis
# Set number of colors
num_colors = 5
# Create a ListedColormap with discrete colors
discrete_cmap = ListedColormap(colormap(np.linspace(0, 1, num_colors)))
# Output RGB values
for i, rgb in enumerate(discrete_cmap.colors):
print(f"Color {i + 1}: RGB = {rgb}")
# Plot a colorbar to visualize
plt.imshow([[i] for i in range(num_colors)], cmap=discrete_cmap, aspect='auto')
plt.colorbar(ticks=range(num_colors))
plt.show()
Для полноты, это варианты cmap, с которыми я столкнулся до сих пор:
Accent, Accent_r, Blues, Blues_r, BrBG, BrBG_r, BuGn, BuGn_r, BuPu, BuPu_r, CMRmap, CMRmap_r, Dark2, Dark2_r, GnBu, GnBu_r, Greens, Greens_r, Grays, Greys_Rd, ORRD, ORGAN, ORD, ORD PRGn_r, Парный, Парный_r, Pastel1, Pastel1_r, Pastel2, Pastel2_r, PiYG, PiYG_r, PuBu, PuBuGn, PuBuGn_r, PuBu_r, PuOr, PuOr_r, PuRd, RdGu_r, Purples, Purples_r, RdGu_r, RdBu_r, RdBu_r, RdBu_r RdYlBu, RdYlBu_r, RdYlGn, RdYlGn_r, Reds, Reds_r, Set1, Set1_r, Set2, Set2_r, Set3, Set3_r, Spectral, Spectral_r, Wistia, Wistia_r, YlGn, YlGnBurOr, YlGnBur_r, YlGnBur_r, YlGnBur_r, YlGnBur_r, YlGnBur_r, YlGnBur, YlGnBur afmhot_r, autumn, autumn_r, binary, binary_r, bone, bone_r, brg, brg_r, bwr, bwr_r, cividis, cividis_r, cool, cool_r, coolwarm, coolwarm_r, Copper, Copper_r, cubehelix, cubehelix_r, flag, flag_earth_r, gist_r, gist_rgist_gray, gist_gray_r, gist_heat, gist_heat_r, gist_ncar, gist_ncar_r, gist_rainbow, gist_rainbow_r, gist_stern, gist_stern_r, gist_yarg, gist_yarg_r, gnuplot_rnnuplot2, grey_rnuplot2, gnuplot_rnuplot2, gnuplot_rnuplot2 jet_r, magma, magma_r, nipy_spectral, nipy_spectral_r, ocean, ocean_r, pink, pink_r, Plasma, Plasma_r, Prism, Prism_r, rainbow, rainbow_r, seismic, seismic_r, spring, spring_r, summer, summer_r, tab10, tab10_r, tab20 tab20b, tab20b_r, tab20c, tab20c_r, terrain, terrain_r, сумерки, twilight_r, twilight_shifted, twilight_shifted_r, viridis, viridis_r, winter, winter_rgray_r, hot, hot_r, hsv, hsv_r, inferno, inferno_r, jet, jet_r, magma, magma_r, nipy_spectral, nipy_spectral_r, ocean, ocean_r, розовый, pink_r, плазма, плазменный_r, призма, prism_r, rainbow_rism, seism, seism весна, spring_r, лето, лето_r, tab10, tab10_r, tab20, tab20_r, tab20b, tab20b_r, tab20c, tab20c_r, terrain, terrain_r, сумерки, twilight_r, twilight_shifted, twilight_shifted_r, viridis, viridis_r, зима, зима gray_r, hot, hot_r, hsv, hsv_r, inferno, inferno_r, jet, jet_r, magma, magma_r, nipy_spectral, nipy_spectral_r, ocean, ocean_r, розовый, pink_r, плазма, плазменный_r, призма, prism_r, rainbow_rism, seism, seism весна, spring_r, лето, лето_r, tab10, tab10_r, tab20, tab20_r, tab20b, tab20b_r, tab20c, tab20c_r, terrain, terrain_r, сумерки, twilight_r, twilight_shifted, twilight_shifted_r, viridis, viridis_r, зима, зима viridis, viridis_r, winter, winter_rviridis, viridis_r, winter, winter_r
Для быстрого и грязного вы можете использовать карту напрямую. Или вы можете просто делать то, что говорит @amaliammr.
data_size = 23 # range 0..23
colors = plt.cm.turbo
color_normal = colours.N/data_size
for i in range(data_size):
col = colours.colors[int(i*color_normal)]