ggplot2绘制饼图:图例百分比与切片不匹配问题排查
问题原因及修复方案
核心问题原因
- 类别顺序不统一:你手动构建的
pie_data里的AgeCategorySex是按自定义顺序排列的,但ggplot会自动将这个字符串向量转为因子,并按字母顺序排序切片和图例。而你生成的custom_legend是按原始自定义顺序拼接的,这就导致图例标签和实际切片的对应关系完全错乱,百分比和颜色自然不匹配。 - 未固定颜色映射:你定义了
colors调色板但从未使用,ggplot默认的颜色映射会随类别排序变化,进一步加剧颜色和类别的不对应。 - 冗余数据操作:循环中反复修改
breed_data$AgeCategory是多余的,你已经通过age_counts直接构建了pie_data,这部分操作不会影响饼图,但会增加不必要的计算开销。
修复代码调整
1. 提前将类别设为固定顺序的因子
在定义AgeCategorySex后添加:
# 将类别设为固定顺序的因子,避免ggplot自动排序 AgeCategorySex <- factor(AgeCategorySex, levels = AgeCategorySex)
2. 替换图例和颜色映射的代码
把原来的scale_fill_discrete替换为scale_fill_manual,手动绑定颜色、类别和标签:
# 替换原有的图例设置代码 chart <- chart + guides(fill = guide_legend(title = "Age Category (Percentage)")) + scale_fill_manual( values = colors, # 使用你定义的颜色板 labels = custom_legend, breaks = AgeCategorySex # 强制按因子顺序显示图例 )
3. 移除冗余的breed_data$AgeCategory操作
删掉以下代码(因为你没有用这个列生成饼图,完全冗余):
# Add AgeCategory column and initialize it breed_data$AgeCategory <- NA
以及内层循环中对breed_data$AgeCategory[j]的赋值操作。
完整修复后的核心循环片段
# Loop through each breed for (breed in data2) { breed_data <- subset(data1, Primary.Breed == breed) # Initialize vector to store counts for each age category sex age_counts <- rep(0, length(AgeCategorySex)) # Calculate difference in days between each birth date and each date in the vector for (i in 1:length(dates)) { date2 <- dates[i] # Reset age counts for each date age_counts <- rep(0, length(AgeCategorySex)) # Calculate age category for each record for (j in 1:nrow(breed_data)) { diff <- as.numeric(difftime(date2, breed_data$DateOfBirth[j], units = "days")) if (!is.na(diff) && diff>0) { if (diff < 90 && breed_data$Sex[j] == "M") { age_counts[1] <- age_counts[1] + 1 } else if (diff < 90 && breed_data$Sex[j] == "F") { age_counts[2] <- age_counts[2] + 1 } else if (diff >= 90 && diff < 180 && breed_data$Sex[j] == "M") { age_counts[3] <- age_counts[3] + 1 } else if (diff >= 90 && diff < 180 && breed_data$Sex[j] == "F") { age_counts[4] <- age_counts[4] + 1 } else if (diff >= 180 && diff < 365 && breed_data$Sex[j] == "M") { age_counts[5] <- age_counts[5] + 1 } else if (diff >= 180 && diff < 365 && breed_data$Sex[j] == "F") { age_counts[6] <- age_counts[6] + 1 } else if (diff >= 365 && breed_data$Sex[j] == "M") { age_counts[7] <- age_counts[7] + 1 } else if (diff >= 365 && breed_data$Sex[j] == "F") { age_counts[8] <- age_counts[8] + 1 } } } # Calculate total count total_count <- sum(age_counts) # Calculate percentages percentages <- paste0(round((age_counts / total_count) * 100, 2), "%") # Create the pie chart using ggplot2 chart_title <- paste("Pie chart for", breed, "on", as.character(dates[i]), "(Total:", total_count, ")") pie_data <- data.frame(AgeCategorySex, Count = age_counts) chart <- ggplot(pie_data, aes(x = "", y = Count, fill = AgeCategorySex)) + geom_bar(stat = "identity") + coord_polar("y", start = 0) + labs(title = chart_title) + theme_void() # Add custom legend with percentages and fixed color mapping custom_legend <- paste(pie_data$AgeCategorySex, percentages, sep = " - ") chart <- chart + guides(fill = guide_legend(title = "Age Category (Percentage)")) + scale_fill_manual( values = colors, labels = custom_legend, breaks = AgeCategorySex ) # Save the plot as a PNG file with increased width ggsave(filename = paste0(breed, "_plot", date2, ".png"), plot = chart, width = 10, height = 4) # Add the plot image to the Word document doc <- body_add_img(doc, src = paste0(breed, "_plot", date2, ".png"), width = 7, height = 4) # Print plot for current month and breed print(chart) } }
内容的提问来源于stack exchange,提问作者Michael Solomon
相关产品推荐
相关产品推荐

