条件表格过滤下ggplot饼图图例与标签位置异常问题
问题诊断与修复
问题根源
饼图标签位置错误的核心原因是pie_final()生成的数据框行顺序不统一:
- 当char1与player1顺序不一致时,
pie_filtered()会交换列,最终pie_final里先放char1的胜率,再放char2的胜率(对应原数据的败率) - 当顺序一致时,
pie_final里先放char1的胜率,再放char2的败率,但此时char2的败率实际是char1的败率,而非char2的胜率,导致数据行逻辑与顺序不匹配,进而csum(累计和)计算错误,标签位置偏移。
同时,pie_filtered()的if-else逻辑存在冗余过滤,列交换后的处理可以更简洁。
修复方案
- 简化
pie_filtered()逻辑,统一输出以input$char1为player1的记录,确保胜率对应char1,败率对应char2 - 强制
pie_final()的数据行顺序为char1在前、char2在后,保证累计和计算的一致性
修改后的完整代码
library(tidyverse) library(ggrepel) library(shiny) library(ggplot2) #create sample dataframe player1 <- c("Mario","Mario", "Peach") player2 <- c("Luigi", "Peach", "Luigi") pcnt_wins <- c(.75,.20,.55) pcnt_loss <- c(.25,.80,.45) data <- data.frame(player1, player2, pcnt_wins, pcnt_loss) ui <- fluidPage( selectInput("char1", "Character 1:", c("Mario" = "Mario", "Luigi" = "Luigi", "Peach" = "Peach")), selectInput("char2","Character 2", c("Mario" = "Mario", "Luigi" = "Luigi", "Peach" = "Peach")), tableOutput("data_table"), textOutput("text"), plotOutput("plot") ) server <- function(input, output) { #intermediate steps to create output table table_filtered <- reactive({ data %>% filter((player1 == input$char1 & player2 == input$char2) | (player1 == input$char2 & player2 == input$char1)) }) # 简化逻辑:统一输出以char1为player1的记录 pie_filtered <- reactive ({ filtered <- table_filtered() # 判断是否需要交换列 if (filtered$player1[1] != input$char1) { filtered %>% mutate( player1 = player2, player2 = player1, pcnt_wins = 1 - pcnt_wins, pcnt_loss = 1 - pcnt_loss ) } else { filtered } }) # 强制pie_final的行顺序:先char1,再char2 pie_final <- reactive({ data.frame( character = c(input$char1, input$char2), pie_win_pcnt = c(pie_filtered()$pcnt_wins, pie_filtered()$pcnt_loss) ) }) #create table output output$data_table <- renderTable({ pie_filtered() }) output$text <- renderText({ class(pie_final()$pie_win_pcnt) }) #create plot output output$plot <- renderPlot({ pie_final() %>% mutate(csum = rev(cumsum(rev(100*pie_win_pcnt))), pos = 100*pie_win_pcnt/2 + lead(csum, 1), pos = if_else(is.na(pos), 100*pie_win_pcnt/2, pos)) %>% ggplot(aes(x = "", y = 100*pie_win_pcnt, fill = character)) + geom_col(width = 1, color = 1) + coord_polar("y", start = 0) + theme_void() + geom_label_repel( aes(y = pos, label = paste0(round(100*pie_win_pcnt,0), "%")), size = 6, nudge_x = 1, show.legend = FALSE ) }) } shinyApp(ui,server)
关键修改点说明
pie_filtered():移除冗余的select和filter操作,直接修改原列;通过判断第一条记录的player1是否等于char1来决定是否交换,逻辑更简洁。pie_final():不再依赖pie_filtered()的列顺序,直接指定字符顺序为char1在前、char2在后,确保数据行顺序完全统一,csum计算逻辑能正确匹配饼图分段。- 移除重复的
geom_bar(geom_col已包含stat="identity"功能),简化绘图代码。
内容的提问来源于stack exchange,提问作者user1243jsat1
相关产品推荐
相关产品推荐

