自定义ggplot函数条件表达式列名引用失效,ggplotly悬浮标签异常
解决ggplotly悬浮提示显示函数代码而非实际值的问题
问题说明
自定义了生成ggplot柱状图的函数,支持将y轴设置为数值水平或百分比格式,通过if-else语句配置对应显示规则。但将图表转换为ggplotly交互式版本后,悬浮提示文本未显示实际y值,反而输出格式化函数的代码;若不用quote则会报错“对象values不存在”,说明函数执行时无法正确识别数据集中的列。
可复现代码如下:
library(tidyverse) #for dplyr library(ggplot2) #for chart library(scales) #for axis label library(plotly) #for interactive chart # 数据集 dataset <- data.frame(geography_name=c("a","b","c","d","e","f"), values=c(2,3,4,5,6,7)) # 自定义格式化函数 perc_form = function(x, d=1) sprintf(paste0("%1.",d,"f"), x) value_form = function(x,s=2,d= -1) format(signif(round(as.numeric(x), d),s), big.mark=",") # 生成ggplot图表的函数 barchart <- function( data_set = dataset, x_var = geography_name, y_var = values, bar_stat_type=NULL) { # 根据格式设置轴类型 if (bar_stat_type=="pct") { y_var_label <- paste0("Rate: ",quote(perc_form({{y_var}})),"%") label_form <- (percent_format(accuracy=1)) } else if (bar_stat_type=="money") { y_var_label <- paste0("Level: £",quote(value_form({{y_var}}))) label_form <- (comma_format()) } # 绘制图表 barchart <- data_set %>% ggplot(mapping = aes(x = {{x_var}}, y = {{y_var}}, text = paste0(geography_name, "\n", eval(y_var_label), "\n")))+ geom_bar(stat = "identity", position = position_dodge(), width = 0.4)+ scale_y_continuous(labels = eval(label_form)) # 此处eval可正常工作 return(barchart) } # ggplot图表可正常生成 gg <- barchart(bar_stat_type="money") gg # ggplotly的悬浮提示标签异常 ggplotly(gg)
解决方案
核心问题是aes中的text参数未在数据上下文里正确求值,quote的误用导致输出函数代码而非实际计算结果。通过提前在数据集中生成悬浮文本列的方式解决:
library(tidyverse) library(ggplot2) library(scales) library(plotly) # 数据集 dataset <- data.frame( geography_name=c("a","b","c","d","e","f"), values=c(2,3,4,5,6,7) ) # 自定义格式化函数 perc_form = function(x, d=1) sprintf(paste0("%1.",d,"f"), x) value_form = function(x,s=2,d= -1) format(signif(round(as.numeric(x), d),s), big.mark=",") # 修正后的绘图函数 barchart <- function( data_set = dataset, x_var = geography_name, y_var = values, bar_stat_type=NULL) { # 提前生成悬浮文本列,确保在数据上下文里求值 data_processed <- data_set %>% mutate( hover_text = case_when( bar_stat_type == "pct" ~ paste0({{x_var}}, "\nRate: ", perc_form({{y_var}}), "%"), bar_stat_type == "money" ~ paste0({{x_var}}, "\nLevel: £", value_form({{y_var}})), TRUE ~ paste0({{x_var}}, "\nValue: ", {{y_var}}) ) ) # 确定y轴标签格式 y_label_form <- if (bar_stat_type == "pct") { percent_format(accuracy=1) } else if (bar_stat_type == "money") { comma_format() } else { identity } # 生成图表 barchart <- data_processed %>% ggplot(mapping = aes(x = {{x_var}}, y = {{y_var}}, text = hover_text))+ geom_bar(stat = "identity", position = position_dodge(), width = 0.4)+ scale_y_continuous(labels = y_label_form) return(barchart) } # 测试 gg <- barchart(bar_stat_type="money") gg ggplotly(gg)
修改要点
- 提前生成悬浮文本:用
dplyr::mutate结合case_when在数据集中创建hover_text列,直接在数据上下文里调用格式化函数,确保拿到实际计算后的数值。 - 简化轴格式处理:去掉不必要的
eval,直接返回格式化函数给scale_y_continuous的labels参数。 - 避免
quote误用:不再用quote包裹函数调用,而是直接在数据行上计算出最终文本。
内容的提问来源于stack exchange,提问作者Modern_ibex
相关产品推荐
相关产品推荐

