【问题标题】:Seaborn color bar on FacetGrid for histplot with normalized color mappingFacetGrid 上的 Seaborn 颜色条用于具有标准化颜色映射的 histplot
【发布时间】:2025-11-29 04:20:01
【问题描述】:

我似乎无法使用 seaborn FacetGrid 显示二维 histplot 的颜色条。有人可以指出我缺少的链接吗?

了解到已经讨论过类似的解决方案,我无法适应我的用例:

  1. Has the right position and values for color bar but isn't working for histplot
  2. This proposal is not running at all & is rather dated so I am not sure it is still supposed to work
  3. Seems to have fixed vmin/vmax and does not work with histplot

具体来说,我希望扩展下面的代码,以便显示颜色条。

import pandas as pd
import numpy as np
import seaborn as sns

df = pd.DataFrame(list(zip([random.randint(0,10) for i in range(1000)], pd.to_datetime(
                            [d.strftime('%Y-%m-%d') for d in pd.date_range('1800-01-01', periods=250, freq='1d')]+\
                            [d.strftime('%Y-%m-%d') for d in pd.date_range('1800-01-01', periods=250, freq='1d')]+\
                            [d.strftime('%Y-%m-%d') for d in pd.date_range('1800-01-01', periods=250, freq='1d')]+\
                            [d.strftime('%Y-%m-%d') for d in pd.date_range('1800-01-01', periods=250, freq='1d')]),
                            [random.choice(string.ascii_letters[26:30]) for i in range(1000)])), 
                            columns=["range","date","case_type"])
df["range"][df["case_type"]=="A"] = [random.randint(4562,873645) for i in range(1000)] 
df["range"][df["case_type"]=="C"] = [random.random() for i in range(1000)] 
fg = sns.FacetGrid(df, col="case_type", col_wrap=2, sharey=False)

fg.map(sns.histplot, "date", "range", stat="count", data=df)
fg.set_xticklabels(rotation=30)
fg.fig.show()

目标是在分面网格的右侧有一个颜色条,跨越整个图表 - 这里有两行,但可能会显示更多。显示的 2D 直方图具有一些非常不同的数据类型,因此每个 bin 和颜色的计数可能非常不同,知道“深蓝色”是 100 还是 1000 很重要。

编辑:为了清楚起见,从 cmets 看来,问题分为两个步骤:

  1. 如何标准化所有绘图之间的颜色编码和
  2. 使用标准化颜色映射在绘图右侧显示一个颜色条

【问题讨论】:

  • 您的示例代码可能不代表您的真实数据。您修改范围值(这与您的问题的上下文无关)但在每个类别中具有可比较的(尽管是随机的)分布。如果您修改每个类别出现的概率,您会注意到两件事 - 面板之间的直方图箱的数量会有所不同,并且相同的颜色将代表不同面板中的不同值,从而使组合的颜色条过时。有办法解决这个问题,但你必须完全不同地构建你的程序。
  • 如果您将cbar=True 添加到fg.map(),您甚至可能会看到颜色代码与您的版本不同。
  • 谢谢。是的,cbarfor 每个子图确实有效,但它占用了大量空间,因此我的目标是将栏放在右边距。解决颜色编码问题是我提出问题的动机的一部分。
  • 那么,您真正的问题不是关于如何创建颜色条,而是如何规范化直方图,以便一种颜色在所有面板中表示相同的值?
  • 嗯,是的,实际上两者都归一化 histplots 颜色编码并在右侧显示归一化的颜色条。为了更清晰,我将进行编辑。

标签: python matplotlib seaborn


【解决方案1】:

我不确定是否有一种与生俱来的方式来实现您想要的情节。但是我们可以预先计算出bin numbervmin/vmax 的合理值,并将它们应用于所有histplots

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

#generate a test dataset with different case_type probabilities
np.random.seed(123)
p1, p2, p3 = 0.8, 0.1, 0.03
df = pd.DataFrame(list(zip(np.random.randint(0, 20, 1000), 
                  pd.to_datetime(4 * [d.strftime('%Y-%m-%d') for d in pd.date_range('1800-01-01', periods=250, freq='1d')]),
                  np.random.choice(list("ABCD"),size=1000, p=[p1, p2, p3, 1-(p1+p2+p3)]))), 
                  columns=["range","date","case_type"])
df.loc[df.case_type == "A", "range"] *=   3
df.loc[df.case_type == "B", "range"] *=  23
df.loc[df.case_type == "C", "range"] *= 123

#determine the bin number for the x-axis
_, bin_edges = np.histogram(df["date"].dt.strftime("%Y%m%d").astype(int), bins="auto")
bin_nr = len(bin_edges)-1

#predetermine min and max count for each category
c_types = df["case_type"].unique()
vmin_list, vmax_list = [], []
for c_type in c_types:
    arr, _, _ = np.histogram2d(df.loc[df.case_type == c_type, "date"], df.loc[df.case_type == c_type, "range"], bins=bin_nr)
    vmin_list.append(arr.min())
    vmax_list.append(arr.max())
    
#find lowest and highest counts for all subplots
vmin_all = min(vmin_list)
vmax_all = max(vmax_list)

#now we are ready to plot
fg = sns.FacetGrid(df, col="case_type", col_wrap=2, sharey=False)
#create common colorbar axis
cax = fg.fig.add_axes([.92, .12, .02, .8])
#map colorbar to colorbar axis with common vmin/vmax values
fg.map(sns.histplot,"date", "range", stat="count", bins=bin_nr, vmin=vmin_all, vmax=vmax_all, cbar=True, cbar_ax=cax, data=df)
#prevent overlap
fg.fig.subplots_adjust(right=.9)
fg.set_xticklabels(rotation=30)

plt.show()

示例输出:

您可能还注意到我更改了您的示例数据框,以便 case_types 以不同的频率出现,否则您看不到 histplots 之间的太大差异。您还应该注意,histplots 是按照它们在数据框中出现的顺序绘制的,这可能不是您希望在图表中看到的顺序。

免责声明:这主要基于mwaskom's answer

【讨论】:

  • 哦哇!这是一个非常酷的答案,因为它解决了我的另一个问题,即如何为直方图提供预先计算的 bin。我实际上是在 spark 上单独计算 bin,并且可以对这些数字做一些有用的事情。非常非常感谢!!
  • 很高兴问题(+奖金问题)得到了解决。