如何根据ID将R数据帧的特定列的重复值替换为NA(第一行除外)。举个例子:
x <- data.frame(id=c("p1","p1","p1","p2","p2"),date=c("d1","d1","d1","d2","d2"))
并且应该导致以下结果:
x2 <- data.frame(id=c("p1","p1","p1","p2","p2"),date=c("d1","NA","NA","d2","NA"))
我必须维护每个id多行的数据结构,只是不希望重复日期值而是一次。
由于
答案 0 :(得分:4)
data.table
方法:
library(data.table)
x3<-data.table(x)
x3[, `:=` (date = ifelse(duplicated(date), NA, date)), by = id]
x3
id date
p1 d1
p1 NA
p1 NA
p2 d2
p2 NA
一般情况下,建议谨慎使用:=
,因为您所做的调整是永久性的。但是,在这种情况下,这就是我们所追求的,并使用<-
来保存,以根据需要定义/重新定义列。有关详细信息,请参阅?data.table
。
答案 1 :(得分:3)
选项1:基本R方法是使用ave()
为date
中的每个组替换重复的NA
值和id
值。
x$date <- ave(
x$date,
x$id,
FUN = function(a) replace(a, duplicated(a), NA_integer_)
)
提供更新的x
数据
id date 1 p1 d1 2 p1 <NA> 3 p1 <NA> 4 p2 d2 5 p2 <NA>
上述方法适用于date
中的多个值,用NA
替换重复项。如果它只是你所追求的第一组值,你可以使用上面或下面的代码,这可能会更快。
ave(
x$date,
x$id,
FUN = function(a) c(a[1], a[-1][NA])
)
此代码获取每个组中的第一个值,并用NA
替换所有其余值。目前尚不清楚您想要哪一个,因为您的示例数据每个id
组只有一个值。
选项2:使用 data.table 包的替代方案。由于NA
是合乎逻辑的,date[NA]
只需将值转换为NA
而不更改数据类型。
library(data.table)
setDT(x)[duplicated(date), date := date[NA], by = id]
给出了
id date 1: p1 d1 2: p1 NA 3: p1 NA 4: p2 d2 5: p2 NA
答案 2 :(得分:2)
<强> BENCHMARK 强>
library(data.table)
library(microbenchmark)
dff <- data.frame(id=c("p1","p1","p1","p2","p2"),date=c("d1","d1","d1","d2","d2"))
func_Bryan.Goggin <- function(x){x3<-data.table(x);x3[, `:=` (date = ifelse(duplicated(date), NA, date)), by = id];}
func_Richard.Scriven <- function(x){x$date <- ave(x$date, x$id, FUN = function(a) replace(a, duplicated(a), NA_integer_));}
func_r2evans <- function(x){groupedx <- by(x, x$date, function(df) {within(df, date <- c(as.character(date[1]), rep(NA, nrow(df) - 1)))});Reduce(rbind, groupedx);}
microbenchmark(func_Bryan.Goggin(dff), func_Richard.Scriven(dff), func_r2evans(dff))
Unit: microseconds
expr min lq mean median uq max neval
func_Bryan.Goggin(dff) 791.436 816.827 886.0153 848.9770 880.9765 1733.408 100
func_Richard.Scriven(dff) 130.103 146.630 157.8821 154.1410 164.3570 305.277 100
func_r2evans(dff) 590.423 615.662 668.7100 637.8975 656.5260 1607.511 100
修改强>
我将func_Richard.Scriven2
排除在基准之外,因为它将通过引用进行调用。
答案 3 :(得分:1)
这有效:
x <- data.frame(id=c("p1","p1","p1","p2","p2"),
date=c("d1","d1","d1","d2","d2"))
groupedx <- by(x, x$date, function(df) {
within(df, date <- c(as.character(date[1]), rep(NA, nrow(df) - 1)))
})
Reduce(rbind, groupedx)
# id date
# 1 p1 d1
# 2 p1 <NA>
# 3 p1 <NA>
# 4 p2 d2
# 5 p2 <NA>
(我使用as.character
是因为你在data.frame
中使用了因子,没有它就把字符串转换成它们的因子整数。如果你使用实际的字符串,你应该可以省略它。)