4

我有一个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      

谢谢

4

2 回答 2

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
    }
}

这是上述两种方法以及 AnandaMahtoreshape2data.table更大数据方法的基准。for-loop与 righti和indices 一起使用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
}

Ananda 的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))
}
于 2013-04-28T12:44:23.513 回答
1

这种解决方案可能效率不高,但这主要是因为在宽、长和宽之间来回转换数据。但是,您可能会发现以“长”形式处理数据更容易。

首先,生成第二个 ID,因为您的 ID 跨越多行。

mydf$ID2 <- with(mydf, ave(ID, ID, FUN = seq_along))

其次,使用melt“reshape2”包将您的数据变成长格式。

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
于 2013-04-28T16:19:13.197 回答