Подтвердить что ты не робот

Найти * все * дублированные записи в data.table(не все-но-одно)

Если я правильно понял, duplicated() функция для data.table возвращает логический вектор, который не содержит первого появления дублированной записи. Каков наилучший способ отметить это первое появление? В случае base::duplicated() я решил это просто путем дизъюнкции с обратной функцией порядка: myDups <- (duplicated(x) | duplicated(x, fromLast=TRUE)) - но в data.table::duplicated(), fromLast=TRUE не включен (я не знаю почему)...

P.S. ok, здесь примитивный пример

myDT <- fread(
"id,fB,fC
 1, b1,c1
 2, b2,c2
 3, b1,c1
 4, b3,c3
 5, b1,c1
")
setkeyv(myDT, c('fB', 'fC'))
myDT[, fD:=duplicated(myDT)]

строки 1, 3 и 5 - все дубликаты, но только 3 и 5 будут включены в duplicated, в то время как мне нужно отметить все из них.

UPD. важное уведомление: ответ, который я принял ниже, работает только для таблицы с ключом. Если вы хотите найти повторяющиеся записи, учитывающие все столбцы, вы должны явно указать setkey все эти столбцы. До сих пор я использовал следующее обходное решение для этого случая:

dups1 <- duplicated(myDT);
dups2 <- duplicated(myDT, fromLast=T);
dups <- dups1 | dups2;
4b9b3361

Ответ 1

Как и для версии data.table версии 1.9.8, решение eddi должно быть изменено так:

dups = duplicated(myDT, by = key(myDT));
myDT[, fD := dups | c(tail(dups, -1), FALSE)]

так:

Изменения в v1.9.8 (по CRAN 25 ноября 2016 года)

ПОТЕНЦИАЛЬНО СЪЕМНЫЕ ИЗМЕНЕНИЯ

По умолчанию все столбцы теперь используются уникальными(), duplicated() и uniqueN() data.table, # 1284 и # 1841. Восстановить старые поведение: options (datatable.old.unique.by.key = TRUE). Через 1 год вариант восстановления старого значения по умолчанию будет устаревшим с предупреждением. В Через 2 года этот вариант будет удален. Пожалуйста, явно передайте ключ = (DT) для ясности. Это зависит только от кода, который зависит от значения по умолчанию. 266 Пакеты CRAN и Bioconductor с использованием data.table были проверены ранее выпуск. 9 необходимо было изменить и были уведомлены. Любые строки кода без проверочного покрытия будут пропущены эти проверки. Любые пакеты не на CRAN или Bioconductor не были проверены.

Ответ 2

Много лет назад это был самый быстрый ответ с большим отрывом (см. историю изменений, если это интересно):

dups = duplicated(myDT, by = key(myDT));
myDT[, fD := dups | c(tail(dups, -1), FALSE)]

С тех пор было много внутренних изменений, которые сделали много вариантов примерно в том же порядке:

myDT <- data.table(id = sample(1e6), 
                   fB = sample(seq_len(1e3), size= 1e6, replace=TRUE), 
                   fC = sample(seq_len(1e3), size= 1e6,replace=TRUE ))
setkey(myDT, fB, fC)

microbenchmark(
   key=myDT[, fD := .N > 1, by = key(myDT)],
   unique=myDT[unique(myDT, by = key(myDT)),fD:=.N>1], 
   dup = myDT[,fD := duplicated.data.frame(.SD)|duplicated.data.frame(.SD, fromLast=TRUE),
              .SDcols = key(myDT)],
   dup2 = {dups = duplicated(myDT, by = key(myDT)); myDT[, fD := dups | c(tail(dups, -1L), FALSE)]},
   dup3 = {dups = duplicated(myDT, by = key(myDT)); myDT[, fD := dups | c(dups[-1L], FALSE)]},
   times=10)

#   expr       min        lq      mean    median        uq       max neval
#    key  523.3568  567.5372  632.2379  578.1474  678.4399  886.8199    10
# unique  189.7692  196.0417  215.4985  210.5258  224.4306  290.2597    10
#    dup 4440.8395 4685.1862 4786.6176 4752.8271 4900.4952 5148.3648    10
#   dup2  143.2756  153.3738  236.4034  161.2133  318.1504  419.4082    10
#   dup3  144.1497  150.9244  193.3058  166.9541  178.0061  460.5448    10

Ответ 3

Это работает:

> myDT[unique(myDT),fD:=.N>1]
> myDT
   id  fB fC    fD
1:  1  b1 c1  TRUE
2:  3  b1 c1  TRUE
3:  5  b1 c1  TRUE
4:  2  b2 c2 FALSE
5:  4  b3 c3 FALSE

Благодаря @flodel лучший способ сделать это:

> myDT[, fD := .N > 1, by = key(myDT)]
> myDT
   id  fB fC    fD
1:  1  b1 c1  TRUE
2:  3  b1 c1  TRUE
3:  5  b1 c1  TRUE
4:  2  b2 c2 FALSE
5:  4  b3 c3 FALSE

Разница в эффективности существенна:

> microbenchmark(
    key=myDT[, fD := .N > 1, by = key(myDT)],
    unique=myDT[unique(myDT),fD:=.N>1])
Unit: microseconds
   expr      min       lq    median        uq       max neval
    key  679.874  715.700  735.0575  773.7595  1825.437   100
 unique 1417.845 1485.913 1522.7475 1567.9065 24053.645   100

Специально для макс. Что там происходит?

Ответ 4

Третий подход (который выглядит более эффективным для этого небольшого примера)

Вы можете явно вызвать duplicated.data.frame....

myDT[,fD := duplicated.data.frame(.SD)|duplicated.data.frame(.SD, fromLast=TRUE),
  .SDcols = key(myDT)]


 microbenchmark(
   key=myDT[, fD := .N > 1, by = key(myDT)],
   unique=myDT[unique(myDT),fD:=.N>1], 
  dup = myDT[,fD := duplicated.data.frame(.SD)|duplicated.data.frame(.SD, fromLast=TRUE), 
    .SDcols = key(myDT)])
## Unit: microseconds
##    expr      min        lq   median        uq       max neval
##     key  556.608  575.9265  588.906  600.9795 27713.242   100
##  unique 1112.913 1164.8310 1183.244 1216.9000  2263.557   100
##     dup  420.173  436.3220  448.396  461.3750   699.986   100

Если мы расширим размер образца data.table, то подход key станет явным победителем

 myDT <- data.table(id = sample(1e6), 
  fB = sample(seq_len(1e3), size= 1e6, replace=TRUE), 
  fC = sample(seq_len(1e3), size= 1e6,replace=TRUE ))
setkeyv(myDT, c('fB', 'fC'))

microbenchmark(
  key=myDT[, fD := .N > 1, by = key(myDT)],
  unique=myDT[unique(myDT),fD:=.N>1], 
  dup = myDT[,fD := duplicated.data.frame(.SD)|duplicated.data.frame(.SD, fromLast=TRUE),
   .SDcols = key(myDT)],times=10)
## Unit: milliseconds
##    expr       min        lq    median        uq       max neval
##     key  355.9258  358.1764  360.7628  450.9218  500.8360    10
##  unique  451.3794  458.0258  483.3655  519.3341  553.2515    10
##     dup 1690.1579 1721.5784 1775.5948 1826.0298 1845.4012    10