R语言Plotly多Y轴堆叠折线图:多X位置悬停显示与Y轴刻度范围调整问题
R语言Plotly多Y轴堆叠折线图:多X位置悬停显示与Y轴刻度范围调整问题
嘿,我来帮你搞定这两个Plotly堆叠折线图的问题,咱们一步步捋清楚~
一、实现多个X位置同时保留悬停信息
你当前的JS代码只能同步单个X位置的悬停效果,要实现新悬停X点时不清除旧悬停信息,我们需要修改JavaScript逻辑,用集合来存储所有需要显示悬停的X值,每次新hover时把X值加入集合,再对所有已记录的X值触发悬停。
修改后的JS代码及调用逻辑
注意要让labels的数量和子图数量完全匹配(你的例子里有3个Y变量,所以对应3个子图):
# 匹配子图数量的标签数组 labels <- c("xy", "xy2", "xy3") labels_json <- jsonlite::toJSON(labels) js_code <- sprintf( 'function(el, x){ // 用集合存储所有需要显示悬停的X值(自动去重) let hoverXs = new Set(); el.on("plotly_hover", function(d) { const currentX = d.xvals[0]; hoverXs.add(currentX); // 对每个子图,触发所有已记录X值的悬停 hoverXs.forEach(xVal => { Plotly.Fx.hover(el.id, {xval: xVal}, %s); }); }); // 可选:点击图表空白处清空所有悬停 el.on("plotly_click", function() { hoverXs.clear(); Plotly.Fx.unhover(el.id); }); }', labels_json )
这样修改后,每次hover新的Time值,之前的悬停信息会保留;如果需要清除所有悬停点,点击图表空白处即可。
二、调整Y轴刻度,显示更多ticks
你的堆叠图用了subplot生成独立Y轴,要增加刻度数量,有两种灵活的实现方式:
方法1:在plotly_stacked函数内自动设置所有Y轴
修改函数,在生成子图后遍历每个Y轴,指定刻度数量或间隔:
plotly_stacked <- function(df, x_colName, cols){ DF <- df[, cols] %>% tidyr::gather(variable, value, -x_colName ) %>% transform(id = as.integer(factor(variable))) DF$variable<- factor( DF$variable, levels = unique( DF$variable)) p <- plot_ly(data = DF, x = ~get(names(DF[1])) , y = ~value, color = ~variable, colors = "Dark2", yaxis = ~paste0( "y",sort(id, decreasing = F))) %>% add_lines() %>% layout( xaxis = list(title = ""), legend = list( orientation = "h", xanchor = "center", x = 0.5)) %>% plotly::subplot(nrows = length(unique(DF$variable)), shareX = TRUE) # 新增:给每个Y轴设置刻度数量,同时添加变量名作为Y轴标题 y_axis_list <- list() for(i in 1:length(unique(DF$variable))){ y_axis_name <- paste0("yaxis", i) y_axis_list[[y_axis_name]] <- list( nticks = 10, # 指定刻度数量,可根据数据调整 # 也可以用dtick指定固定间隔,比如dtick = 0.02 title = unique(DF$variable)[i] ) } p <- p %>% layout(!!!y_axis_list) return(p) }
方法2:调用函数后手动单独设置Y轴
如果不想修改原函数,也可以在生成图表后,通过layout单独调整每个Y轴:
p <- plotly_stacked(df, "Time", c( "Time" ,"a", "b", "c")) %>% layout( yaxis = list(nticks = 10), # 对应变量a的Y轴 yaxis2 = list(nticks = 10), # 对应变量b的Y轴 yaxis3 = list(nticks = 10) # 对应变量c的Y轴 ) %>% layout(hovermode = "x") %>% htmlwidgets::onRender(js_code)
完整可运行测试代码
把所有修改整合后的完整代码如下,直接运行就能看到效果:
library(plotly) library(tidyr) library(dplyr) library(htmlwidgets) library(jsonlite) # 修改后的堆叠图函数 plotly_stacked <- function(df, x_colName, cols){ DF <- df[, cols] %>% tidyr::gather(variable, value, -x_colName ) %>% transform(id = as.integer(factor(variable))) DF$variable<- factor( DF$variable, levels = unique( DF$variable)) p <- plot_ly(data = DF, x = ~get(names(DF[1])) , y = ~value, color = ~variable, colors = "Dark2", yaxis = ~paste0( "y",sort(id, decreasing = F))) %>% add_lines() %>% layout( xaxis = list(title = ""), legend = list( orientation = "h", xanchor = "center", x = 0.5)) %>% plotly::subplot(nrows = length(unique(DF$variable)), shareX = TRUE) # 自动设置每个Y轴的刻度与标题 y_axis_list <- list() for(i in 1:length(unique(DF$variable))){ y_axis_name <- paste0("yaxis", i) y_axis_list[[y_axis_name]] <- list( nticks = 10, title = unique(DF$variable)[i] ) } p <- p %>% layout(!!!y_axis_list) return(p) } # 创建测试数据框 df <- data.frame( Time = c(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9, 1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2), a = c(0.01,0.02,0.03,0.04,0.05,0.06,0.07, 0.08,0.09,0.1,0.11,0.12,0.13,0.14,0.15,0.16,0.17,0.18, 0.19,0.2), b = c(0.001,0.002,0.003,0.004,0.005,0.006, 0.007,0.008,0.009,0.01,0.011,0.012,0.013,0.014,0.015, 0.016,0.017,0.018,0.019,0.02), c = c(1e-04,2e-04,3e-04,4e-04,5e-04,6e-04, 7e-04,8e-04,9e-04,0.001,0.0011,0.0012,0.0013,0.0014, 0.0015,0.0016,0.0017,0.0018,0.0019,0.002) ) # 多X悬停的JS代码 labels <- c("xy", "xy2", "xy3") # 对应3个子图 labels_json <- jsonlite::toJSON(labels) js_code <- sprintf( 'function(el, x){ let hoverXs = new Set(); el.on("plotly_hover", function(d) { const currentX = d.xvals[0]; hoverXs.add(currentX); hoverXs.forEach(xVal => { Plotly.Fx.hover(el.id, {xval: xVal}, %s); }); }); // 点击空白处清除所有悬停 el.on("plotly_click", function() { hoverXs.clear(); Plotly.Fx.unhover(el.id); }); }', labels_json ) # 生成并渲染图表 p <- plotly_stacked(df, "Time", c( "Time" ,"a", "b", "c")) %>% layout(hovermode = "x") %>% htmlwidgets::onRender(js_code) p
运行后你会看到:
- 每次hover不同的Time值,之前的悬停信息会保留,所有hover过的点都会在每个子图显示对应数值;
- 每个Y轴的刻度数量增加,数据细节展示更清晰。
备注:内容来源于stack exchange,提问作者ACE
相关产品推荐
相关产品推荐

