You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ggplotly中dynamicTicks = T不遵循position属性的解决求助

问题

我用ggplot创建了一个调整过点位置的图表,但设置dynamicTicks = TRUE属性后,点的位置恢复成默认状态了。有没有办法覆盖这个设置,保留我指定的点位置?

(图:dynamicTicks = FALSE时的效果)
(图:dynamicTicks = TRUE时的效果)

可复现代码

library(tidyverse)
library(plotly)

df <- structure(list(SerialNumber = c("634", "634", "567", "634", "634", 
                                      "562", "567", "567", "562", "562", "634", "569", "634", "634", 
                                      "634", "634", "634", "634", "634", "634", "634", "634", "561", 
                                      "634", "569", "569", "634", "634", "634", "567", "567", "567", 
                                      "634", "634", "567", "567", "567", "634", "562", "569"), `_device` = c("module1", 
                                                                                                             "module1", "module1", "module1", "module2", "module4", "module2", 
                                                                                                             "module2", "module4", "module1", "module1", "module4", "module1", 
                                                                                                             "module1", "module1", "module1", "module1", "module1", "module3", 
                                                                                                             "module1", "module1", "module3", "module2", "module1", "module1", 
                                                                                                             "module1", "module1", "module1", "module2", "module1", "module1", 
                                                                                                             "module3", "module1", "module1", "module1", "module1", "module1", 
                                                                                                             "module1", "module1", "module4"), `_source_program` = c("program1", 
                                                                                                                                                                     "program2", "program3", "program2", "program4", "program2", "program2", 
                                                                                                                                                                     "program1", "program2", "program2", "program2", "program2", "program2", 
                                                                                                                                                                     "program2", "program2", "program2", "program2", "program2", "ONprogram3", 
                                                                                                                                                                     "program1", "program3", "ONprogram3", "program6", "program1", 
                                                                                                                                                                     "program6", "program2", "program4", "program6", "program4", "program6", 
                                                                                                                                                                     "program6", "ONprogram3", "program6", "program2", "program4", 
                                                                                                                                                                     "program6", "program6", "program2", "program6", "program2"), 
                     date = structure(c(18891, 18906, 18937, 18940, 18943, 18949, 
                                        18952, 18956, 18966, 18972, 18983, 18981, 19007, 19023, 19036, 
                                        19036, 19037, 19066, 19072, 19082, 19082, 19088, 19086, 19091, 
                                        19106, 19106, 19115, 19109, 19122, 19142, 19145, 19151, 19149, 
                                        19149, 19157, 19157, 19169, 19175, 19185, 19192), class = "Date")), row.names = c(NA, 
                                                                                                                          -40L), spec = structure(list(cols = list(SerialNumber = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                              "collector")), `_device` = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                                                                     "collector")), `_source_program` = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                                                                                                                                    "collector")), date = structure(list(format = ""), class = c("collector_date", 
                                                                                                                                                                                                                                                                                                                                                                                                 "collector"))), default = structure(list(), class = c("collector_guess", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                       "collector")), delim = ","), class = "col_spec"), class = c("spec_tbl_df", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   "tbl_df", "tbl", "data.frame"))

programs <- unique(df$`_source_program`)

nudgeAmounts <- ""

for (i in 1:length(programs)) {
  if (i == 1) {
    nudgeAmounts <- ifelse(df$`_source_program` == programs[i],as.double(paste0("0.", i)), 0)
  } else {
    nudgeAmounts <- ifelse(df$`_source_program` == programs[i],as.double(paste0("0.", i)), nudgeAmounts)
  }
  
}

shapes <- c(15,16,17,18,7,8)
names(shapes) <- programs

g <- ggplot(df, aes(date, SerialNumber, shape = `_source_program` , color =`_device`)) +
  geom_point(position = position_nudge(y = nudgeAmounts), 
             size = 3
  ) +
  theme_bw() + 
  expand_limits(y = c(1, length(levels(as.factor(df$SerialNumber))) + 1)) + 
  labs(y = "Serial Number", x = "Date") +
  scale_shape_manual(values = shapes)

g
ggplotly(g, dynamicTicks = TRUE) 
解决方案

问题根源在于dynamicTicks = TRUE会让plotly重新计算y轴的坐标映射,直接覆盖ggplot中position_nudge的偏移效果。要解决这个问题,不在绘图层做偏移,而是直接修改数据里的y值,这样不管plotly是否启用动态刻度,都会基于修改后的坐标渲染点。

