我有一个数据框架,看起来像这样:
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
的重复值,用0
或NA
替换任何重复值,但留下行&列完整(即不删除任何)。这里的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
中的值出现的顺序,所以如果它们被移动也没关系。是否有一种有效的方法来做到这一点,最好是用某种整洁的方式?我知道我可以更长的枢轴并删除重复的行,但我的数据集非常大,我正在寻找一种不需要枢轴的方法。
Base R way usingapply
:
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()
这个解决方案并不完美,但希望足够简单。
duplicated()
函数做你想做的。您可以使用apply()
函数按行向duplicated()
提供您的数据。
dat[t(apply(dat, MARGIN = 1, duplicated))] <- 0