【问题标题】:Tag all duplicate rows in R as in Stata像在 Stata 中一样标记 R 中的所有重复行
【发布时间】:2014-05-18 13:14:11
【问题描述】:

从我的问题 here 开始,我试图在 R 中复制 Stata 命令 duplicates tag 的功能,它允许我标记数据集中根据给定的一组重复的行变量:

clear *
set obs 16
g f1 = _n
expand 104
bys f1: g f2 = _n
expand 2
bys f1 f2: g f3 = _n
expand 41
bys f1 f2 f3: g f4 = _n
des  // describe the dataset in memory

preserve
sample 10  // draw a 10% random sample
tempfile sampledata
save `sampledata', replace
restore

// append the duplicate rows to the data
append using `sampledata'
sort f1-f4

duplicates tag f1-f4, generate(dupvar)
browse if dupvar == 1  // check that all duplicate rows have been tagged

编辑

这是 Stata 产生的(应@Arun 的要求添加):

f1   f2   f3   f4   dupvar  
 1    1    1    1        0  
 1    1    1    2        0  
 1    1    1    3        1  
 1    1    1    3        1  
 1    1    1    4        0  
 1    1    1    5        0  
 1    1    1    6        0  
 1    1    1    7        0  
 1    1    1    8        1  
 1    1    1    8        1

请注意,(f1, f2, f3, f4) = (1, 1, 1, 3) 有两行,这两行都标记为dupvar = 1。同样,对于 (f1, f2, f3, f4) =(1, 1, 1, 8) 重复的两行。

R:

基本函数duplicated 仅标记第二个重复项。因此,我编写了一个函数来复制 R 中的 Stata 功能,使用 ddply

# Values of (f1, f2, f3, f4) uniquely identify observations
dfUnique = expand.grid(f1 = factor(1:16),
            f2 = factor(1:41),
            f3 = factor(1:2),
            f4 = factor(1:104))

# sample some extra rows and rbind them
dfDup = rbind(dfUnique, dfUnique[sample(1:nrow(dfUnique), 100), ])

# dummy data 
dfDup$data = rnorm(nrow(dfDup))

# function: use ddply to tag all duplicate rows in the data
fnDupTag = function(dfX, indexVars) {
  dfDupTag = ddply(dfX, .variables = indexVars, .fun = function(x) {
    if(nrow(x) > 1) x$dup = 1 else x$dup = 0
    return(x)
  })
  return(dfDupTag)
}

# test the function
indexVars = paste0('f', 1:4, sep = '')
dfTemp = fnDupTag(dfDup, indexVars)

但正如在链接的问题中一样,性能是一个大问题。 Another possible solution

dfDup$dup = duplicated(dfDup[, indexVars]) | 
  duplicated(dfDup[, indexVars], fromLast = TRUE) 
dfDupSorted = with(dfDup, dfDup[order(eval(parse(text = indexVars))), ])

我有几个问题:
1. 是否可以让ddply版本更快?
2.第二个版本使用duplicated是否正确?对于重复行的两个以上副本? 3. 我将如何使用data.table 做到这一点?那会更快吗?

【问题讨论】:

  • @Arun 我不想采取任何行动 -- 我想创建一个指标来识别由一组变量标识的同质组。
  • @Arun 已添加示例输出。
  • 你可以通过ave(1:nrow(dd), dd[, 1:4], FUN = function(x) length(x) > 1)获得dupvar

标签: r data.table plyr stata


【解决方案1】:

我会在这里回答你的第三个问题..(我认为你的other post 或多或少回答了第一个问题)。

## Assuming DT is your data.table
DT[, dupvar := 1L*(.N > 1L), by=c(indexVars)]

:= 添加一个新列dupvar 通过引用(因此非常快,因为没有复制)。 .Ndata.table 中的一个特殊变量,它提供了属于每个组的观察数(这里是每个 f1,f2,f3,f4)。

花点时间浏览?data.table(并在那里运行示例)以了解用法。以后会为你节省很多时间。

所以,基本上,我们按indexVars 分组,检查是否为.N > 1L,如果是,则返回TRUE。我们乘以 1L 以返回 integer 而不是 logical 值。

如果需要,您还可以使用setkey 按列对其进行排序。


从下一个版本开始(目前在 v1.9.3 - 开发版中实现),还有一个函数 setorder 被导出,只是data.table 进行排序 通过引用 em>,无需设置键。它也可以按升序或降序排序。 (请注意,setkey 总是仅按升序排序)。

也就是说,在下一个版本中你可以这样做:

setorder(DT, f1, f2, f3, f4)
## or equivalently
setorderv(DT, c("f1", "f2", "f3", "f4"))

此外,DT[order(...)] 的用法也在内部进行了优化,以使用data.table 的快速排序。也就是说,在内部检测到DT[order(...)] 并更改为DT[forder(DT, ...)],这比base 的order 快得多。因此,如果您不想通过引用来更改它,并且想将排序后的 data.table 分配给另一个变量,您可以这样做:

DT_sorted <- DT[order(f1, f2, f3, f4)] ## internally optimised for speed
                                       ## but still copies!

HTH

【讨论】:

  • Stata 命令duplicates tag 实际上会报告重复的数量,因此正确的解决方案更简单DT[, dupvar := .N - 1L, by=c(indexVars)]。还有this discussion
【解决方案2】:

对于您的三个问题,我真的没有答案,但我可以为您节省一些时间。我还在 Stata 和 R 之间分配时间,并且经常错过 Stata 的 duplicates 命令。但是如果你subset 然后mergeall=TRUE,那么你可以节省很多时间。

这是一个例子。

# my more Stata-ish approach
system.time({
    dupes <- dfDup[duplicated(dfDup[, 1:4]), 1:4]
    dupes$dup <- 1
    dfTemp2 <- merge(dfDup, dupes, all=TRUE)
    dfTemp2$dup <- ifelse(is.na(dfTemp2$dup), 0, dfTemp2$dup)
})

这要快一些。

> system.time({
+ fnDupTag = function(dfX, indexVars) {
+   dfDupTag = ddply(dfX, .variables = indexVars, .fun = function(x) {
+     if(nrow(x) > 1) x .... [TRUNCATED] 
   user  system elapsed 
 118.75    0.22  120.11 

> # my more Stata-ish approach
> system.time({
+     dupes <- dfDup[duplicated(dfDup[, 1:4]), 1:4]
+     dupes$dup <- 1
+     dfTemp2 <- merge(dfDup,  .... [TRUNCATED] 
   user  system elapsed 
   0.63    0.00    0.63 

结果相同(取决于all.equal 的精度)。

> # compare
> dfTemp <- dfTemp[with(dfTemp, order(f1, f2, f3, f4, data)), ]

> dfTemp2 <- dfTemp2[with(dfTemp2, order(f1, f2, f3, f4, data)), ]
> all.equal(dfTemp, dfTemp2)
[1] "Attributes: < Component 2: Mean relative difference: 1.529748e-05 >"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-31
    • 2020-04-22
    • 2020-04-08
    • 2021-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多