自定义ggplot2 ggproto的draw_key函数alpha图例异常修复咨询
问题根因
setup_params缺少参数存在性判断:原有逻辑不管用户是否主动传入alpha参数、是否将alpha映射到变量,都会强行根据数据量覆盖params$alpha,会导致用户显式传入的固定alpha值被自动规则覆盖。- 初始版本的
draw_key直接用params$alpha强制覆盖了data$alpha,即便是alpha已经映射到变量、data$alpha已经拿到了正确的映射值也会被替换,这是alpha映射场景下图例错误的核心原因,你后续修改的加了NA判断的draw_key已经解决了这个问题,还有两处逻辑需要优化即可覆盖所有场景。 draw_panel里的alpha赋值逻辑不严谨:any(is.na(coords$alpha))只要存在一个NA值就会把所有点的alpha都替换为params$alpha,如果存在部分行alpha为NA、部分行是映射值的场景,会把正常的映射值覆盖。
修复方案
直接替换GeomMyPoint里的setup_params和draw_panel方法,draw_key保留你修改后的版本即可:
GeomMyPoint <- ggplot2::ggproto("GeomMyPoint", ggplot2::Geom, required_aes = c("x", "y"), non_missing_aes = c("size", "shape", "colour"), default_aes = ggplot2::aes( shape = 19, colour = "black", size = 2, fill = NA, alpha = NA, stroke = 0.5 ), setup_params = function(data, params) { # 仅当用户没有主动传入alpha参数时才自动根据数据量计算alpha if (is.null(params$alpha)) { n <- nrow(data) if (n > 100 && n <= 200) { params$alpha <- 0.3 } else if (n > 200) { params$alpha <- 0.15 } else { params$alpha <- 1 } } params }, draw_panel = function(data, panel_scales, coord, alpha) { if (is.character(data$shape)) { data$shape <- ggplot2::translate_shape_string(data$shape) } coords <- coord$transform(data, panel_scales) # 仅替换alpha为NA的行,不覆盖已经存在的映射值/用户传入值 coords$alpha[is.na(coords$alpha)] <- alpha grid::pointsGrob( x = coords$x, y = coords$y, pch = coords$shape, gp = grid::gpar( col = alpha(coords$colour, coords$alpha), fill = alpha(coords$fill, coords$alpha), fontsize = coords$size * ggplot2::.pt + coords$stroke * ggplot2::.stroke / 2, lwd = coords$stroke * ggplot2::.stroke / 2 ) ) }, draw_key = function(data, params, size) { if (is.na(data$alpha)) { data$alpha <- params$alpha } ggplot2::draw_key_point(data, params, size) } )
修复后四个测试用例的图例都可以正常显示:
- gg1:无alpha映射和传参,使用自动计算的alpha,图例和图中alpha透明度一致
- gg2:显式传alpha=1,图例和图中点都为不透明
- gg3:alpha映射到分组变量z,图例的两个分组透明度和图中实际展示一致
- gg4:alpha和size同时映射到变量,两个维度的图例都可正常展示
内容的提问来源于stack exchange,提问作者moremo
相关产品推荐
相关产品推荐

