Как вернуть уникальные элементы между векторами, сохраняя исходный вектор этих уникальных элементов?

#r #list #vector #unique #data-mining

#r #Список #вектор #уникальный #интеллектуальный анализ данных

Вопрос:

Например, у меня есть 5 векторов в списке:

 A <- c(1,2,3,4,5)

B <- c(1,2,3,4,5,6)

C <- c(5,6,7,8,9)

D <- c(8,9)
  

На самом деле у меня есть 100 таких векторов, но я дал только 5 векторов для воспроизводимости.
Моя цель -:

  1. Определите уникальные элементы, исходящие из векторов. Например, vector A не должен ничего возвращать, потому что все его элементы являются частью vector B , однако vector B вносит дополнительный уникальный элемент, и это так 6 . Vector C должен дать мне 7,8,9 , поскольку c(5,6) они уже были включены vector B . Vector D ничего не должно возвращать, потому что все его элементы являются частью C
  2. распознать, какой элемент является уникальным из какого вектора
  3. Найдите, какие векторы являются подмножествами других больших векторов. Например, vector D является подмножеством C и vector A является подмножеством vector B .

До сих пор единственным решением, которое я нашел, было:

 Reduce(setdiff, list("my_vectors"))
  

Но это не позволяет мне распознать, какой элемент уникален из какого вектора. Например, Reduce(setdiff, list(A,B)) вернул 6 бы, но я бы понятия не имел, откуда 6 взялся ( A или B )?

Моя трудность заключается в том, что это крупномасштабная проблема, у меня нет только 5 векторов, у меня их 100, поэтому я не могу найти устойчивое решение. Любые советы приветствуются.

Редактировать: мои векторы находятся в списке

Комментарии:

1. Являются ли векторы в списке .GlobalEnv или в списке?

2. Они находятся в списке. Итак, у меня есть список векторов.

3. В вашем примере нет эксклюзивных шифров.

4. Это не совсем точно определено: например, A не должен возвращать ничего, потому что B содержит все элементы в A, но B должен возвращать 6. Однако 6 также содержится в C. Итак, здесь единственным по-настоящему уникальным элементом, который у вас есть, является 7 в C, я прав?

5. Точно. Это довольно неоднозначно.

Ответ №1:

Предположим, что ваши данные хранятся следующим образом:

 my_vectors <- list(
  A = c(1,2,3,4,5),
  B = c(1,2,3,4,5,6),
  C = c(5,6,7,8,9),
  D = c(8,9)
)
  

Если вы используете accumulate = TRUE вызов Reduce to , вы также получаете все промежуточные результаты. Мы можем использовать это вместе с union для пошагового создания общего набора (обратите внимание, что я установил init = c() , чтобы убедиться, что мы начинаем с пустого):

 acc <- Reduce(union, my_vectors, init = c(), accumulate = T)
  

Затем мы можем взять значение setdiff каждого элемента с помощью этого созданного списка.

 lapply(1:length(my_vectors), function(i) setdiff(my_vectors[[i]], acc[[i]]))
  

Это дает

 [[1]]
[1] 1 2 3 4 5

[[2]]
[1] 6

[[3]]
[1] 7 8 9

[[4]]
numeric(0)
  

Вы можете применить имена my_vectors позже, если хотите.

Ответ №2:

Первым наивным подходом был бы цикл for, просто чтобы иметь рабочее решение. Функция возвращает список с ненужными элементами и фреймом данных, описывающим, из какого вектора в векторном списке исходят уникальные элементы (первое появление).

 A <- c(1,2,3,4,5)
B <- c(1,2,3,4,5,6)
C <- c(5,6,7,8,9)
D <- c(8,9)

vectorList <- list(A,B,C,D)

ff <- function(vectorList) {
  uniques <- unique(vectorList[[1]])
  comingFromDf <- data.frame(values=uniques)
  comingFromDf$source <- 1
  
  for(k in 2:length(vectorList)) {
    vec <- vectorList[[k]]
    newUniques <- vec[!(vec %in% uniques)]
    if(length(newUniques)) {
      newUniques <- unique(newUniques)
      toAdd <- data.frame(values=newUniques)
      toAdd$source <- k
      comingFromDf <- rbind(comingFromDf,toAdd)
      uniques <- c(uniques,newUniques)
    }
  }
  
  list(uniqueElements = uniques,
       comingFromInfo = comingFromDf)
}

ff(vectorList)
  

Я не знаю, насколько эффективной вам нужна функция, но даже с 200 векторами длиной 1000 она, похоже, быстро завершается (я не знаю о ваших размерах):

 bigVectorList <- lapply(1:200, function(k) {
  sample(1:1e6,1000)
})

microbenchmark::microbenchmark(ff(bigVectorList),times=10)
#Unit: milliseconds
#              expr      min       lq     mean   median      uq      max neval
#ff(bigVectorList) 619.5148 624.8351 639.7535 633.2326 647.118 685.0387    10
  

На моей машине это заняло чуть больше половины секунды, может быть, для вас этого достаточно. Поскольку функция включает только векторы и фрейм данных, было бы довольно легко повторно реализовать ее на C и с использованием Rcpp. Это должно быть намного быстрее, чем реализация цикла for в R. Кроме того, вы можете рассмотреть возможность использования accumulate аргумента -в Reduce -функции для сохранения промежуточных результатов вычисления.

Ответ №3:

Вот tidyverse решение.

lag(accumulate(l, union)) отслеживает все элементы, которые были замечены до сих пор. Разница между этим и исходным списком приводит к появлению новых элементов.

 library(tidyverse)

l <- lst(A, B, C, D)

map2(l, lag(accumulate(l, union)), setdiff)
#> $A
#> [1] 1 2 3 4 5
#> 
#> $B
#> [1] 6
#> 
#> $C
#> [1] 7 8 9
#> 
#> $D
#> numeric(0)
  

Вот ответ на ваш другой вопрос о том, какие векторы являются подмножествами других больших векторов.

expand_grid получит все комбинации векторов. Отфильтруйте это, чтобы найти, какой вектор является подмножеством любого другого вектора.

 l %>%
  enframe() %>%
  expand_grid(a = ., b = .) %>%
  filter(
    a$name != b$name,
    map2_lgl(a$value, b$value, ~all(.x %in% .y))
  ) %>%
  transmute(this_vector = a$name, is_a_subset_of_this_vector = b$name)
#> # A tibble: 2 x 2
#>   this_vector is_a_subset_of_this_vector
#>   <chr>       <chr>                     
#> 1 A           B                         
#> 2 D           C 
  

Ответ №4:

Здесь у вас есть только один действительно уникальный элемент, который находится 7 внутри C . Ниже будут возвращены уникальные элементы, а также их членство

 mylist <- list("A"=A,"B"=B,"C"=C,"D"=D) #better for 100's of vectors
myres <- !unlist(lapply(1:length(mylist), function(x) unlist(mylist[x]) %in% unlist(mylist[-x])))
result <- as.numeric(unlist(mylist)[myres])
member <- sapply(mylist, function(x) result %in% x)
membername <- names(mylist[member])
result
membername
> result
 7 
> membername
[1] "C"