具体操作:

  1. 将SerialNumber转换为数值类型,方便添加偏移量
  2. 给每个_source_program对应的行添加固定的小偏移,生成新的y列(比如y_adjusted)
  3. 用新的y列绘图,同时通过scale_y_continuous设置刻度标签为原SerialNumber的值

修改后的完整代码

library(tidyverse)
library(plotly)

df <- structure(list(SerialNumber = c("634", "634", "567", "634", "634", 
                                      "562", "567", "567", "562", "562", "634", "569", "634", "634", 
                                      "634", "634", "634", "634", "634", "634", "634", "634", "561", 
                                      "634", "569", "569", "634", "634", "634", "567", "567", "567", 
                                      "634", "634", "567", "567", "567", "634", "562", "569"), `_device` = c("module1", 
                                                                                                             "module1", "module1", "module1", "module2", "module4", "module2", 
                                                                                                             "module2", "module4", "module1", "module1", "module4", "module1", 
                                                                                                             "module1", "module1", "module1", "module1", "module1", "module3", 
                                                                                                             "module1", "module1", "module3", "module2", "module1", "module1", 
                                                                                                             "module1", "module1", "module1", "module2", "module1", "module1", 
                                                                                                             "module3", "module1", "module1", "module1", "module1", "module1", 
                                                                                                             "module1", "module1", "module4"), `_source_program` = c("program1", 
                                                                                                                                                                     "program2", "program3", "program2", "program4", "program2", "program2", 
                                                                                                                                                                     "program1", "program2", "program2", "program2", "program2", "program2", 
                                                                                                                                                                     "program2", "program2", "program2", "program2", "program2", "ONprogram3", 
                                                                                                                                                                     "program1", "program3", "ONprogram3", "program6", "program1", 
                                                                                                                                                                     "program6", "program2", "program4", "program6", "program4", "program6", 
                                                                                                                                                                     "program6", "ONprogram3", "program6", "program2", "program4", 
                                                                                                                                                                     "program6", "program6", "program2", "program6", "program2"), 
                     date = structure(c(18891, 18906, 18937, 18940, 18943, 18949, 
                                        18952, 18956, 18966, 18972, 18983, 18981, 19007, 19023, 19036, 
                                        19036, 19037, 19066, 19072, 19082, 19082, 19088, 19086, 19091, 
                                        19106, 19106, 19115, 19109, 19122, 19142, 19145, 19151, 19149, 
                                        19149, 19157, 19157, 19169, 19175, 19185, 19192), class = "Date")), row.names = c(NA, 
                                                                                                                          -40L), spec = structure(list(cols = list(SerialNumber = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                              "collector")), `_device` = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                                                                     "collector")), `_source_program` = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                                                                                                                                    "collector")), date = structure(list(format = ""), class = c("collector_date", 
                                                                                                                                                                                                                                                                                                                                                                                                 "collector"))), default = structure(list(), class = c("collector_guess", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                       "collector")), delim = ","), class = "col_spec"), class = c("spec_tbl_df", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   "tbl_df", "tbl", "data.frame"))

# 1. 给每个program分配偏移量,直接在数据层面处理
programs <- unique(df$`_source_program`)
nudge_map <- setNames(seq(0.1, length(programs)*0.1, by=0.1), programs)

# 2. 转换SerialNumber为数值,并添加偏移得到新的y列
df <- df %>%
  mutate(
    SerialNumber_num = as.numeric(SerialNumber),
    y_adjusted = SerialNumber_num + nudge_map[`_source_program`]
  )

shapes <- c(15,16,17,18,7,8)
names(shapes) <- programs

# 3. 用调整后的y_adjusted绘图,设置刻度为原SerialNumber的值
g <- ggplot(df, aes(date, y_adjusted, shape = `_source_program`, color = `_device`)) +
  geom_point(size = 3) +
  theme_bw() +
  scale_y_continuous(
    breaks = unique(df$SerialNumber_num),
    labels = unique(df$SerialNumber),
    limits = range(df$y_adjusted) + c(-0.5, 0.5)
  ) +
  labs(y = "Serial Number", x = "Date") +
  scale_shape_manual(values = shapes)

g
ggplotly(g, dynamicTicks = TRUE)

内容的提问来源于stack exchange,提问作者Zoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:54:25