Я хочу удалить все элементы, которые встречаются в векторе более одного раза. В частности, сюда входят символьные, числовые и целочисленные векторы. В настоящее время я использую duplicated() как вперед, так и назад (используя параметр fromLast).

Есть ли более эффективный с вычислительной точки зрения (более быстрый) способ выполнить это в R? Приведенное ниже решение достаточно просто для записи / чтения, но кажется неэффективным выполнять двойной поиск дважды. Может быть, лучше использовать метод подсчета с использованием дополнительной структуры данных?

Примере:

d <- c(1,2,3,4,1,5,6,4,2,1)
d[!(duplicated(d) | duplicated(d, fromLast=TRUE))]
#[1] 3 5 6

Связанные сообщения SO здесь и здесь.

11
Megatron 10 Май 2016 в 23:40

3 ответа

Лучший ответ

Некоторые сроки:

set.seed(1001)
d <- sample(1:100000, 100000, replace=T)
d <- c(d, sample(d, 20000, replace=T))  # ensure many duplicates
mb <- microbenchmark::microbenchmark(
  d[!(duplicated(d) | duplicated(d, fromLast=TRUE))],
  setdiff(d, d[duplicated(d)]),
  {tmp <- rle(sort(d)); tmp$values[tmp$lengths == 1]},
  as.integer(names(table(d)[table(d)==1])),
  d[!(duplicated.default(d) | duplicated.default(d, fromLast=TRUE))],
  d[!(d %in% d[duplicated(d)])],
  { ud = unique(d); ud[tabulate(match(d, ud)) == 1L] },
  d[!(.Internal(duplicated(d, F, F, NA)) | .Internal(duplicated(d, F, T, NA)))]
)
summary(mb)[, c(1, 4)]  # in milliseconds
#                                                                                expr      mean
#1                               d[!(duplicated(d) | duplicated(d, fromLast = TRUE))]  18.34692
#2                                                       setdiff(d, d[duplicated(d)])  24.84984
#3                       {     tmp <- rle(sort(d))     tmp$values[tmp$lengths == 1] }   9.53831
#4                                         as.integer(names(table(d)[table(d) == 1])) 255.76300
#5               d[!(duplicated.default(d) | duplicated.default(d, fromLast = TRUE))]  18.35360
#6                                                      d[!(d %in% d[duplicated(d)])]  24.01009
#7                        {     ud = unique(d)     ud[tabulate(match(d, ud)) == 1L] }  32.10166
#8 d[!(.Internal(duplicated(d, F, F, NA)) | .Internal(duplicated(d,      F, T, NA)))]  18.33475

Учитывая комментарии, давайте посмотрим, все ли они верны?

 results <- list(d[!(duplicated(d) | duplicated(d, fromLast=TRUE))],
         setdiff(d, d[duplicated(d)]),
         {tmp <- rle(sort(d)); tmp$values[tmp$lengths == 1]},
         as.integer(names(table(d)[table(d)==1])),
         d[!(duplicated.default(d) | duplicated.default(d, fromLast=TRUE))],
         d[!(d %in% d[duplicated(d)])],
         { ud = unique(d); ud[tabulate(match(d, ud)) == 1L] },
         d[!(.Internal(duplicated(d, F, F, NA)) | .Internal(duplicated(d, F, T, NA)))])
 all(sapply(ls, all.equal, c(3, 5, 6)))
 # TRUE
12
Megatron 10 Май 2016 в 21:54

Вы можете сделать это с помощью функции rle:

tmp <- rle(sort(d))
res <- tmp$values[tmp$lengths == 1]

Идея состоит в том, чтобы найти количество одинаковых значений в векторе.

Здесь есть множество альтернатив: Подсчет количества элементов со значениями x в векторе

Изменить

Посмотрев тесты, @NBATrends у меня возникли подозрения. Теоретически подсчет элементов за один проход должен быть примерно в 2 раза быстрее по сравнению с исходной логикой duplicated.

Я пробовал сделать это с помощью data.table:

library(data.table)
dt <- data.table(d)
res <-  dt[, count:= .N, by = d][count == 1]$d

И вот тесты для разных размеров выборки для трех решений (я свел их к быстрым уникальным подходам):

benchmarking

Видно, что с ростом выборки data.table начинает превосходить другие методы (в 2 раза).

Вот код для воспроизведения:

set.seed(1001)
N <- c(3, 4, 5, 6 ,7)
n <- 10^N
res <- lapply(n, function(x) {
d <- sample(1:x/10, 5 * x, replace=T)
d <- c(d, sample(d, x, replace=T))  # ensure many duplicates
dt <- data.table(d)
mb <- microbenchmark::microbenchmark(
  "duplicated(original)" = d[!(duplicated(d) | duplicated(d, fromLast=TRUE))],
  "tabulate" = { ud = unique(d); ud[tabulate(match(d, ud)) == 1L] },
  "data.table" = dt[, count:= .N, by = d][count == 1]$d,
  times = 1,unit = "ms")
sm <- summary(mb)[, c(1, 4, 8)]
sm$size = x
return(sm)

})

res <- do.call("rbind", res)

require(ggplot2)
##The values Year, Value, School_ID are
##inherited by the geoms
ggplot(res, aes(x = res$size, y = res$mean, colour=res$exp)) + 
geom_line() + scale_x_log10() + scale_y_log10() +
geom_point() 
6
Community 23 Май 2017 в 12:31

Вы можете использовать операцию установки:

d <- c(1,2,3,4,1,5,6,4,2,1)
duplicates = d[duplicated(d)]
setdiff(d, duplicates)
[1] 3 5 6

(Не уверен, что это более эффективно, чем приведенный выше код, но он кажется концептуально более чистым)

0
djq 10 Май 2016 в 21:10