Matplotlib离散色条


94

我正在尝试在matplotlib中为散点图制作离散色条

我有x,y数据,并且每个点都有一个整数标记值,我想用一种独特的颜色来表示它,例如

plt.scatter(x, y, c=tag)

通常,标记将是介于0到20之间的整数,但是确切的范围可能会发生变化

到目前为止,我只是使用默认设置,例如

plt.colorbar()

它提供了连续的颜色范围。理想情况下,我想要一组n种离散颜色(在本示例中为n = 20)。更好的办法是使标记值0产生灰色,而使1-20变为彩色。

我已经找到了一些“烹饪书”脚本,但是它们非常复杂,我不能认为它们是解决看似简单的问题的正确方法


1
这个帮助?
Francesco Montesano

感谢链接,但是第二个示例是我关于执行(看似)琐碎任务的过于复杂的方法的意思-第一个链接很有用
bph

1
我发现此链接对于离散化现有颜色图很有帮助:gist.github.com/jakevdp/91077b0cae40f8f8244a
BallpointBen

Answers:


91

通过使用BoundaryNorm作为散点的规范化器,可以非常轻松地创建自定义离散色条。(在我的方法中)古怪的位使0显示为灰色。

对于图像,我经常使用cmap.set_bad()并将数据转换为numpy掩码数组。使灰色为0会容易得多,但我无法将其与散点图或自定义cmap一起使用。

或者,您可以从头开始制作自己的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种不同的颜色很难读取其特定值,但这当然取决于您。


我不确定是否允许这样做,但是您能在这里看看我的问题吗?
vwos

6
plt.colorbar.ColorbarBase引发错误。使用mpl.colorbar.ColorbarBase
zeeshan khan,

谢谢您的回答,真的很想念这份文件。我试图将其转置为百分位数的风玫瑰,但是我遇到了一个颜色映射错误。这是一个不同的用例,但可能表明它N-1在中cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N-1)。如果不是这样,颜色在垃圾箱内分布不均,则您会遇到栅栏障碍问题。
jlandercy

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

我不确定N-1,也许您是正确的,但我无法在示例中复制它。您可以使用来避免使用LinearSegmentedColormap(及其N参数)ListedColormap。自13年以来,文档已经有了很大的改进,例如:matplotlib.org/3.1.1/tutorials/colors/…–
罗格·卡西斯

62

您可以按照以下示例操作

#!/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()

产生以下图像:

poormans_contour


14
cmap = cm.get_cmap('jet',20)然后,scatter(x,y,c = tags,cmap = cmap)可以帮助我解决问题-很难找到适用于matplotlib的文档
bph

链接似乎已断开,仅供参考。
奎因·卡尔弗

42

上面的答案是好的,除非它们在颜色栏上没有正确的刻度线位置。我喜欢在颜色的中间打勾,以便数字->颜色映射更清晰。您可以通过更改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)

离散色条的示例


1
我同意,在查看离散数据时,将刻度线放在相应颜色的中间非常有帮助。您的第二种方法是正确的。但是,您的第一种方法通常是错误的:您在刻度线上标注的值与其在颜色栏上的位置不一致。set_ticklabels(...)仅应用于控制标签格式(例如,十进制数字等)。如果数据确实是离散的,则您可能不会注意到任何问题。如果系统中有噪音(例如2-> 1.9),则标签不一致会导致误导和错误的颜色条。
E. Davis

E.,我认为您是对的,更改限制是更好的解决方案,因此我删除了另一个解决方案-尽管两者都无法很好地处理“噪音”。处理连续数据将需要一些调整。
ben.dichter 2015年

37

要设置高于或低于色图范围的值,您需要使用色图的set_overset_under方法。如果要标记特定值,则将其屏蔽(即创建一个屏蔽数组),然后使用该set_bad方法。(请参阅基本colormap类的文档: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()

在此处输入图片说明


多数民众赞成在真的-我尝试使用set_under,但没有包括vmin,所以我认为它没有做任何事情
bph

8

这个主题已经很好地涵盖了,但是我想添加一些更具体的内容:我想确保将某个值映射到该颜色(而不是任何颜色)。

它并不复杂,但是因为它花了我一些时间,它可能会帮助其他人避免浪费我很多时间:)

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

在此处输入图片说明


试图复制此内容,但是由于未定义“ tmp”,因此代码无法运行。还不清楚lambda函数中的“ pos”是什么。谢谢!
刘Liu

@GeorgeLiu确实您是写的!我犯了一个复制/粘贴错误,现在已修复!代码片段现在正在运行!关于pos我为什么不知道为什么要在这里,但是FuncFormatter()要求它……也许其他人可以启发我们!
Enzoupi

7

我一直在研究这些想法,这是我的五分钱。它避免了调用BoundaryNorm和指定normscatter和作为参数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在调用Set1指定第一个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)

散点图的散点图


1

我认为您想查看colors.ListedColormap生成您的颜色图,或者如果您只需要静态颜色图,我一直在开发可能会有所帮助的应用程序


看起来很酷,可能满足我的需求-您能建议一种将灰度值标记到现有色彩图上的方法吗?以便0值显示为灰色而其他值显示为颜色?
bph

@Hiett如何根据您的y值生成RGB数组color_list并将其传递给ListedColormap?您可以使用color_list [y == value_to_tag] = gray_color标记值。
克里斯C
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.