Matplotlib 离散颜色条

2025-01-20 09:07:00
admin
原创
124
摘要:问题描述:我正在尝试为 matplotlib 中的散点图制作离散颜色条我有 x、y 数据,并且每个点都有一个整数标签值,我想用唯一的颜色来表示,例如plt.scatter(x, y, c=tag) 通常标签是 0-20 之间的整数,但具体范围可能会改变到目前为止我只是使用默认设置,例如plt.colorbar...

问题描述:

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

我有 x、y 数据,并且每个点都有一个整数标签值,我想用唯一的颜色来表示,例如

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

通常标签是 0-20 之间的整数,但具体范围可能会改变

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

plt.colorbar()

这给出了连续的颜色范围。理想情况下,我想要一组 n 种离散颜色(本例中 n=20)。更好的方法是让标签值为 0 以产生灰色,1-20 产生彩色。

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


解决方案 1:

您可以使用 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 种不同的颜色,读取具体值有点困难,但这当然取决于你。

解决方案 2:

您可以按照下面的示例或文档中新添加的示例进行操作

#!/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_轮廓

解决方案 3:

上述答案都很好,只是它们在颜色条上没有正确的标记位置。我喜欢将标记放在颜色的中间,这样数字 -> 颜色的映射会更清晰。您可以通过更改 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)

离散颜色条示例

解决方案 4:

要设置高于或低于颜色图范围的值,您需要使用颜色图的set_over和方法。如果您想标记特定值,请屏蔽它(即创建一个屏蔽数组),然后使用方法。(查看基本颜色图类的文档:http ://matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap )set_under`set_bad`

听起来你想要这样的东西:

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

在此处输入图片描述

解决方案 5:

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

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

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

在此处输入图片描述

解决方案 6:

我一直在研究这些想法,以下是我的一点看法。它避免了调用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调用中的Set1指定该颜色图的第一个颜色,调用中的n最后一个
指定构造一个带有颜色的图(默认值为 256)。为了将设置为默认颜色图,我发现有必要给它命名并注册它,即:n`from_listncm`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)

具有离散颜色的散点图

解决方案 7:

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

解决方案 8:

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

解决方案 9:

我的用例类似,但有一些额外的要求:

  1. 应该将任意整数列表映射到任意颜色。

  2. 不应假设整数是连续的、排序的或在数据中表示的。

感谢@Enzoupi 提供一个起点,我对其进行了重构,使其更具可重用性,并进行了调试,以正确地将颜色映射到整数。

import numpy as np
import matplotlib as mpl
import pylab as plt


class Label:
    def __init__(self, integer, color, label):
        self.integer = integer
        self.color = color
        self.label = label


class DiscreteColormapGenerator:
    def __init__(self, labels):
        self.labels = labels
        self.labels.sort(key=lambda x: x.integer)

    def get_colormap(self):
        return mpl.colors.ListedColormap([l.color for l in self.labels])

    def get_normalizer(self):
        bins = [l.integer for l in self.labels]
        bins = [self.labels[0].integer - 1] + bins + [self.labels[-1].integer + 1]
        bins = np.array(bins[0:-1] + bins[1:]).reshape(2, -1).mean(0)
        return mpl.colors.BoundaryNorm(bins, len(self.labels), clip=True)

    def get_legend_patches(self):
        return [mpl.patches.Patch(color=l.color, label=l.label) for l in labels]


if __name__ == "__main__":
    labels = [
        Label(1, "blue", "Sea"),
        Label(2, "red", "City"),
        Label(13, "orange", "Sand"),
        Label(7, "green", "Forest"),
    ]

    cmpr = DiscreteColormapGenerator(labels)
    cmpr.get_normalizer()

    A = np.reshape([7, 2, 13, 7, 2, 2], (2, 3))

    fig, ax = plt.subplots()
    im = ax.imshow(A, cmap=cmpr.get_colormap(), norm=cmpr.get_normalizer())
    fig.legend(handles=cmpr.get_legend_patches())
    plt.show()
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2941  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1803  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色。然而,在实际运行中,资源冲突是经常会遇到的难题。资源冲突可能导致项目进度延迟、成本增加以及产品质量下降等一系列问题,严重影响企业的效益与竞争力。因此,如何有效应对PLM系统中的资源冲突,成为众多企业关注的焦点。接下来,我们将详细探讨5...
plm项目管理系统   31  
  敏捷项目管理与产品生命周期管理(PLM)的融合,正成为企业在复杂多变的市场环境中提升研发效率、增强竞争力的关键举措。随着技术的飞速发展和市场需求的快速更迭,传统的研发流程面临着诸多挑战,而将敏捷项目管理理念融入PLM,有望在2025年实现研发流程的深度优化,为企业创造更大的价值。理解敏捷项目管理与PLM的核心概念敏捷项...
plm项目   31  
  模块化设计在现代产品开发中扮演着至关重要的角色,它能够提升产品开发效率、降低成本、增强产品的可维护性与可扩展性。而产品生命周期管理(PLM)系统作为整合产品全生命周期信息的关键平台,对模块化设计有着强大的支持能力。随着技术的不断发展,到 2025 年,PLM 系统在支持模块化设计方面将有一系列令人瞩目的技术实践。数字化...
plm软件   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用