R - Как преобразовать это вложенное для l oop в функцию lapply, которая может изменить список - PullRequest
3 голосов
/ 21 марта 2020

У меня есть данные, которые выглядят так:

aList <- list(a1 = c("apple", "banana", "orange", "strawberry", "cherry"),
              a2 = c("banana", "cherry", "apple"),
              a3 = c("apple", "strawberry", "pineapple"),
              a4 = c("raspberry", "strawberry", "apple"),
              a5 = c("pineapple", "lemon", "orange", "banana", "apple"),
              a6 = c("lemon", "apple", "blueberry"),
              a7 = c("watermelon", "apple", "banana", "mango"),
              a8 = c("mango", "cherry", "apple", "lemon"),
              a9 = c("orange", "banana", "strawberry"),
              a10 = c("mango", "strawberry"))

Я бы хотел получить их в вертикальном формате, например, что происходит при запуске этого кода:

vertical_data <- list()
for (x in names(aList)) {
  for (y in aList[[x]]) {
    if (is.null(vertical_data[[y]])) {
      vertical_data[[y]] <- x
    } else {
      vertical_data[[y]] <- c(x, vertical_data[[y]])
    }
  }
}
vertical_data

I Мне бы хотелось, чтобы каждая запись рассказывала мне, где находится конкретный фрукт.

Это было достаточно легко сделать с двойным для l oop. Но когда я делаю то же самое с вложенной функцией lapply, похоже, что она вообще не изменяет список (то есть vertical_data). Это почему? Причина, по которой я хотел бы сделать это с помощью функции применения, заключается в том, что это быстрее. В моем реальном наборе данных будут тысячи предметов и "фрукты". Это займет слишком много времени для циклов.

Я бы очень признателен за помощь.

Спасибо

Ответы [ 2 ]

4 голосов
/ 21 марта 2020

Мы можем использовать split для данных unlist ed

split(rep(names(aList), lengths(aList)), unlist(aList))

Или другой вариант: stack для двух столбцов data.frame, а затем выполнить команду split

with(stack(aList), split(as.character(ind), values))
#$apple
#[1] "a1" "a2" "a3" "a4" "a5" "a6" "a7" "a8"

#$banana
#[1] "a1" "a2" "a5" "a7" "a9"

#$blueberry
#[1] "a6"

#$cherry
#[1] "a1" "a2" "a8"

#$lemon
#[1] "a5" "a6" "a8"

#$mango
#[1] "a7"  "a8"  "a10"

#$orange
#[1] "a1" "a5" "a9"

#$pineapple
#[1] "a3" "a5"

#$raspberry
#[1] "a4"

#$strawberry
#[1] "a1"  "a3"  "a4"  "a9"  "a10"

#$watermelon
#[1] "a7"

Или, как упомянул @rawr

unstack(stack(aList)[2:1])

Относительно назначения в пределах lapply и for l oop, это основано на окружающей среде. В for l oop присваивание изменяет объект в глобальном env, но в lapply это автономный env, иначе нужно сделать <<- (не рекомендуется) или указать env как глобальный env

vertical_data <- list()
lapply(names(aList), function(x) lapply(aList[[x]], 
      function(y) if (is.null(vertical_data[[y]])) {
         vertical_data[[y]] <<- x
         } else {vertical_data[[y]] <<- c(x, vertical_data[[y]])
         }))
1 голос
/ 21 марта 2020

Мы можем использовать enframe для преобразования списка имен в фрейм данных, а затем разделить name на основе value.

tibble::enframe(aList) %>% tidyr::unnest(value) %>% {split(.$name, .$value)}

#$apple
#[1] "a1" "a2" "a3" "a4" "a5" "a6" "a7" "a8"

#$banana
#[1] "a1" "a2" "a5" "a7" "a9"

#$blueberry
#[1] "a6"

#$cherry
#[1] "a1" "a2" "a8"

#$lemon
#[1] "a5" "a6" "a8"

#$mango
#[1] "a7"  "a8"  "a10"

#$orange
#[1] "a1" "a5" "a9"

#$pineapple
#[1] "a3" "a5"

#$raspberry
#[1] "a4"

#$strawberry
#[1] "a1"  "a3"  "a4"  "a9"  "a10"

#$watermelon
#[1] "a7"
...