8

我正在使用 matplotlib 1.3.0,我有以下内容:

import matplotlib.pyplot as plt
cmap = plt.cm.jet
plt.contourf([[.12, .2], [.8, 2]], levels=[0, .1, .3, .5, 1, 3], cmap=cmap, vmin=0, vmax=3)
plt.colorbar()

产生:

在此处输入图像描述

我不明白的是所有其他颜色都去哪儿了?据我了解,通过指定vmin=0,颜色条应使用此图像中vmax=3的全部范围:cmap

在此处输入图像描述

它是在没有给出vmin,vmaxlevels参数的情况下产生的。那么......我在这里错过了什么?

编辑 1

作为对 tom10 和 tcaswell 的回应。我原以为会像你说的那样,但是……不幸的是,事实并非如此。看看这个:

plt.contourf([[.12, .2], [.8, 3.2]], levels=[0, .1, .3, .5, 1, 3], cmap=cmap, vmin=0, vmax=3)
plt.colorbar()

和:

在此处输入图像描述

也许要澄清一下:假设我有数据,它的重要特征在 0.1 左右,但有一些在 3 左右。所以我给它一个levels=[0, 0.005, 0.075, 0.1, 0.125, 0.15, 0.2, 1, 2.5, 2.75, 3, 3.25]and vmin=0, vmax=3.25。现在我希望看到全范围的颜色,但所有重要的数据点 0.005 到 0.125 最终都在蓝色区域(通过使用标准plt.cm.jet颜色图)。我想说的是……如果我给出levels=[0, 1, 2, 3], vmin=0, vmax=3一些从 0 到 3 的数据,我希望看到给定颜色图中的所有颜色,但如果我给出levels=[0, 0.9, 0.1, 0.11, 1, 3], vmi=0, vmax=3相同的结果,我希望看到所有给定颜色图中的颜色,除了映射到正确的间隔,相反,我看到一堆蓝色为 0-0.11 区域着色,一些绿色/黄色为该区域的其他部分着色。希望这使它...有点清楚。

编辑 2

即使我不给任何norm或,也会发生同样的情况vmin, vmax

编辑 3

参考 tcaswell 的评论,行为方式......至少对我来说是违反直觉的。我希望颜色在某种程度上独立于数据点。我希望始终使用颜色图中的全部颜色(除非vmin, vmax大于/小于levels最小值、最大值)。换句话说,看看我不久前做过的这段代码(Python 3):

import matplotlib.colors as mc
def addNorm(cmapData):
    cmapData['norm'] = mc.BoundaryNorm(cmapData['bounds'], cmapData['cmap'].N)
    return True
def discretize(cmap, bounds):
    resCmap = {}
    resCmap['cmap'] = mc.ListedColormap( \
        [cmap(i/len(bounds[1:])) for i in range(len(bounds[1:]))]
    )
    resCmap['bounds'] = bounds
    addNorm(resCmap)
    return resCmap

然后将其用作:

levels = [0, .1, .3, .5, 1, 3]
cmapData = discretize(plt.cm.jet, bounds=levels)
plt.contourf([[.12, .2], [.8, 3.2]], levels=levels, cmap=cmapData['cmap'], norm=cmapData['norm'])
plt.colorbar()

它给出了您可以实际区分特征(0.1-0.5)的图,即通过使用上述方法,它们不再位于蓝色区域plt.cm.jet

在此处输入图像描述

我的意思是,我知道我解决了这个问题,而且不久前也解决了......但我想我的问题是......为什么 matplotlib 中的默认值不是这个?我本来希望它是这种方式......或者它可能只是一个配置/参数/默认情况下启用它的东西,我错过了?

4

4 回答 4

3

在玩了一会儿之后,这个问题的答案似乎比我想象的要容易得多。先解释一下。在阅读有关规范化类的文档时,matplotlib.colors我想......好吧,matplotlib.colors.BoundaryNorm应该在这里使用!但正如您在以下示例中看到的那样,出了点问题:

import matplotlib.pyplot as plt
import matplotlib.colors as mc
levels = [0, .1, .3, .5, 1, 3]
norm = mc.BoundaryNorm(levels, len(levels)-1)
plt.contourf([[.12, .2], [.8, 2]], levels=levels, norm=norm)
plt.colorbar()
plt.show()

