我有data.frame
ID code1 code2 code3
A 143 143 144
A 35 453 35
A 35 15
B 46 46 45
B 12 43 765
C 255 455 344
C 343 343 343
C 343 23 23
每个代码一次出现
可以重复id。真正的data.frame非常大
ID code1 code2 code3
A 143 144
A 35 453
A 35 15
B 46 45
B 12 43 765
C 255 455 344
C 343
C 343 23
感谢
答案 0 :(得分:4)
这个怎么样?
df[, 2:4][t(apply(df[,2:4], 1, duplicated))] <- NA
修改:很多更快的基础解决方案:
for (i in 2:(ncol(df)-1)) {
for (j in (i+1):ncol(df)) {
chk <- df[[i]] == df[[j]]
df[[j]][chk] <- NA
}
}
这是上述两种方法的基准,以及AnandaMahto对更大数据的reshape2
和data.table
方法。使用带有for-loop
和i
索引的j
似乎是最快的。
require(microbenchmark)
microbenchmark(ar.f <- arun.f(df), ar.s <- arun.s(df),
an.f <- ananda.ave(df),
an.s <- ananda.dt(copy(DT)), times=10)
# Unit: milliseconds
# expr min lq median uq max neval
# arun.f(df) 4816.3937 5197.0626 6402.454 6955.9380 7534.6912 10
# arun.s(df) 114.8372 118.7971 149.284 202.6081 297.4787 10
# ananda.ave(df) 2877.7936 3288.5935 3650.660 3985.5390 4111.9064 10
# ananda.dt(copy(DT)) 3383.1229 3861.6379 4432.751 4776.6108 5368.6504 10
set.seed(1234)
df <- cbind(data.frame(ID = rep(letters[1:20], each=1e4)), stringsAsFactors=FALSE),
matrix(sample(1:10, 6 * 1e5, replace=TRUE), ncol=3))
names(df)[2:4] <- paste0("code", 1:3)
arun.f <- function(df) {
df[, 2:4][t(apply(df[,2:4], 1, duplicated))] <- NA
df
}
arun.s <- function(df) {
for (i in 2:(ncol(df)-1)) {
for (j in (i+1):ncol(df)) {
chk <- df[[i]] == df[[j]]
df[[j]][chk] <- NA
}
}
df
}
ave
+ reshape2
解决方案:library(reshape2)
ananda.ave <- function(df) {
df$ID2 <- with(df, ave(ID, ID, FUN = seq_along))
m.df <- melt(df, id.vars=c("ID", "ID2"))
m.df[duplicated(m.df[setdiff(names(m.df), "variable")]), "value"] <- NA
dcast(m.df, ID + ID2 ~ variable)
}
data.table
解决方案:(修改了一点以进行更优化)
library(data.table)
DT <- data.table(df)
ananda.dt <- function(dt) {
temp <- dt[, list(ID2 = 1:.N, Value = unlist(.SD, use.names=FALSE)), by ="ID"]
temp[duplicated(temp), Value := NA]
out <- setnames(temp[, as.list(Value), by=list(ID, ID2)], 3:5, paste0("code", 1:3))
}
答案 1 :(得分:1)
这种解决方案可能效率低下,但这主要是因为在宽,长和宽之间来回转换数据。但是,您可能会发现以“长”形式处理数据更容易。
首先,生成第二个ID,因为您有跨越多行的ID。
mydf$ID2 <- with(mydf, ave(ID, ID, FUN = seq_along))
其次,使用“reshape2”包中的melt
将数据转换为长格式。
library(reshape2)
m.df <- melt(mydf, id.vars=c("ID", "ID2"))
使用长格式数据,可以更轻松地识别重复项并将其替换为NA
。
m.df[duplicated(m.df[setdiff(names(m.df), "variable")]), "value"] <- NA
如果您对长形式的数据感到满意。停在那里。如果您想将其恢复为宽屏,请使用dcast
(再次来自“reshape2”)。
dcast(m.df, ID + ID2 ~ variable)
# ID ID2 code1 code2 code3
# 1 A 1 143 NA 144
# 2 A 2 35 453 NA
# 3 A 3 NA 35 15
# 4 B 1 46 NA 45
# 5 B 2 12 43 765
# 6 C 1 255 455 344
# 7 C 2 343 NA NA
# 8 C 3 343 23 NA
作为参考,这在基础R中也是可行的,但语法更笨拙(即使它的表现可能比“reshape2”更好)。
mydf$ID2 <- with(mydf, ave(ID, ID, FUN = seq_along))
m.df <- cbind(mydf[c("ID", "ID2")],
stack(mydf[setdiff(names(mydf), c("ID", "ID2"))]))
m.df[duplicated(m.df[setdiff(names(m.df), "ind")]), "values"] <- NA
cbind(mydf[c("ID", "ID2")], unstack(m.df, values ~ ind))
data.table
解决方案您可能想要探索data.table
,因为您提到数据很大。这是一种可能的解决方案(尽管@Arun可能有更直接的解决方案)。
library(data.table)
DT <- data.table(mydf)
## Creates your long data.table
temp <- DT[, list(ID2 = 1:.N, Value = unlist(.SD)), by ="ID"]
## Changes duplicates to NA and adds in the "Code" column
temp[duplicated(temp), Value := NA][, Variable := rep(names(DT)[-1],
each = nrow(DT))]
## "Reshapes" the data from long to wide
temp[, as.list(setattr(Value, 'names', Variable)), by=list(ID, ID2)]
# ID ID2 code1 code1 code1
# 1: A 1 143 NA 144
# 2: A 2 35 453 NA
# 3: A 3 NA 35 15
# 4: B 1 46 NA 45
# 5: B 2 12 43 765
# 6: C 1 255 455 344
# 7: C 2 343 NA NA
# 8: C 3 343 23 NA