Python Matplotlib 离散颜色条
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14777066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Matplotlib discrete colorbar
提问by bph
I am trying to make a discrete colorbar for a scatterplot in matplotlib
我正在尝试为 matplotlib 中的散点图制作一个离散的颜色条
I have my x, y data and for each point an integer tag value which I want to be represented with a unique colour, e.g.
我有我的 x, y 数据和每个点的整数标记值,我想用独特的颜色表示,例如
plt.scatter(x, y, c=tag)
typically tag will be an integer ranging from 0-20, but the exact range may change
通常标记将是一个范围从 0-20 的整数,但确切的范围可能会改变
so far I have just used the default settings, e.g.
到目前为止,我只使用了默认设置,例如
plt.colorbar()
which gives a continuous range of colours. Ideally i would like a set of n discrete colours (n=20 in this example). Even better would be to get a tag value of 0 to produce a gray colour and 1-20 be colourful.
它提供了连续的颜色范围。理想情况下,我想要一组 n 个离散颜色(在本例中 n=20)。更好的是获得 0 的标签值以产生灰色,而 1-20 则是彩色的。
I have found some 'cookbook' scripts but they are very complicated and I cannot think they are the right way to solve a seemingly simple problem
我找到了一些“食谱”脚本,但它们非常复杂,我认为它们是解决看似简单问题的正确方法
采纳答案by Rutger Kassies
You can create a custom discrete colorbar quite easily by using a BoundaryNorm as normalizer for your scatter. The quirky bit (in my method) is making 0 showup as grey.
通过使用 BoundaryNorm 作为散点的标准化器,您可以很容易地创建自定义离散颜色条。古怪的一点(在我的方法中)使 0 显示为灰色。
For images i often use the cmap.set_bad() and convert my data to a numpy masked array. That would be much easier to make 0 grey, but i couldnt get this to work with the scatter or the custom cmap.
对于图像,我经常使用 cmap.set_bad() 并将我的数据转换为 numpy 掩码数组。制作 0 灰色会容易得多,但我无法让它与散点图或自定义 cmap 一起使用。
As an alternative you can make your own cmap from scratch, or read-out an existing one and override just some specific entries.
作为替代方案,您可以从头开始制作自己的 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)


I personally think that with 20 different colors its a bit hard to read the specific value, but thats up to you of course.
我个人认为用 20 种不同的颜色读取具体值有点困难,但这当然取决于你。
回答by David Zwicker
You could follow this example:
你可以按照这个例子:
#!/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()
which produces the following image:
产生以下图像:


回答by ChrisC
I think you'd want to look at colors.ListedColormapto generate your colormap, or if you just need a static colormap I've been working on an appthat might help.
我想你会想看看colors.ListedColormap来生成你的颜色图,或者如果你只需要一个静态颜色图,我一直在开发一个可能有帮助的应用程序。
回答by Joe Kington
To set a values above or below the range of the colormap, you'll want to use the set_overand set_undermethods of the colormap. If you want to flag a particular value, mask it (i.e. create a masked array), and use the set_badmethod. (Have a look at the documentation for the base colormap class: http://matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap)
要设置高于或低于颜色图范围的值,您需要使用颜色图的set_over和set_under方法。如果你想标记一个特定的值,屏蔽它(即创建一个屏蔽数组),并使用该set_bad方法。(查看基本颜色图类的文档:http: //matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap)
It sounds like you want something like this:
听起来你想要这样的东西:
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()


回答by ben.dichter
The above answers are good, except they don't have proper tick placement on the colorbar. I like having the ticks in the middle of the color so that the number -> color mapping is more clear. You can solve this problem by changing the limits of the matshow call:
上面的答案很好,只是它们在颜色栏上没有正确的刻度位置。我喜欢在颜色中间打勾,这样数字 -> 颜色映射更清晰。您可以通过更改 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)


回答by Kristjan Jonasson
I have been investigating these ideas and here is my five cents worth. It avoids calling BoundaryNormas well as specifying normas an argument to scatterand colorbar. However I have found no way of eliminating the rather long-winded call to matplotlib.colors.LinearSegmentedColormap.from_list.
我一直在研究这些想法,这是我的五美分。它避免调用BoundaryNorm以及指定norm作为参数scatter和colorbar。但是,我发现没有办法消除对matplotlib.colors.LinearSegmentedColormap.from_list.
Some background is that matplotlib provides so-called qualitative colormaps, intended to use with discrete data. Set1, e.g., has 9 easily distinguishable colors, and tab20could be used for 20 colors. With these maps it could be natural to use their first n colors to color scatter plots with n categories, as the following example does. The example also produces a colorbar with n discrete colors approprately labelled.
一些背景是 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)
which produces the image below. The nin the call to Set1specifies
the first ncolors of that colormap, and the last nin the call to from_listspecifies to construct a map with ncolors (the default being 256). In order to set cmas the default colormap with plt.set_cmap, I found it to be necessary to give it a name and register it, viz:
产生下面的图像。的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)
回答by Enzoupi
This topic is well covered already but I wanted to add something more specific : I wanted to be sure that a certain value would be mapped to that color (not to any color).
这个主题已经很好地涵盖了,但我想添加一些更具体的内容:我想确保某个值会映射到该颜色(而不是任何颜色)。
It is not complicated but as it took me some time, it might help others not lossing as much time as I did :)
这并不复杂,但因为我花了一些时间,它可能会帮助其他人不会像我一样浪费太多时间:)
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()

