ggplot热力图如何合并离散颜色标度仅显示Fair/Unfair两类图例
Shiny公平性指标热力图渲染修复方案
问题说明
- 需实现公平性判定规则:指标值落在
[-0.1, 0.1]区间为Fair类,其余值为Unfair类,两类使用对应固定颜色填充 - 要求尽可能保留原有分箱
breaks设置,最终图例仅展示Fair、Unfair两个分类,不重复展示Unfair条目 - 当前平均机会差异(Average Odds Difference)指标输出效果如下:

原有实现代码
output$fairness_heatmap <- renderPlot({ # 读取用户选择的指标 heatmap_data <- NULL groups <- NULL values <- NULL color <- NULL if (input$fairness_metric == "Equal Opportunity Difference") { heatmap_data <- rw$`Equal Opportunity Difference` groups <- cut(round(heatmap_data, 2), breaks = c(-Inf, -0.11, 0.10, Inf)) values <- c(unfair_color, fair_color, unfair_color) color <- ifelse((round(heatmap_data, 2) <= 0.10000) & (round(heatmap_data, 2) >= -0.10000), "black", "white") } else if (input$fairness_metric == "Average Odds Difference") { heatmap_data <- rw$`Average Odds Difference` groups <- cut(round(heatmap_data, 2), breaks = c(-Inf, -0.11, 0.10, Inf)) values <- c(unfair_color, fair_color, unfair_color) color <- ifelse((round(heatmap_data, 2) <= 0.1000) & (round(heatmap_data, 2) >= -0.1000), "black", "white") } else if (input$fairness_metric == "Equalized Odds") { heatmap_data <- rw$`Equalized Odds` groups <- cut(round(heatmap_data, 2), breaks = c(0.0, 0.1, Inf)) values <- c(fair_color, unfair_color) color <- ifelse(round(heatmap_data, 2) <= 0.10000, "black", "white") } # 绘制热力图 ggplot(rw, aes(x = rw$label_perc, y = rw$prot_attr_perc, fill = groups)) + geom_tile() + geom_text(aes(label = round(heatmap_data, 2)), color = color, size = 4) + xlab(label = "Population with Negative Outcome (%)") + ylab(label = "Minority Population in Data (%)") + ggtitle(paste(input$fairness_metric, "across scenarios (after reweighing)", sep = " ")) + scale_x_continuous(expand = c(0, 0), breaks = breaks, labels = labels) + scale_y_continuous(expand = c(0, 0), breaks = breaks, labels = rev(labels)) + scale_fill_manual("Fairness", breaks = levels(groups), labels = c("Unfair", "Fair", "Unfair"), values = values) + theme( panel.grid.major.x = element_blank(), panel.grid.major.y = element_blank(), plot.background = element_blank(), rect = element_blank(), panel.grid = element_blank() ) }, bg = "transparent")
修复方案
完全保留原有分箱breaks设置,仅做3处调整即可实现需求:
- 复用原有已验证正确的阈值判断逻辑(即文本颜色
color的判定规则),新增二分类公平性字段,避免重复写判断逻辑引入误差 - 将热力图填充映射从原始三分箱/二分箱因子改为新增的二分类公平性字段
- 调整填充标度参数,仅指定Fair、Unfair两个分类的颜色和标签,自动合并同色分类,同时修正
aes中直接用rw$映射列名的不规范写法,避免Shiny响应式更新时出现渲染异常
修复后完整代码:
output$fairness_heatmap <- renderPlot({ # 读取用户选择的指标 heatmap_data <- NULL groups <- NULL color <- NULL if (input$fairness_metric == "Equal Opportunity Difference") { heatmap_data <- rw$`Equal Opportunity Difference` groups <- cut(round(heatmap_data, 2), breaks = c(-Inf, -0.11, 0.10, Inf)) color <- ifelse((round(heatmap_data, 2) <= 0.10000) & (round(heatmap_data, 2) >= -0.10000), "black", "white") } else if (input$fairness_metric == "Average Odds Difference") { heatmap_data <- rw$`Average Odds Difference` groups <- cut(round(heatmap_data, 2), breaks = c(-Inf, -0.11, 0.10, Inf)) color <- ifelse((round(heatmap_data, 2) <= 0.1000) & (round(heatmap_data, 2) >= -0.1000), "black", "white") } else if (input$fairness_metric == "Equalized Odds") { heatmap_data <- rw$`Equalized Odds` groups <- cut(round(heatmap_data, 2), breaks = c(0.0, 0.1, Inf)) color <- ifelse(round(heatmap_data, 2) <= 0.10000, "black", "white") } # 新增二分类公平性字段,完全复用原有阈值逻辑,无需修改原有分箱breaks fair_cat <- ifelse(color == "black", "Fair", "Unfair") # 绘制热力图 ggplot(rw, aes(x = label_perc, y = prot_attr_perc, fill = fair_cat)) + geom_tile() + geom_text(aes(label = round(heatmap_data, 2)), color = color, size = 4) + xlab(label = "Population with Negative Outcome (%)") + ylab(label = "Minority Population in Data (%)") + ggtitle(paste(input$fairness_metric, "across scenarios (after reweighing)", sep = " ")) + scale_x_continuous(expand = c(0, 0), breaks = breaks, labels = labels) + scale_y_continuous(expand = c(0, 0), breaks = breaks, labels = rev(labels)) + scale_fill_manual("Fairness", breaks = c("Fair", "Unfair"), values = c("Fair" = fair_color, "Unfair" = unfair_color), labels = c("Fair", "Unfair")) + theme( panel.grid.major.x = element_blank(), panel.grid.major.y = element_blank(), plot.background = element_blank(), rect = element_blank(), panel.grid = element_blank() ) }, bg = "transparent")
修复效果说明
- 原有分箱逻辑完全保留,数值判定和填充色100%匹配规则:
[-0.1, 0.1]区间为Fair类用fair_color填充,其余区间为Unfair类用unfair_color填充 - 图例仅展示Fair、Unfair两个条目,无重复分类
- 修正了原代码中
aes直接调用rw$列名的潜在问题,响应式渲染更稳定
内容的提问来源于stack exchange,提问作者Matthew Francis
相关产品推荐
相关产品推荐

