Matplotlib 离散色标

我试图在 matplotlib 中为散点图创建一个离散的颜色条

我有我的 x,y 数据,对于每个点,我想用一个唯一的颜色来表示一个整数标签值,例如。

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

典型的标记将是一个范围从0到20的整数,但是确切的范围可能会改变

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

plt.colorbar()

它提供了一个连续的颜色范围。理想情况下,我想要一组 n 个离散的颜色(在这个例子中 n = 20)。更好的方法是获得一个0的标签值来生成一个灰色,1-20是彩色的。

我已经找到了一些“食谱”脚本,但他们是非常复杂的,我不能认为他们是正确的方式来解决一个看似简单的问题

189549 次浏览

你可以跟随 下面的例子或者 文档中新添加的例子

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

我想你会想看看 色彩来生成你的颜色图,或者如果你只是需要一个静态的颜色图,我一直在 一个应用程序的工作,可能会有帮助。

您可以创建一个自定义的离散颜色条非常容易使用边界范围作为正常化您的散射。奇怪的地方(在我的方法中)是使0显示为灰色。

对于图像,我经常使用 cmap.set _ bad ()并将我的数据转换为一个简单的掩码数组。这将是更容易使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)

enter image description here

我个人认为,20种不同的颜色有点难以阅读的具体价值,但这当然取决于你。

要设置一个高于或低于颜色映射范围的值,需要使用颜色映射的 set_overset_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()

enter image description here

以上的答案都很好,只是颜色栏上没有正确的蜱虫位置。我喜欢在颜色的中间有刻度,这样数字-> 颜色映射就更清晰了。您可以通过更改 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) - 0.5,
vmax=np.max(data) + 0.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)

example of discrete colorbar

我一直在研究这些想法,这里是我的五分钱的价值。它避免调用 BoundaryNorm以及将 norm指定为 scattercolorbar的参数。然而,我找不到办法消除相当冗长的呼叫 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)

调用 Set1中的 n指定 该颜色图的第一个 n颜色,以及 from_list调用中的最后一个 n颜色 指定使用 n颜色构造映射(默认为256)。为了使用 plt.set_cmapcm设置为默认的颜色图,我发现有必要给它一个名称并注册它,即:

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)

scatterplot with disrete colors

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

这并不复杂,但由于它花了我一些时间,它可能会帮助其他人不像我那样浪费那么多时间:)

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

enter image description here