python - FacetGrid 上的 Seaborn 颜色条用于具有标准化颜色映射的 histplot
问题描述
histplot
我似乎无法使用 seaborn显示二维的颜色条FacetGrid
。有人可以指出我缺少的链接吗?
了解到已经讨论了类似的解决方案,我无法适应我的用例:
具体来说,我希望扩展下面的代码,以便显示颜色条。
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 很重要。
编辑:为了清楚起见,从评论中可以看出问题分为两个步骤:
- 如何标准化所有图之间的颜色编码和
- 使用标准化颜色映射在绘图右侧显示颜色条
解决方案
我不确定是否有一种与生俱来的方式来实现您想要的情节。但是我们可以预先计算bin number
和vmin
/的合理值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 的回答。