首页 > 解决方案 > FacetGrid 上的 Seaborn 颜色条用于具有标准化颜色映射的 histplot

问题描述

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

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

  1. 具有颜色条的正确位置和值,但不适用于 histplot
  2. 这个提议根本没有运行,而且已经过时了,所以我不确定它是否仍然应该工作
  3. 似乎有固定的 vmin/vmax 并且不适用于 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 很重要。

2 直方图搜索颜色条

编辑:为了清楚起见,从评论中可以看出问题分为两个步骤:

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

标签: pythonmatplotlibseaborn

解决方案


我不确定是否有一种与生俱来的方式来实现您想要的情节。但是我们可以预先计算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 的回答


推荐阅读