Matplotlib 对称对数颜色图不以零为中心
Matplotlib Symmetric Logarithmic colormap not centered at zero
我正在研究一个问题,即从 matplotlib.colors
导入的默认颜色图 SymLogNorm
对于我的数据实际上并不对称。我的数据的均值和中值高于零,但我希望零值显示为白色。 SymLogNorm
returns 中心值略高于零的颜色图 -- 微妙,但绝对引人注目。
这个问题有什么解决办法吗?
这是我的解决方法 - 它基本上对靠近中心的区域使用手动线性刻度,对更远的区域使用对数刻度。您可以像指定 SymLogNorm 一样指定 lin_thres 和其他参数。
class MidpointLogNorm(colors.SymLogNorm):
"""
Normalise the colorbar so that diverging bars work there way either side from a prescribed midpoint value)
e.g. im=ax1.imshow(array, norm=MidpointNormalize(midpoint=0.,vmin=-100, vmax=100))
All arguments are the same as SymLogNorm, except for midpoint
"""
def __init__(self, lin_thres, lin_scale, midpoint=None, vmin=None, vmax=None):
self.midpoint = midpoint
self.lin_thres = lin_thres
self.lin_scale = lin_scale
#fraction of the cmap that the linear component occupies
self.linear_proportion = (lin_scale / (lin_scale + 1)) * 0.5
print(self.linear_proportion)
colors.SymLogNorm.__init__(self, lin_thres, lin_scale, vmin, vmax)
def __get_value__(self, v, log_val, clip=None):
if v < -self.lin_thres or v > self.lin_thres:
return log_val
x = [-self.lin_thres, self.midpoint, self.lin_thres]
y = [0.5 - self.linear_proportion, 0.5, 0.5 + self.linear_proportion]
interpol = np.interp(v, x, y)
return interpol
def __call__(self, value, clip=None):
log_val = colors.SymLogNorm.__call__(self, value)
out = [0] * len(value)
for i, v in enumerate(value):
out[i] = self.__get_value__(v, log_val[i])
return np.ma.masked_array(out)
我从这里以中点为中心画了灵感:http://chris35wills.github.io/matplotlib_diverging_colorbar/
您之前的回答对我来说并不适用,因为中点与原始中点“相距甚远”。问题是您的对数值中没有考虑中点。
我稍微更改了您的代码并通过创建 2 个 SymLogNorm 解决了该问题。两者都以中点为中心。一个从vmin开始,一个从vmax结束(极值按中点填充)。
如果我取一个值 v,我会这样分配颜色条坐标:
vmin < v < midpoint - lin_thresh,所以使用从 vmin
开始的 SymLogNorm
中点 - lin_thresh < v < 中点 + lin_thresh,所以使用插值
midpoint + lin_thresh < v < vmax,所以使用结束于 vmax
的 SymLogNorm
class MidpointLogNorm(colors.SymLogNorm):
def __init__(self, lin_thres, lin_scale, midpoint=None, vmin=None, vmax=None):
self.midpoint = midpoint
self.lin_thres = lin_thres
self.lin_scale = lin_scale
#fraction of the cmap that the linear component occupies
self.linear_proportion = (lin_scale / (lin_scale + 1)) * 0.5
print(self.linear_proportion)
# Create norm with vmin at 0 and midpoint at 0.5
self.SymLogNorm1 = colors.SymLogNorm(lin_thres, lin_scale, vmin, 2*self.midpoint + np.abs(vmin))
# Create norm with midpoint at 0.5 and vmax at 1
self.SymLogNorm2 = colors.SymLogNorm(lin_thres, lin_scale, 2*self.midpoint - vmax, vmax)
colors.SymLogNorm.__init__(self, lin_thres, lin_scale, vmin, vmax)
def __get_value__(self, v, log_val1_i, log_val2_i, clip=None):
v = np.array(v)
x = [self.vmin, self.midpoint, self.vmax]
y = [0., 0.5, 1.]
interpol = np.interp(v, x, y)
out = np.where(np.abs(v) < self.lin_thres, interpol, v)
out = np.where(out > self.lin_thres, log_val2_i, out)
out = np.where(out < self.lin_thres, log_val1_i, out)
return np.ma.masked_array(out)
def __call__(self, value, clip=None):
log_val1 = self.SymLogNorm1(value)
log_val2 = self.SymLogNorm2(value)
out = [0] * len(value)
for i, v in enumerate(value):
out[i] = self.__get_value__(v, log_val1[i], log_val2[i])
return np.ma.masked_array(out)
我正在研究一个问题,即从 matplotlib.colors
导入的默认颜色图 SymLogNorm
对于我的数据实际上并不对称。我的数据的均值和中值高于零,但我希望零值显示为白色。 SymLogNorm
returns 中心值略高于零的颜色图 -- 微妙,但绝对引人注目。
这个问题有什么解决办法吗?
这是我的解决方法 - 它基本上对靠近中心的区域使用手动线性刻度,对更远的区域使用对数刻度。您可以像指定 SymLogNorm 一样指定 lin_thres 和其他参数。
class MidpointLogNorm(colors.SymLogNorm):
"""
Normalise the colorbar so that diverging bars work there way either side from a prescribed midpoint value)
e.g. im=ax1.imshow(array, norm=MidpointNormalize(midpoint=0.,vmin=-100, vmax=100))
All arguments are the same as SymLogNorm, except for midpoint
"""
def __init__(self, lin_thres, lin_scale, midpoint=None, vmin=None, vmax=None):
self.midpoint = midpoint
self.lin_thres = lin_thres
self.lin_scale = lin_scale
#fraction of the cmap that the linear component occupies
self.linear_proportion = (lin_scale / (lin_scale + 1)) * 0.5
print(self.linear_proportion)
colors.SymLogNorm.__init__(self, lin_thres, lin_scale, vmin, vmax)
def __get_value__(self, v, log_val, clip=None):
if v < -self.lin_thres or v > self.lin_thres:
return log_val
x = [-self.lin_thres, self.midpoint, self.lin_thres]
y = [0.5 - self.linear_proportion, 0.5, 0.5 + self.linear_proportion]
interpol = np.interp(v, x, y)
return interpol
def __call__(self, value, clip=None):
log_val = colors.SymLogNorm.__call__(self, value)
out = [0] * len(value)
for i, v in enumerate(value):
out[i] = self.__get_value__(v, log_val[i])
return np.ma.masked_array(out)
我从这里以中点为中心画了灵感:http://chris35wills.github.io/matplotlib_diverging_colorbar/
您之前的回答对我来说并不适用,因为中点与原始中点“相距甚远”。问题是您的对数值中没有考虑中点。
我稍微更改了您的代码并通过创建 2 个 SymLogNorm 解决了该问题。两者都以中点为中心。一个从vmin开始,一个从vmax结束(极值按中点填充)。
如果我取一个值 v,我会这样分配颜色条坐标:
vmin < v < midpoint - lin_thresh,所以使用从 vmin
开始的 SymLogNorm中点 - lin_thresh < v < 中点 + lin_thresh,所以使用插值
midpoint + lin_thresh < v < vmax,所以使用结束于 vmax
的 SymLogNormclass MidpointLogNorm(colors.SymLogNorm): def __init__(self, lin_thres, lin_scale, midpoint=None, vmin=None, vmax=None): self.midpoint = midpoint self.lin_thres = lin_thres self.lin_scale = lin_scale #fraction of the cmap that the linear component occupies self.linear_proportion = (lin_scale / (lin_scale + 1)) * 0.5 print(self.linear_proportion) # Create norm with vmin at 0 and midpoint at 0.5 self.SymLogNorm1 = colors.SymLogNorm(lin_thres, lin_scale, vmin, 2*self.midpoint + np.abs(vmin)) # Create norm with midpoint at 0.5 and vmax at 1 self.SymLogNorm2 = colors.SymLogNorm(lin_thres, lin_scale, 2*self.midpoint - vmax, vmax) colors.SymLogNorm.__init__(self, lin_thres, lin_scale, vmin, vmax) def __get_value__(self, v, log_val1_i, log_val2_i, clip=None): v = np.array(v) x = [self.vmin, self.midpoint, self.vmax] y = [0., 0.5, 1.] interpol = np.interp(v, x, y) out = np.where(np.abs(v) < self.lin_thres, interpol, v) out = np.where(out > self.lin_thres, log_val2_i, out) out = np.where(out < self.lin_thres, log_val1_i, out) return np.ma.masked_array(out) def __call__(self, value, clip=None): log_val1 = self.SymLogNorm1(value) log_val2 = self.SymLogNorm2(value) out = [0] * len(value) for i, v in enumerate(value): out[i] = self.__get_value__(v, log_val1[i], log_val2[i]) return np.ma.masked_array(out)