【问题标题】:Python: Shifted logarithmic colorbar, white color offset to centerPython:移动对数颜色条,白色偏移到中心
【发布时间】:2018-07-15 11:32:10
【问题描述】:

我正在努力使用pyplot.imshow 绘制我的数据。我使用“RdBu_r”颜色图,并且我需要白色在不以 1 为中心的对数刻度上的值为 1。但我该怎么做呢? 我尝试了works for seaborn 的'center=1',但是matplotlib 中没有这样的属性。我也试过这个:

import matplotlib.pyplot as plt
im=plt.imshow(proportion, cmap="RdBu_r", norm=LogNorm(), vmin=0.01, vmax=10)
axs=plt.gca()
cb = plt.colorbar(im, ax=axs,extend="both")

proportion 是我的数据数组,范围从 0.01 到 10。但是似乎没有办法指定白色在这个比例上应该是 1。 有没有办法做到这一点?

再次注意,我需要在这里使用颜色渐变和对数归一化。

【问题讨论】:

  • 您想以对数刻度将1 上的白色居中吗? LogNorm() 是问题的一部分还是尝试的解决方案?有一些关于在色标上定义中点的帖子,this one,现在也是matplotlib documentation 的一部分,还有this one,其中显示了一些替代尝试。
  • @ImportanceOfBeingErnest LogNorm 是问题的一部分...

标签: python matplotlib seaborn


【解决方案1】:

有一些关于在色标上定义中点的问题和答案。尤其是this one,它现在也是matplotlib documentation 的一部分。

这个想法是继承matplotlib.colors.Normalize 并让它接受进一步的参数midpoint。然后可以使用这将中点两侧的两个范围线性内插到范围[0,0.5][0.5,1]

在对数刻度上找到一个中点,原则上我们可以做同样的事情,只是我们将matplotlib.colors.LogNorm 子类化并取所有值的对数,然后在范围内插入这个对数[0,0.5][0.5,1]

在下面的示例中,我们的数据介于 0.00110 之间。使用通常的LogNorm,这会导致颜色图的中间(在 RdBu 颜色图的情况下为白色)位于0.1。如果我们想在1 处有白色,我们将1 指定为MidPointLogNorm 的中点。

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

x,y = np.meshgrid(np.linspace(-3,0,19), np.arange(10))
f = lambda x,y : 10**x*(1+y)
z = f(x,y)

fig, (ax,ax2) = plt.subplots(ncols=2, figsize=(12,4.8))

im = ax.pcolormesh(x,y,z, cmap="RdBu_r", norm=LogNorm(vmin=z.min(), vmax=z.max()))
fig.colorbar(im, ax=ax)
ax.set_title("LogNorm")

class MidPointLogNorm(LogNorm):
    def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
        LogNorm.__init__(self,vmin=vmin, vmax=vmax, clip=clip)
        self.midpoint=midpoint
    def __call__(self, value, clip=None):
        # I'm ignoring masked values and all kinds of edge cases to make a
        # simple example...
        x, y = [np.log(self.vmin), np.log(self.midpoint), np.log(self.vmax)], [0, 0.5, 1]
        return np.ma.masked_array(np.interp(np.log(value), x, y))


im2 = ax2.pcolormesh(x,y,z, cmap="RdBu_r", 
                            norm=MidPointLogNorm(vmin=z.min(), vmax=z.max(), midpoint=1))
fig.colorbar(im2, ax=ax2)
ax2.set_title("MidPointLogNorm")
plt.show()


适用于 nan 值的更新解决方案:您需要将 nan 值替换为某个值(数组中值范围之外的最佳值),然后用这些数字屏蔽数组。在MidPointLogNorm 内部,我们需要处理nan 值,如this question 所示。
import numpy as np
import matplotlib.pyplot as plt
from  matplotlib.colors import LogNorm

x,y = np.meshgrid(np.linspace(-3,0,19), np.arange(10))
f = lambda x,y : 10**x*(1+y)
z = f(x,y)
z[1:3,1:3] = np.NaN

#since nan values cannot be used on a log scale, we need to change them to 
# something other than nan, 
replace = np.nanmax(z)+900
z = np.where(np.isnan(z), replace, z)
# now we can mask the array
z = np.ma.masked_where(z == replace, z)

fig, (ax,ax2) = plt.subplots(ncols=2, figsize=(12,4.8))

im = ax.pcolormesh(x,y,z, cmap="RdBu_r", norm=LogNorm(vmin=z.min(), vmax=z.max()))
fig.colorbar(im, ax=ax)
ax.set_title("LogNorm")

class MidPointLogNorm(LogNorm):
    def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
        LogNorm.__init__(self,vmin=vmin, vmax=vmax, clip=clip)
        self.midpoint=midpoint
    def __call__(self, value, clip=None):
        result, is_scalar = self.process_value(value)
        x, y = [np.log(self.vmin), np.log(self.midpoint), np.log(self.vmax)], [0, 0.5, 1]
        return np.ma.array(np.interp(np.log(value), x, y), mask=result.mask, copy=False)


im2 = ax2.pcolormesh(x,y,z, cmap="RdBu_r", 
                            norm=MidPointLogNorm(vmin=z.min(), vmax=z.max(), midpoint=1))
fig.colorbar(im2, ax=ax2)
ax2.set_title("MidPointLogNorm")
plt.show()

【讨论】:

  • 这很有帮助!您能否评论一下我应该怎么做才能避免数据数组中出现 NaN 值?
  • “避免”是什么意思?
  • 如果我有几列 nans 需要用于我的绘图(否则我会在使用轴和刻度标签时遇到困难),它表示所有值都必须是正数...如果我可以上传图片它使它更清晰......
  • 您需要将 nan 值替换为数组中值范围之外的某个值数,以便可以对这些值取对数。我更新了答案。
猜你喜欢
  • 1970-01-01
  • 2020-05-05
  • 2018-05-19
  • 2012-07-20
  • 1970-01-01
  • 2013-07-02
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多