这给出了这个: 在此处输入图像描述 这显然是我们不想要的!我在想......为什么你必须给构造函数BoundaryNorm要使用的颜色数量?......不应该BoundaryNorm使用颜色图的全部范围?然后它让我印象深刻,只需对上面的代码稍作改动:

# use here 256 instead of len(levels)-1 becuase
# as it's mentioned in the documentation for the
# colormaps, the default colormaps use 256 colors in their
# definition: print(plt.cm.jet.N) for example
norm = mc.BoundaryNorm(levels, 256)

我们得到: 在此处输入图像描述 这正是我们想要的!

或者你我们可以这样做:

cmap = # user define cmap
norm = mc.BoundaryNorm(levels, cmap.N)
# which is I guess a little bit more programatically (is this a word?!) correct
于 2013-10-21T11:38:51.933 回答
2

填充区域的颜色由它在(iirc)之间填充的两条线的中点拾取。您看到的黄色是2您设置的颜色图和限制下的映射。

如果要按区域索引映射颜色,请进行一些猴子修补:

def _process_colors_by_index(self):
    """
    Color argument processing for contouring.

    The color is based in the index in the level set, not
    the actual value of the level.

    """
    self.monochrome = self.cmap.monochrome
    if self.colors is not None:
        # Generate integers for direct indexing.
        i0, i1 = 0, len(self.levels)
        if self.filled:
            i1 -= 1
        # Out of range indices for over and under:
        if self.extend in ('both', 'min'):
            i0 = -1
        if self.extend in ('both', 'max'):
            i1 += 1
        self.cvalues = list(range(i0, i1))
        self.set_norm(colors.NoNorm())
    else:
        self.cvalues = range(len(self.levels))
    self.set_array(range(len(self.levels)))
    self.autoscale_None()
    if self.extend in ('both', 'max', 'min'):
        self.norm.clip = False

    # self.tcolors are set by the "changed" method


orig = matplotlib.contour.ContourSet._process_colors
matplotlib.contour.ContourSet._process_colors = _process_colors_by_index
cmap = plt.cm.jet
figure()
out = plt.contourf([[.12, .2], [.8, 2]], levels=[0, .1, .3, .5, 1, 3], cmap=cmap)
plt.colorbar()
# fix what we have done
matplotlib.contour.ContourSet._process_colors = orig

输出

您可能会做得更好,也可以将移位减少 1/2。

您还可以进入并更改现有轮廓的颜色。看起来您需要更改的值out.cvalues然后调用out.changed()该对象。

破坏性较小的版本是norm通过 sub-classing编写自定义matplotlib.colors.Normalize,请参阅colors.py的模板。

于 2013-09-03T20:42:35.103 回答
0

您的数据的最大值是2。在你设置的有问题的情节中vmax=3

更详细地说,vmax设置映射中使用的颜色范围。由于这比您的数据范围大得多,因此当您绘制数据时,您看不到完整的颜色范围。这进一步混淆了您选择的少量levels颜色,它没有向您显示所有可用的颜色,因为颜色栏仅显示整个 1 到 3 范围内的单一颜色,再次模糊了超过 2 的可用颜色。

于 2013-09-03T20:55:15.627 回答
0

实际上,我认为最好的解决方案就在这个地方:

http://protracted-matter.blogspot.ie/2012/08/nonlinear-colormap-in-matplotlib.html

它定义了这个解决所有问题的小类:

class nlcmap(mc.LinearSegmentedColormap):
    """A nonlinear colormap"""

    name = 'nlcmap'

    def __init__(self, cmap, levels):
        self.cmap = cmap
        # @MRR: Need to add N for backend
        self.N = cmap.N
        self.monochrome = self.cmap.monochrome
        self.levels = np.asarray(levels, dtype='float64')
        self._x = self.levels / self.levels.max()
        self._y = np.linspace(0.0, 1.0, len(self.levels))

    #@MRR Need to add **kw for 'bytes'
    def __call__(self, xi, alpha=1.0, **kw):
        yi = np.interp(xi, self._x, self._y)
        return self.cmap(yi, alpha)

该脚本最初是由一个名叫罗伯特·赫特兰的人开发的。所有详细信息都在上面的链接中。

于 2013-09-30T14:39:29.503 回答