绘制Slope Chart时遇ylim()报错:离散值传入连续刻度
问题:绘制Slope Chart时触发离散值/连续刻度不匹配错误
首次尝试绘制Slope Chart,代码如下:
df1 <- dplyr::data_frame('tests' = c("2023","2024"), 'Pre-season Avg.' = c(42.35,51.38), 'Post-season Avg.' = c(90.4,86.56))
colnames(df1) <- c("tests", "Pre-season Avg.", "Post-season Avg.") left_label <- paste(df1$'tests', df1$'Pre-season Avg.') right_label <- paste(df1$'tests', df1$'Post-season Avg.') df1$class <- ifelse((df1$'Post-season Avg.' - df1$'Pre-season Avg.') < 0, "red", "dodgerblue")
plot <- ggplot(df1) + geom_segment(aes(x=1, xend=2, y="Pre-season Avg.", yend="Post-season Avg.", col=class), size=0.75, show.legend=F) + geom_vline(xintercept=1, linetype="dashed", size=0.1) + geom_vline(xintercept=2, linetype="dashed", size=0.1) + scale_color_manual(labels = c("Up","Down"), values=c("red", "dodgerblue")) + labs(x="", y="Scores by Average") + xlim(0.5,2.5) + ylim(0, (1.1*(max(df1$'Pre-season Avg.', df1$'Post-season Avg.')))) plot <- plot + geom_text(label=left_label, y=df1$'Pre-season Avg.', x=rep(1, NROW( df1)), hjust=1.1, size=3.5) plot <- plot + geom_text(label=right_label, y=df1$'Post-season Avg.', x=rep(2, NROW( df1)), hjust=-0.1, size=3.5) plot <- plot + geom_text(label="Pre-season", x=1, y=1.1*(max(df1$'Pre-season Avg.', df1$'Post-season Avg.')), hjust=1.2, size=5) plot <- plot + geom_text(label="Post-season", x=2, y=1.1*(max(df1$'Pre-season Avg.', df1$'Post-season Avg.')), hjust=-0.1, size=5) plot + theme(panel.background = element_blank(), panel.grid = element_blank(), axis.ticks = element_blank(), axis.text.x = element_blank(), panel.border = element_blank(), plot.margin = unit(c(1,2,1,2), "cm")) plot
其中df1$'Pre-season Avg.'和df1$'Post-season Avg.'均为double类型,但运行时触发报错:
Error in `ylim()`: ! Discrete values supplied to continuous scale. ℹ Example values: "Pre-season Avg." and "Pre-season Avg."
错误原因分析
- 核心问题出在
geom_segment的y和yend参数:你传入的是字符串"Pre-season Avg."和"Post-season Avg.",ggplot2会将这些字符串识别为离散类别,而非对应列中的连续数值,因此和后续设置的连续ylim冲突,触发报错。 - 你的数据是宽格式(wide format),而ggplot2的设计更适配长格式(tidy format)数据,这也是导致此类错误的常见诱因。
修复方案
方案1:直接修正原代码的参数引用
将geom_segment中带引号的列名字符串替换为实际的列引用(列名含空格时用反引号包裹):
plot <- ggplot(df1) + # 关键修改:把y="Pre-season Avg."改成y=`Pre-season Avg.` geom_segment(aes(x=1, xend=2, y=`Pre-season Avg.`, yend=`Post-season Avg.`, col=class), size=0.75, show.legend=F) + geom_vline(xintercept=1, linetype="dashed", size=0.1) + geom_vline(xintercept=2, linetype="dashed", size=0.1) + scale_color_manual(labels = c("Up","Down"), values=c("red", "dodgerblue")) + labs(x="", y="Scores by Average") + xlim(0.5,2.5) + ylim(0, (1.1*(max(df1$'Pre-season Avg.', df1$'Post-season Avg.')))) # 后续代码保持不变...
方案2:转成长格式数据(推荐,更符合ggplot2规范)
将宽格式数据转换为长格式,代码会更简洁、易维护:
library(dplyr) library(tidyr) library(ggplot2) # 1. 准备长格式数据 df1 <- tibble(tests = c("2023","2024"), `Pre-season Avg.` = c(42.35,51.38), `Post-season Avg.` = c(90.4,86.56)) %>% # 转长格式:将赛季列合并为一列 pivot_longer(cols = -tests, names_to = "season", values_to = "avg_score") %>% # 给赛季分配x轴位置 mutate(season_order = ifelse(season == "Pre-season Avg.", 1, 2)) # 2. 计算分数变化方向的颜色 df1 <- df1 %>% group_by(tests) %>% mutate(class = ifelse(last(avg_score) - first(avg_score) < 0, "red", "dodgerblue")) %>% ungroup() # 3. 绘制Slope Chart ggplot(df1, aes(x = season_order, y = avg_score, group = tests, color = class)) + geom_segment(aes(xend = lead(season_order), yend = lead(avg_score)), size=0.75, show.legend=F) + geom_vline(xintercept = 1, linetype="dashed", size=0.1) + geom_vline(xintercept = 2, linetype="dashed", size=0.1) + # 添加数据标签 geom_text(aes(label = paste(tests, avg_score)), hjust = ifelse(season_order == 1, 1.1, -0.1), size=3.5) + # 添加赛季标题 geom_text(x=1, y=1.1*max(df1$avg_score), label="Pre-season", hjust=1.2, size=5) + geom_text(x=2, y=1.1*max(df1$avg_score), label="Post-season", hjust=-0.1, size=5) + scale_color_manual(labels = c("Up","Down"), values=c("red", "dodgerblue")) + labs(x="", y="Scores by Average") + xlim(0.5,2.5) + ylim(0, 1.1*max(df1$avg_score)) + theme(panel.background = element_blank(), panel.grid = element_blank(), axis.ticks = element_blank(), axis.text.x = element_blank(), panel.border = element_blank(), plot.margin = unit(c(1,2,1,2), "cm"))
内容的提问来源于stack exchange,提问作者RKeithL
相关产品推荐
相关产品推荐

