R语言Shiny框架中Plotly分组散点图无法更新及悬停信息缺失的问题求助
问题描述
我希望在R语言的Shiny框架中使用Plotly库创建一个交互式散点图,用于展示分组数据,且鼠标悬停在数据点上时,能够显示该点的X轴、Y轴坐标以及分组变量名称(本示例中为Species)。我对Shiny较为熟悉,但刚接触Plotly,以下是我编写的示例代码:
library(shiny) library(shinyWidgets) library(plotly) library(tidyverse) ui<-fluidPage( titlePanel("Iris"), tabsetPanel( tabPanel("Iris scatter", sidebarPanel(width=5, h5("Select the axis variables"), selectInput("eda_scatter_x", "Select x-axis", c("Sepal Length"="Sepal.Length", "Sepal Width" ="Sepal.Width", "Petal Length"= "Petal.Length", "Petal Width"= "Petal.Width")), selectInput("eda_scatter_y", "Select y-axis", c("Sepal Length"="Sepal.Length", "Sepal Width" ="Sepal.Width", "Petal Length"= "Petal.Length", "Petal Width"= "Petal.Width")), actionButton(inputId = "eda_run", "Run analysis")), mainPanel( plotlyOutput("eda_scatter_graph") )) ) ) server <- function(input,output,session){ observeEvent(input$eda_run,{ output$eda_scatter_graph<- renderPlotly({ iris%>% group_by(Species)%>% summarise_at(vars(Sepal.Length:Petal.Width),mean)%>% plot_ly(x=input$eda_scatter_x, y=input$eda_scatter_y, size=I(c(100,50)))%>% add_markers() }) }) } shinyApp(ui,server)
目前遇到两个问题:
- 通过selectInput选择X轴、Y轴变量后,点击“Run analysis”按钮,图表无法更新;
- 鼠标悬停在数据点上时,无法显示分组变量名称及对应的坐标信息。
恳请指出代码中的错误,并提供修改建议,谢谢!
解决方案
咱们来一步步拆解并解决这两个问题:
问题1:图表无法更新的原因及修复
核心错误:
plot_ly()中直接传入input$eda_scatter_x这类字符型变量名是无效的——Plotly需要的是数据框中的变量映射,而非字符串常量,这会导致图表始终使用初始的无效值,自然无法响应选择变化。
修复方法:
使用tidyverse的tidyeval语法(sym()和!!运算符),把字符串变量名转换成Plotly能识别的变量映射,或者用get()函数提取变量,这里推荐前者,更贴合tidyverse的使用习惯。
问题2:悬停信息缺失的原因及修复
核心错误:
你没有自定义Plotly的悬停提示内容,默认提示只会显示基础的数值,不会包含分组名称;另外你设置的size=I(c(100,50))只有两个值,但鸢尾花的分组有3个,会导致尺寸赋值异常,也会影响体验。
修复方法:
- 在
add_markers()中用text参数自定义悬停文本,配合hoverinfo="text"显示自定义内容; - 调整size参数为适合3个分组的统一值,或者按需求设置对应尺寸。
修改后的完整代码
library(shiny) library(shinyWidgets) library(plotly) library(tidyverse) ui<-fluidPage( titlePanel("Iris"), tabsetPanel( tabPanel("Iris scatter", sidebarPanel(width=5, h5("Select the axis variables"), selectInput("eda_scatter_x", "Select x-axis", c("Sepal Length"="Sepal.Length", "Sepal Width" ="Sepal.Width", "Petal Length"= "Petal.Length", "Petal Width"= "Petal.Width")), selectInput("eda_scatter_y", "Select y-axis", c("Sepal Length"="Sepal.Length", "Sepal Width" ="Sepal.Width", "Petal Length"= "Petal.Length", "Petal Width"= "Petal.Width")), actionButton(inputId = "eda_run", "Run analysis")), mainPanel( plotlyOutput("eda_scatter_graph") )) ) ) server <- function(input,output,session){ # 用reactiveVal存储汇总数据,让逻辑更清晰,也方便后续复用 summarized_data <- reactiveVal(NULL) observeEvent(input$eda_run,{ # 计算分组均值,用across替代summarise_at(tidyverse新版本推荐语法) data <- iris%>% group_by(Species)%>% summarise(across(Sepal.Length:Petal.Width, mean), .groups = "drop") summarized_data(data) }) output$eda_scatter_graph<- renderPlotly({ # 确保数据存在才渲染图表,避免初始状态报错 req(summarized_data()) # 把字符串变量名转换成Plotly可识别的变量映射 x_var <- sym(input$eda_scatter_x) y_var <- sym(input$eda_scatter_y) summarized_data()%>% plot_ly(x = x_var, y = y_var, size = I(100))%>% add_markers( # 自定义悬停文本,包含分组名称和格式化的坐标值 text = ~paste0("Species: ", Species, "\n", str_replace_all(input$eda_scatter_x, "\\.", " "), ": ", round(!!x_var, 2), "\n", str_replace_all(input$eda_scatter_y, "\\.", " "), ": ", round(!!y_var, 2)), hoverinfo = "text" )%>% # 设置坐标轴为用户选择的友好名称(比如显示"Sepal Length"而非"Sepal.Length") layout( xaxis = list(title = names(which(c("Sepal Length"="Sepal.Length", "Sepal Width" ="Sepal.Width", "Petal Length"= "Petal.Length", "Petal Width"= "Petal.Width") == input$eda_scatter_x))), yaxis = list(title = names(which(c("Sepal Length"="Sepal.Length", "Sepal Width" ="Sepal.Width", "Petal Length"= "Petal.Length", "Petal Width"= "Petal.Width") == input$eda_scatter_y))) ) }) } shinyApp(ui,server)
额外优化说明
- 用
across()替代了summarise_at(),这是tidyverse 1.0.0版本后的推荐语法,兼容性和可读性更好; - 添加
req(summarized_data())避免初始状态下数据为空导致的报错; - 优化了坐标轴标签,显示用户选择的友好名称,提升体验;
- 对悬停数值做了四舍五入,让信息更整洁。
内容的提问来源于stack exchange,提问作者metaltoaster
相关产品推荐
相关产品推荐

