使用dplyr的slice_max筛选销售额最高的两个国家全部行的问题排查及含NA值场景处理
我来帮你分析下问题所在,以及怎么修正!
原代码的问题
你当前的代码逻辑是按国家分组后,在每个组内筛选s值最大的2行,但s是每个国家的销售额总和,每个组里的所有行s值都完全一致,所以slice_max会返回每个组里的所有行(只要行数≤2),这显然不是你要的——你真正需要的是先找出销售额总和最高的2个国家,再取出这两个国家的全部行数据。
修正方案(无NA的情况)
正确的思路应该分两步:先计算每个国家的销售额总和,选出总和排名前2的国家;再从原数据中筛选这些国家的所有行。用dplyr可以这么写:
library(dplyr) df1 <- data.frame( country = c(1, 1, 2, 2, 3), sales = c(1, 1, 1, 2, 3) ) # 第一步:找出销售额总和前2的国家 top_countries <- df1 %>% group_by(country) %>% summarise(total_sales = sum(sales)) %>% slice_max(total_sales, n = 2) %>% pull(country) # 提取top2国家的编号 # 第二步:筛选原数据中属于这些国家的所有行 result <- df1 %>% filter(country %in% top_countries) result
如果你喜欢链式写法,也可以合并成一段,但分两步的逻辑更清晰、可读性更强。
处理包含NA的情况
当sales字段存在NA时,默认的sum(sales)会返回NA,导致对应的国家无法参与排名。这时候需要在求和时加上na.rm = TRUE参数忽略NA值,修改后的代码如下:
df1 <- data.frame( country = c(1, 1, 2, 2, 3), sales = c(1, 1, NA, 4, 3) ) top_countries <- df1 %>% group_by(country) %>% summarise(total_sales = sum(sales, na.rm = TRUE)) %>% # 关键:加入na.rm=TRUE忽略NA slice_max(total_sales, n = 2) %>% pull(country) result <- df1 %>% filter(country %in% top_countries) result
这里country2的销售额总和会被计算为sum(NA,4, na.rm=TRUE)=4,country3为3,country1为2,所以最终top2国家是country2和3,结果会包含这两个国家的所有行(如果想去掉带NA的行,可以在最后再加filter(!is.na(sales)))。
关键总结
- 原代码错误的核心是分组后在组内执行slice_max,而你需要的是在全局范围内选出top国家,再筛选对应行
- 处理NA值时,一定要在
sum()中加入na.rm=TRUE,否则会直接影响排名结果
内容的提问来源于stack exchange,提问作者Przemyslaw Remin
相关产品推荐
相关产品推荐

