Я пытаюсь создать дискретную цветовую шкалу для диаграммы рассеяния в matplotlib
У меня есть данные x, y и для каждой точки целочисленное значение тега, которое я хочу представить уникальным цветом, например
plt.scatter(x, y, c=tag)
обычно тег будет целым числом от 0 до 20, но точный диапазон может измениться.
до сих пор я использовал настройки по умолчанию, например
plt.colorbar()
что дает непрерывный диапазон цветов. В идеале мне нужен набор из n дискретных цветов (в этом примере n = 20). Еще лучше было бы получить значение тега 0 для получения серого цвета и 1-20 для красочного.
Я нашел несколько сценариев "поваренной книги", но они очень сложные, и я не могу думать, что они являются правильным способом решения, казалось бы, простой проблемы.
python
matplotlib
bph
источник
источник
Ответы:
Вы можете довольно легко создать настраиваемую дискретную цветовую полосу, используя BoundaryNorm в качестве нормализатора для разброса. Причудливый бит (в моем методе) делает то, что 0 отображается серым.
Для изображений я часто использую cmap.set_bad () и конвертирую свои данные в массив с маскировкой numpy. Было бы намного проще сделать 0 серым, но я не мог заставить это работать с разбросом или настраиваемым cmap.
В качестве альтернативы вы можете создать свой собственный cmap с нуля или прочитать существующий и переопределить только некоторые конкретные записи.
import numpy as np import matplotlib as mpl import matplotlib.pylab as plt fig, ax = plt.subplots(1, 1, figsize=(6, 6)) # setup the plot x = np.random.rand(20) # define the data y = np.random.rand(20) # define the data tag = np.random.randint(0, 20, 20) tag[10:12] = 0 # make sure there are some 0 values to show up as grey cmap = plt.cm.jet # define the colormap # extract all colors from the .jet map cmaplist = [cmap(i) for i in range(cmap.N)] # force the first color entry to be grey cmaplist[0] = (.5, .5, .5, 1.0) # create the new map cmap = mpl.colors.LinearSegmentedColormap.from_list( 'Custom cmap', cmaplist, cmap.N) # define the bins and normalize bounds = np.linspace(0, 20, 21) norm = mpl.colors.BoundaryNorm(bounds, cmap.N) # make the scatter scat = ax.scatter(x, y, c=tag, s=np.random.randint(100, 500, 20), cmap=cmap, norm=norm) # create a second axes for the colorbar ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8]) cb = plt.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm, spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i') ax.set_title('Well defined discrete colors') ax2.set_ylabel('Very custom cbar [-]', size=12)
Я лично считаю, что с 20 различными цветами немного сложно прочитать конкретное значение, но это, конечно, зависит от вас.
источник
plt.colorbar.ColorbarBase
выдает ошибку. Usempl.colorbar.ColorbarBase
N-1
вcmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N-1)
. В противном случае цвета неравномерно распределены в ящиках, и у вас есть проблема с забором.q=np.arange(0.0, 1.01, 0.1) cmap = mpl.cm.get_cmap('jet') cmaplist = [cmap(x) for x in q] cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, len(q)-1) norm = mpl.colors.BoundaryNorm(q, cmap.N)
N-1
, возможно, вы правы, но я не могу повторить это на своем примере. Вы можете избежатьLinearSegmentedColormap
(и егоN
аргумента), используя расширениеListedColormap
. Документация значительно улучшилась по сравнениюВы можете последовать этому примеру :
#!/usr/bin/env python """ Use a pcolor or imshow with a custom colormap to make a contour plot. Since this example was initially written, a proper contour routine was added to matplotlib - see contour_demo.py and http://matplotlib.sf.net/matplotlib.pylab.html#-contour. """ from pylab import * delta = 0.01 x = arange(-3.0, 3.0, delta) y = arange(-3.0, 3.0, delta) X,Y = meshgrid(x, y) Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1) Z = Z2 - Z1 # difference of Gaussians cmap = cm.get_cmap('PiYG', 11) # 11 discrete colors im = imshow(Z, cmap=cmap, interpolation='bilinear', vmax=abs(Z).max(), vmin=-abs(Z).max()) axis('off') colorbar() show()
что дает следующее изображение:
источник
Приведенные выше ответы хороши, за исключением того, что в них нет правильного размещения галочки на шкале цветов. Мне нравится иметь галочки в середине цвета, чтобы отображение числа → цвет было более четким. Вы можете решить эту проблему, изменив пределы вызова matshow:
import matplotlib.pyplot as plt import numpy as np def discrete_matshow(data): #get discrete colormap cmap = plt.get_cmap('RdBu', np.max(data)-np.min(data)+1) # set limits .5 outside true range mat = plt.matshow(data,cmap=cmap,vmin = np.min(data)-.5, vmax = np.max(data)+.5) #tell the colorbar to tick at integers cax = plt.colorbar(mat, ticks=np.arange(np.min(data),np.max(data)+1)) #generate data a=np.random.randint(1, 9, size=(10, 10)) discrete_matshow(a)
источник
set_ticklabels(...)
следует использовать только для управления форматированием метки (например, десятичным числом и т. д.). Если данные действительно дискретны, вы можете не заметить никаких проблем. Если в системе присутствует шум (например, 2 -> 1,9), эта несогласованная маркировка приведет к вводящей в заблуждение и неправильной шкале цветов.Чтобы установить значения выше или ниже диапазона цветовой карты, вы захотите использовать
set_over
иset_under
методы цветовой карты. Если вы хотите отметить конкретное значение, замаскируйте его (т. Е. Создайте замаскированный массив) и используйтеset_bad
метод. (Посмотрите документацию для базового класса цветовой карты: http://matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap )Похоже, вам нужно что-то вроде этого:
import matplotlib.pyplot as plt import numpy as np # Generate some data x, y, z = np.random.random((3, 30)) z = z * 20 + 0.1 # Set some values in z to 0... z[:5] = 0 cmap = plt.get_cmap('jet', 20) cmap.set_under('gray') fig, ax = plt.subplots() cax = ax.scatter(x, y, c=z, s=100, cmap=cmap, vmin=0.1, vmax=z.max()) fig.colorbar(cax, extend='min') plt.show()
источник
Эта тема уже хорошо освещена, но я хотел добавить кое-что более конкретное: я хотел быть уверен, что определенное значение будет сопоставлено с этим цветом (а не с каким-либо цветом).
Это несложно, но поскольку это заняло у меня некоторое время, это может помочь другим не терять столько времени, сколько я :)
import matplotlib from matplotlib.colors import ListedColormap # Let's design a dummy land use field A = np.reshape([7,2,13,7,2,2], (2,3)) vals = np.unique(A) # Let's also design our color mapping: 1s should be plotted in blue, 2s in red, etc... col_dict={1:"blue", 2:"red", 13:"orange", 7:"green"} # We create a colormar from our list of colors cm = ListedColormap([col_dict[x] for x in col_dict.keys()]) # Let's also define the description of each category : 1 (blue) is Sea; 2 (red) is burnt, etc... Order should be respected here ! Or using another dict maybe could help. labels = np.array(["Sea","City","Sand","Forest"]) len_lab = len(labels) # prepare normalizer ## Prepare bins for the normalizer norm_bins = np.sort([*col_dict.keys()]) + 0.5 norm_bins = np.insert(norm_bins, 0, np.min(norm_bins) - 1.0) print(norm_bins) ## Make normalizer and formatter norm = matplotlib.colors.BoundaryNorm(norm_bins, len_lab, clip=True) fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: labels[norm(x)]) # Plot our figure fig,ax = plt.subplots() im = ax.imshow(A, cmap=cm, norm=norm) diff = norm_bins[1:] - norm_bins[:-1] tickz = norm_bins[:-1] + diff / 2 cb = fig.colorbar(im, format=fmt, ticks=tickz) fig.savefig("example_landuse.png") plt.show()
источник
pos
я не совсем уверен, почему он здесь, но он запрашивается FuncFormatter () ... Может быть, кто-то еще может просветить нас об этом!Я исследовал эти идеи, и вот мои пять центов. Он избегает вызова
BoundaryNorm
и указанияnorm
в качестве аргумента дляscatter
иcolorbar
. Однако я не нашел способа избавиться от довольно длинного обращения кmatplotlib.colors.LinearSegmentedColormap.from_list
.Некоторая предыстория состоит в том, что matplotlib предоставляет так называемые качественные цветовые карты, предназначенные для использования с дискретными данными.
Set1
, например, имеет 9 легко различимых цветов иtab20
может использоваться для 20 цветов. С этими картами было бы естественно использовать их первые n цветов для раскрашивания диаграмм разброса с n категориями, как в следующем примере. В примере также создается палитра с n отдельными цветами, обозначенными должным образом.import matplotlib, numpy as np, matplotlib.pyplot as plt n = 5 from_list = matplotlib.colors.LinearSegmentedColormap.from_list cm = from_list(None, plt.cm.Set1(range(0,n)), n) x = np.arange(99) y = x % 11 z = x % n plt.scatter(x, y, c=z, cmap=cm) plt.clim(-0.5, n-0.5) cb = plt.colorbar(ticks=range(0,n), label='Group') cb.ax.tick_params(length=0)
что производит изображение ниже. В
n
вызове toSet1
указывает первыеn
цвета этой палитры, а последнийn
в вызовеfrom_list
указывает на создание карты сn
цветами (по умолчанию 256). Чтобы установитьcm
палитру по умолчанию сplt.set_cmap
, я обнаружил, что необходимо дать ему имя и зарегистрировать его, а именно:cm = from_list('Set15', plt.cm.Set1(range(0,n)), n) plt.cm.register_cmap(None, cm) plt.set_cmap(cm) ... plt.scatter(x, y, c=z)
источник
Я думаю, вы захотите посмотреть на colors.ListedColormap, чтобы сгенерировать свою цветовую карту, или если вам просто нужна статическая цветовая карта, я работал над приложением, которое могло бы помочь.
источник