删除跨几列的重复值但保留行

时间:2021-02-17 01:09:31

标签: r dplyr duplicates

我有一个如下所示的数据框:

dat <- data.frame(id=1:6,
                  z_1=c(100,290,38,129,0,290),
                  z_2=c(20,0,0,0,0,290),
                  z_3=c(0,0,38,0,0,98),
                  z_4=c(0,0,38,127,38,78),
                  z_5=c(23,0,25,0,0,98),
                  z_6=c(100,0,25,127,0,9))

dat

  id z_1 z_2 z_3 z_4 z_5 z_6
1  1 100 20  0   0   23  100
2  2 290  0  0   0   0   0
3  3  38  0  38  38  25  25
4  4 129  0  0   127 0   127
5  5   0  0  0   38  0   0
6  6 290 290 98  78  98  9

我想在每一行中删除 z_x 的重复值,用 0NA 替换任何重复值,但保持行和列完好无损(即不删除任何) .这里的 0 不算作重复,它们是缺失值。列中的重复值是可以的。我的理想输出如下所示:

   id z_1 z_2 z_3 z_4 z_5 z_6
1  1  100 20  0   0   23  0
2  2  290 0   0   0   0   0
3  3  38  0   0   0   25  0
4  4  129 0   0   127 0   0
5  5   0  0   0   38  0   0
6  6  290 0   98  78  0   9

我并不真正关心 z_x 中的值出现的顺序,所以如果它们四处移动也无妨。有没有一种有效的方法可以做到这一点,最好以某种整洁的方式?我知道我可以旋转更长的时间并删除重复的行,但我的数据集非常大,我正在寻找一种无需旋转的方法。

2 个答案:

答案 0 :(得分:5)

使用 apply 的基本 R 方式:

cols <- grep('z_\\d+', names(dat))
dat[cols] <- t(apply(dat[cols], 1, function(x)  replace(x, duplicated(x), 0)))

#  id z_1 z_2 z_3 z_4 z_5 z_6
#1  1 100  20   0   0  23   0
#2  2 290   0   0   0   0   0
#3  3  38   0   0   0  25   0
#4  4 129   0   0 127   0   0
#5  5   0   0   0  38   0   0
#6  6 290   0  98  78   0   9

tidyverse 无需重塑的方式可以使用 pmap 完成:

library(tidyverse)

dat %>%
  mutate(result = pmap(select(., matches('z_\\d+')), ~{
    x <- c(...)
    replace(x, duplicated(x), 0)
    })) %>%
  select(id, result) %>%
  unnest_wider(result)

由于@thelatemail 执行的测试表明,与按行处理数据相比,重塑是更好的选择,您可能需要考虑它。

dat %>%
  pivot_longer(cols = matches('z_\\d+')) %>%
  group_by(id) %>%
  mutate(value = replace(value, duplicated(value), 0)) %>%
  pivot_wider()

答案 1 :(得分:0)

这个解决方案不是 tidyverse,但希望足够简单。

duplicated() 函数执行您想要的操作。您可以使用 apply() 函数按行提供 duplicated() 您的数据。

dat[t(apply(dat, MARGIN = 1, duplicated))] <- 0
相关问题