如何为对数坐标线图设置色条

3

我在为一组对应于幂律的多条线绘制的图中添加色条时遇到了问题。

为了为非图像绘制创建颜色条,我添加了一个虚拟绘图(来自这里的答案:Matplotlib - add colorbar to a sequence of line plots)。

颜色条刻度不对应绘图的颜色。

我尝试更改颜色条的规范,并且可以微调它以使其对于特定情况准确无误,但我无法普遍地做到这一点。

def plot_loglog_gauss():
    from matplotlib import cm as color_map
    import matplotlib as mpl

    """Creating the data"""
    time_vector = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256]
    amplitudes = [t ** 2 * np.exp(-t * np.power(np.linspace(-0.5, 0.5, 100), 2)) for t in time_vector]

    """Getting the non-zero minimum of the data"""
    data = np.concatenate(amplitudes).ravel()
    data_min = np.min(data[np.nonzero(data)])

    """Creating K-space data"""
    k_vector = np.linspace(0,1,100)

    """Plotting"""
    number_of_plots = len(time_vector)
    color_map_name = 'jet'
    my_map = color_map.get_cmap(color_map_name)
    colors = my_map(np.linspace(0, 1, number_of_plots, endpoint=True))

    # plt.figure()
    # dummy_plot = plt.contourf([[0, 0], [0, 0]], time_vector, cmap=my_map)
    # plt.clf()

    norm = mpl.colors.Normalize(vmin=time_vector[0], vmax=time_vector[-1])
    cmap = mpl.cm.ScalarMappable(norm=norm, cmap=color_map_name)
    cmap.set_array([])


    for i in range(number_of_plots):
        plt.plot(k_vector, amplitudes[i], color=colors[i], label=time_vector[i])

    c = np.arange(1, number_of_plots + 1)
    plt.xlabel('Frequency')
    plt.ylabel('Amplitude')
    plt.yscale('symlog', linthreshy=data_min)
    plt.xscale('log')
    plt.legend(loc=3)

    ticks = time_vector
    plt.colorbar(cmap, ticks=ticks, shrink=1.0, fraction=0.1, pad=0)

    plt.show()

Plot generated

通过与图例进行比较,您会发现刻度值与实际颜色不匹配。例如,在色图中,128以绿色显示,而在图例中为红色。

实际结果应该是一个线性色彩条,其上刻度在色彩条上定期间隔(对应于不规则时间间隔)。当然,每个刻度的颜色也应该正确。

(最终图包含多个子图(len(time_vector)〜100),我减少了子图数量来说明并能够显示图例。)

为了澄清,这就是我想要的结果样子。

Plot wanted


1
小修正 - 1,2,4,8,16,32,64 ,128,256 - MBo
真的。更新了代码和图片。 - AsaridBeck91
链接问题中被接受的答案并不实用。你考虑过其他答案吗? - ImportanceOfBeingErnest
@ImportanceOfBeingErnest:当你写下这个评论的时候,我正在准备一个解决方案。事实上,我正要说同样的话。 - Sheldore
是的,我尝试了所有那些答案,但都无法使它们起作用。 - AsaridBeck91
你需要决定的一个主要问题是,对于无法进行对数缩放的数字0应该发生什么? - ImportanceOfBeingErnest
1个回答

4

最重要的原则是保持线图和ScalarMappable的颜色同步。这意味着,线的颜色不应该来自于一个独立的颜色列表,而是应该从相同的colormap中获取,并使用与将要显示的colorbar相同的归一化方法。

一个主要的问题是如何处理 0 ,因为它不能作为对数归一化的一部分。以下是一个解决方案,假设在 0 到 2 之间使用线性比例尺,在上面使用对数比例尺,并使用 SymLogNorm

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

"""Creating the data"""
time_vector = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256]
amplitudes = [t ** 2 * np.exp(-t * np.power(np.linspace(-0.5, 0.5, 100), 2)) for t in time_vector]

"""Getting the non-zero minimum of the data"""
data = np.concatenate(amplitudes).ravel()
data_min = np.min(data[np.nonzero(data)])

"""Creating K-space data"""
k_vector = np.linspace(0,1,100)

"""Plotting"""
cmap = plt.cm.get_cmap("jet")
norm = mpl.colors.SymLogNorm(2, vmin=time_vector[0], vmax=time_vector[-1])

sm = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([])

for i in range(len(time_vector)):
    plt.plot(k_vector, amplitudes[i], color=cmap(norm(time_vector[i])), label=time_vector[i])

#c = np.arange(1, number_of_plots + 1)
plt.xlabel('Frequency')
plt.ylabel('Amplitude')
plt.yscale('symlog', linthreshy=data_min)
plt.xscale('log')
plt.legend(loc=3)

cbar = plt.colorbar(sm, ticks=time_vector, format=mpl.ticker.ScalarFormatter(), 
                    shrink=1.0, fraction=0.1, pad=0)

plt.show()

enter image description here


1
如果没有功能关系,你宁愿使用分散的colorbar,对吗?就像这个答案中所述。(当然,如果只使用足够多的颜色,任何离散的colorbar都会看起来连续,如果仍然需要的话。) - ImportanceOfBeingErnest
天哪,我靠近了。我正在尝试以 norm 为参数使用 gamma=2PowerNorm 方法。我将删除我的无用回答。 - Sheldore
如果您在上述代码中将 time_vector 替换为 [0, 1, 2, 128, 256],则色条和线条颜色会匹配。所以我不确定您的意思是什么。 - ImportanceOfBeingErnest
1
这里提供的解决方案不使用 "linspace" 函数。但是,如果您想要使用它,我会引用 这个答案 - ImportanceOfBeingErnest
1
是的,你可以。这就是在这种情况下使用“格式”所用的。您可以将其替换为任何其他格式化程序。 - ImportanceOfBeingErnest
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接