Matplotlib离散颜色条

ruarlubt  于 2023-10-24  发布在  其他
关注(0)|答案(8)|浏览(117)

我正在尝试在matplotlib中为散点图制作离散颜色条
我有我的x,y数据,对于每个点,我希望用唯一的颜色表示的整数标记值,例如。

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

典型地,tag将是范围从0-20的整数,但是确切的范围可以改变
到目前为止,我只是使用默认设置,例如。

plt.colorbar()

这给出了一个连续的颜色范围。理想情况下,我想要一组n个离散的颜色(在这个例子中n=20)。甚至更好的是得到一个标签值0来产生灰色,1-20是彩色的。
我找到了一些“食谱”脚本,但它们非常复杂,我不认为它们是解决看似简单问题的正确方法

qxsslcnc

qxsslcnc1#

你可以通过使用BoundaryNorm作为散点的归一化器来创建一个自定义的离散色条。
对于图像,我经常使用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种不同的颜色有点难以阅读的具体价值,但这取决于你当然。

amrnrhlw

amrnrhlw2#

您可以按照this example belownewly added example in the documentation

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

这会产生以下图像:

ymzxtsji

ymzxtsji3#

上面的答案都很好,除了它们在颜色条上没有正确的记号位置。我喜欢把记号放在颜色的中间,这样数字->颜色Map就更清晰了。你可以通过改变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)

aor9mmx1

aor9mmx14#

要将a值设置为高于或低于colormap的范围,您需要使用colormap的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()

lrpiutwd

lrpiutwd5#

这个主题已经很好地涵盖了,但我想添加一些更具体的东西:我想确保某个值将Map到该颜色(而不是任何颜色)。
这并不复杂,但因为它花了我一些时间,它可能会帮助其他人不像我一样失去那么多时间:)

import numpy as np
import matplotlib
from matplotlib import pyplot as plt
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)
plt.show()

gr8qqesn

gr8qqesn6#

我一直在研究这些想法,这里是我的五美分价值。它避免调用BoundaryNorm以及指定norm作为scattercolorbar的参数。然而,我没有找到消除对matplotlib.colors.LinearSegmentedColormap.from_list的冗长调用的方法。
一些背景是matplotlib提供了所谓的定性色彩图,旨在用于离散数据。例如,Set1有9种容易区分的颜色,tab20可以用于20种颜色。使用这些Map,使用它们的前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指定该色彩Map表的前n种颜色,调用from_list中的最后n指定用n种颜色构造Map表(默认值为256)。为了将cm设置为plt.set_cmap的默认色彩Map表,我发现有必要给予一个名称并注册它,即:

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)

uttx8gqw

uttx8gqw7#

我想你会想看看颜色。ListedColormap可以生成你的色彩Map表,或者如果你只是需要一个静态色彩Map表,我一直在研究an app,这可能会有所帮助。

brtdzjyr

brtdzjyr8#

@Enzoupi的回答有很多好东西在里面。我把它分解了一下,看看是什么。这是我的注解版本。所有的功劳都归于他们。

import numpy as np
import matplotlib
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap

# Let's design a dummy land use field
A = np.reshape([7, 2, 13, 7, 2, 2], (2, 3))

# 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(colors=list(col_dict.values()))

# Note the colormap `cm` has no information as to the values we want each color
# to represent... to do that, we have to normalize the image.
# We create bins by adding 0.5 to each value (this assumes no two values are
# less than 0.5 apart)
norm_bins = np.sort([*col_dict.keys()]) + 0.5
# We must also add a bin at the bottom; doesn't matter really where as long as
# it's below the minimum value. This is because `BoundaryNorm` needs bins on
# either side of a value to map that value to a particular color.
norm_bins = np.insert(norm_bins, 0, np.min(norm_bins) - 1.0)  # add one below the minimum
norm = matplotlib.colors.BoundaryNorm(norm_bins, len(col_dict), clip=True)

# 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 = ["Sea", "City", "Sand", "Forest"]

# We need a tick formatter that takes the value `x` and maps it to a label.
# We use the normalizer we created to take land use values and convert to the
# color index, and then use that to pick from our label list.
fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: labels[norm(x)])

# But, as-is, the ticks will be at the boundary limits, which will appear to
# show incorrect labels. So we must also put the ticks at the center of each bin
# in the normalizer.
diff = norm_bins[1:] - norm_bins[:-1]
ticks = norm_bins[:-1] + diff / 2

# So in summary:
# Plot `A` using a list of colors `cm`. Normalize the values in `A` using `norm`
# (so that discontinous values map to the correct colors)
im = plt.imshow(A, cmap=cm, norm=norm)
# Add a colorbar which positions ticks at the center of each color band and
# formats them so they're labeled according to the meaning of the value.
plt.colorbar(im, format=fmt, ticks=ticks)
plt.show()

相关问题