ggplot直方图着色异常排查:零值柱子出现双色问题
问题分析与解决
问题重现
你使用以下数据集df4和ggplot代码绘制直方图,希望将负数对应的柱子设为蓝色(#41A2D0),非负数(含0)设为灰色,但零值柱子出现了双色异常:
数据集:
df4<- data.frame(structure(list(Percentage.of.change = c(0.8, 0.6, -0.6, -3.1, 2.7, -1.4, -3.4, -11.3, -3.8, -6.8, 5, 0.6, -2.7, -2.4, -0.9, -2.8, -3.5, 1.2, 4.7, -2.5, -4, -2.9, 0, -2.2, -0.9, -4.1, 0, -1.9, -5.2, -6.4, -3.9, -2.5, -3.8, -4.5, -1.8, -3.1, -2.7, -9.2, -1.9, -5.9, -6.5, -2.3, 0, -3.8, -4.2, -1.5, 1.7, 0, -0.6, -5.3, -0.2)), row.names = c(NA, 51L), class = "data.frame")
绘图代码:
ggplot(df4, aes(x = Percentage.of.change, fill = ifelse(df4$Percentage.of.change < 0, "#41A2D0", "grey"), color = "black")) + geom_histogram(binwidth = 1, col="black")+ scale_fill_manual(values = c( "#41A2D0", "grey"), guide = FALSE) + #scale_x_continuous(limits = c(-16,6), breaks = seq(-16,6,1))+ theme_minimal()+ labs(x = "Change in Percentage of Body Weight (%)", y = "Number of Individuals", title = "Relative Weight Change", subtitle = "Relative weight change for individuals whose goal was to lose weight and completed the program (n=209)")
问题根源
你之前的代码是给每个数据点单独分配填充色,而非给整个直方图柱子(bin)分配颜色。当某个bin里同时存在正负数据点时,柱子就会被拆分成两种颜色显示。
小数据量时没出现问题,是因为刚好没有正负数据点被分到同一个bin里;数据量变大后,概率升高,就会暴露这个逻辑漏洞。binwidth=1只是设置每个bin的宽度,和颜色异常无关。
解决方法
方法1:用after_stat给bin分配颜色(推荐)
利用ggplot的after_stat函数,在直方图计算完bin之后,根据bin的中心值(或左边界)判断颜色,确保每个bin只有单一颜色:
ggplot(df4, aes(x = Percentage.of.change)) + geom_histogram( binwidth = 1, col = "black", # 根据bin的中心值判断:中心<0设蓝色,否则灰色 aes(fill = after_stat(x < 0)) ) + scale_fill_manual( values = c("FALSE" = "grey", "TRUE" = "#41A2D0"), guide = FALSE ) + theme_minimal() + labs( x = "Change in Percentage of Body Weight (%)", y = "Number of Individuals", title = "Relative Weight Change", subtitle = "Relative weight change for individuals whose goal was to lose weight and completed the program (n=209)" )
如果想更精准控制,可以把after_stat(x < 0)改成after_stat(xmin < 0),用bin的左边界判断,确保所有负数区间的bin都显示蓝色。
方法2:手动计算bin统计量,用geom_col绘制
先手动计算每个bin的计数和区间,再指定颜色,完全掌控绘制逻辑:
library(dplyr) # 计算每个bin的计数和颜色 bin_data <- df4 %>% # 划分左闭右开的bin,和ggplot默认逻辑一致 mutate(bin = cut(Percentage.of.change, breaks = seq(floor(min(Percentage.of.change)), ceiling(max(Percentage.of.change)), by = 1), include.lowest = TRUE, right = FALSE)) %>% count(bin) %>% # 计算bin的中心值,用于x轴定位 mutate(bin_center = as.numeric(sub("\\[(.*),.*)", "\\1", bin)) + 0.5, # 根据中心值判断颜色 fill_color = ifelse(bin_center < 0, "#41A2D0", "grey")) # 绘制柱状图 ggplot(bin_data, aes(x = bin_center, y = n, fill = fill_color)) + geom_col(width = 1, col = "black") + scale_fill_identity(guide = FALSE) + theme_minimal() + labs( x = "Change in Percentage of Body Weight (%)", y = "Number of Individuals", title = "Relative Weight Change", subtitle = "Relative weight change for individuals whose goal was to lose weight and completed the program (n=209)" )
内容的提问来源于stack exchange,提问作者pww_qs
相关产品推荐
相关产品推荐

