删除重复项,保留出现最频繁的行

4

我想要对数据进行去重,保留出现最频繁的行。如果存在平局,则返回任意一行即可,按照字母或数字顺序排列。我希望按照idvar分组进行处理。

最小可重现示例:

df <- data.frame(
  id = rep("a", 8),
  var = c(rep("b", 4), rep("c", 4)),
  val = c("d", "d", "d", "e", "f", "f", "g", "g")
)

> df
  id var val
1  a   b   d
2  a   b   d
3  a   b   d
4  a   b   e
5  a   c   f
6  a   c   f
7  a   c   g
8  a   c   g

Should be:

  id var val
1  a   b   d
2  a   c   f

我正在处理大数据集和 tidyverse 管道链,因此最好使用 dplyr 解决方案。


a-b-d不是最频繁的行吗?为什么还返回a-c-f呢? - csgroen
我想按id和var分组。我打算采用这里提供的解决方案,并将其放在分组之后。现在正在走路回家,如果到那时还没有其他人回答,我会更新问题。 - Mark White
哦,我看懂了。我想@Psidom提供的解决方案应该可以解决问题。 - csgroen
1
@MKR 正确 - 我没有偏好,只想选择最频繁的一个。也许可以提出有力的论据,应该在 a c fa c g 之间随机决定。 - Mark White
1
@MKR 假设有一种情况是平局,我们希望在这种情况下随机选择一行。我们可以使用 slice(sample(1:n(), 1) 来代替 slice(1),对吗? - Mark White
显示剩余5条评论
6个回答

3
使用tablewhich.max来提取mode
df %>% 
    group_by(id, var) %>% 
    summarise(val = {t <- table(val); names(t)[which.max(t)] })

# A tibble: 2 x 3
# Groups:   id [?]
#  id    var   val  
#  <fct> <fct> <chr>
#1 a     b     d    
#2 a     c     f  

另一种在基础R中实现此操作的方法:直接创建三维列联表,然后沿第三个轴找到最大列:
apply(table(df), c(1, 2), function(v) names(v)[which.max(v)])

#   var
#id  b   c  
#  a "d" "f"

将这个转换成数据框:

as.data.frame.table(
    apply(table(df), c(1, 2), function(v) names(v)[which.max(v)])
)

#  id var Freq
#1  a   b    d
#2  a   c    f

在我的现有用例中,被接受为最佳组合的是(a)简洁易读和(b)在进行“microbenchmark”时最快的。 - Mark White

2
我怀疑这并不会更快,但另一种选择是:
df %>% 
    group_by(id, var) %>% 
    filter(row_number() == rle(as.character(val))$lengths %>% 
                                {sum(.[1:which.max(.)])})

2

使用dplyr

library(dplyr)

df %>% 
  group_by(id, var, val) %>% 
  summarise(n = n()) %>% 
  group_by(id, var) %>% 
  arrange(-n) %>% 
  slice(1) %>% 
  ungroup() %>% 
  select(-n)

# # A tibble: 2 x 3
#   id    var   val  
#   <fct> <fct> <fct>
# 1 a     b     d    
# 2 a     c     f   

2

一种选项是使用tablemax,如下所示:

library(dplyr)
df %>% group_by(id, var) %>%
  filter(table(val) == max(table(val))) %>%
  slice(1)
# # A tibble: 2 x 3
# # Groups: id, var [2]
# id     var    val   
# <fctr> <fctr> <fctr>
# 1 a      b      d     
# 2 a      c      g    

注意: 在平局的情况下,按照 a c g 的顺序进行排序。根据原始帖子(OP)的要求,在平局的情况下可以返回任何记录。


1
这是我的尝试:

library(dplyr)
df %>%
  group_by(id,var,val) %>%
  mutate(n=n()) %>%
  arrange(desc(n)) %>%
  group_by(id,var) %>%
  filter(row_number()==1) %>%
  select(-n)

`


你缺少了“最频繁”的部分。如果第四行是第一行,那么你的输出将会是 a b e - zx8754
@zx8754 是的,误解了问题并更新了版本。这应该可以工作。 - Lerong

1
一种使用count函数的dplyr解决方案:

library(dplyr)

df %>%
  count(id,var,val,sort = T) %>%
  group_by(id,var) %>%
  summarize_at("val",head,1)

# # A tibble: 2 x 3
#       id    var    val
#   <fctr> <fctr> <fctr>
# 1      a      b      d
# 2      a      c      f

或许更习惯用语但较长:

df %>%
  count(id,var,val,sort = T) %>%
  group_by(id,var) %>%
  slice(1) %>%
  select(-n) %>%
  ungroup

或者使用tally,使用稍微不同的语法输出相同的结果:

df %>%
  group_by(id,var,val) %>%
  tally(sort = T) %>%
  slice(1) %>%
  select(-n) %>%
  ungroup

一个基本解决方案:

df2 <- aggregate(x ~ .,cbind(df,x=1),sum)
aggregate(val ~ id+var, df2[order(-df2$x),],head,1)
#   id var val
# 1  a   b   d
# 2  a   c   f

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接