Plotnine绘制热图遇TypeError:连续值传入离散刻度的解决办法
问题解决:plotnine中连续值传入离散刻度的错误处理
问题描述
使用Polars处理数据时,需要为每个team2条目绘制独立热图,并保持统一调色板以确保跨图颜色可对比。通过mizani.cmap_pal基于全局value的极值归一化,已为每个value生成对应十六进制颜色并存入color列。但循环绘图时,构建value到color的字典并使用plotnine.scale_fill_manual设置填充色,触发错误:
TypeError: Continuous value supplied to discrete scale
数据结构
| team1 | team2 | type | value | color |
|---|---|---|---|---|
| team1_1 | team2_1 | 18 | 115.850278 | #443b84 |
| team1_1 | team2_2 | 26 | 24.241389 | #470e61 |
| team1_2 | team2_2 | 13 | 3.278333 | #440256 |
| team1_1 | team2_3 | 30 | 94.118333 | #46327e |
| team1_3 | team2_3 | 13 | 35.186111 | #481467 |
| ... | ... | ... | ... | ... |
| team1_2 | team2_1 | 18 | 67.937778 | #482576 |
| team1_2 | team2_4 | 22 | 0.0 | #440154 |
| team1_4 | team2_2 | 30 | 1.199444 | #440154 |
| team1_1 | team2_5 | 15 | 0.0 | #440154 |
| team1_1 | team2_3 | 11 | 8.345278 | #450559 |
生成color列的代码
from mizani.palettes import cmap_pal palette = cmap_pal("viridis") palette_values = (df["value"] - df["value"].min()) / ( df["value"].max() - df["value"].min() ) df = df.with_columns(color=pl.Series(palette(palette_values)))
原绘图循环代码
for t2 in df["team2"].unique(): df1 = df.filter(pl.col("team2") == t2) color_dict = { key: value for key, value in zip(df1["value"], df1["color"]) } plt = ( pn.ggplot( data=df1.to_pandas(), mapping=pn.aes( x="team1", y="type", label="value", fill="value", ), ) + pn.geom_tile(show_legend=False) + pn.scale_fill_manual(color_dict) + pn.geom_text(show_legend=False, size=9, format_string="{:.2f}") )
错误原因
scale_fill_manual是为离散变量设计的刻度映射工具,而代码中fill绑定的是连续型的value列,导致连续值被传入离散刻度逻辑,触发类型错误。此外,浮点数作为字典键还可能存在精度匹配问题(如两个近似相等的value因浮点精度差异无法匹配对应颜色)。
解决方案
方案一:直接映射预计算的color列
既然已提前为每行计算好对应颜色,可直接将fill映射到color列,无需再通过scale_fill_manual转译:
for t2 in df["team2"].unique(): df1 = df.filter(pl.col("team2") == t2) plt = ( pn.ggplot( data=df1.to_pandas(), mapping=pn.aes( x="team1", y="type", label="value", fill="color", # 改为绑定color列 ), ) + pn.geom_tile(show_legend=False) + pn.geom_text(show_legend=False, size=9, format_string="{:.2f}") ) plt.savefig(f"heatmap_team2_{t2}.png") # 按需保存或展示
方案二:复用全局极值的连续刻度映射
若希望保留fill与value的绑定逻辑,可直接使用连续刻度工具scale_fill_cmap,并传入全局value的极值以确保跨图颜色统一:
# 提前计算全局value的极值 global_min = df["value"].min() global_max = df["value"].max() for t2 in df["team2"].unique(): df1 = df.filter(pl.col("team2") == t2) plt = ( pn.ggplot( data=df1.to_pandas(), mapping=pn.aes( x="team1", y="type", label="value", fill="value", ), ) + pn.geom_tile(show_legend=False) + pn.scale_fill_cmap( palette="viridis", limits=(global_min, global_max), # 强制使用全局极值计算颜色 na_value="#ffffff" # 可选:设置缺失值颜色 ) + pn.geom_text(show_legend=False, size=9, format_string="{:.2f}") ) plt.savefig(f"heatmap_team2_{t2}.png")
方案对比
- 方案一适合已预计算颜色的场景,逻辑直接,无需额外处理颜色映射。
- 方案二更贴合plotnine的连续刻度设计,无需维护额外的
color列,自动处理颜色归一化,推荐使用。
内容的提问来源于stack exchange,提问作者CopyOfA
相关产品推荐
相关产品推荐

