Как использовать объединение элементов во вложенном списке в R

#r

#r

Вопрос:

Скажем, у меня есть вложенный список lst (все элементы относятся к классу int ). Я не знаю длину lst заранее; однако я знаю, что каждый элемент lst представляет собой список длины, скажем k

 length(lst[[i]]) # this equals k and is known in advance, 
                 # this is true for i = 1 ... length(lst)
  

Как мне использовать union 1-го элемента, 2-го элемента, …, k-го элемента из всех элементов lst

В частности, если длина lst равна n , я хочу (не R-код):

 # I know that union can only be taken for 2 elements, 
# following is for illustration purposes
listUnion1 <- union(lst[[1, 1]], lst[[2, 1]], ..., lst[[n, 1]])
listUnion2 <- union(lst[[1, 2]], lst[[2, 2]], ..., lst[[n, 2]])
.
.
.
listUnionk <- union(lst[[1, k]], lst[[2, k]], ..., lst[[n, k]])
  

Любая помощь или указания приветствуются.

Вот набор данных, который можно использовать, n = 3 и k = 2

 list(structure(list(a = 1:5, b = 6:11), .Names = c("a", "b")), 
    structure(list(a = 6:11, b = 1:5), .Names = c("a", "b")), 
    structure(list(a = 12, b = 12), .Names = c("a", "b")))
  

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

1. Пожалуйста, дайте мне знать, если вопрос неясен или есть какая-то проблема. Пожалуйста, примите мои искренние извинения заранее.

2. единственное, что меня смутило, — это ваше обозначение, lst[[1, 1]] которое не соответствует правильному R-коду для списка, который вы показываете. Я знаю, вы говорите, что это не R-код, но lst[[1]][1] было бы лучше, поскольку это R-код. Другой вещью, которая была бы полезна, был бы некоторый пример вывода для примера данных, которые вы предоставляете (кстати, спасибо за это!). Помимо этого, я думаю, что Q хорош и интересен.

3. @gavin Спасибо вам за ваше решение. Извините, я должен был быть осторожен!

Ответ №1:

Вот общее решение, аналогичное по духу решению @Ramnath, но избегающее использования union() , которое является двоичной функцией. Хитрость заключается в том, чтобы отметить, что union() реализовано как:

 unique(c(as.vector(x), as.vector(y)))
  

и бит внутри unique() может быть получен путем удаления из списка n -го компонента каждого списка.

Тогда полное решение таково:

 unionFun <- function(n, obj) {
    unique(unlist(lapply(obj, `[[`, n)))
}
lapply(seq_along(lst[[1]]), FUN = unionFun, obj = lst)
  

что дает:

 [[1]]
 [1]  1  2  3  4  5  6  7  8  9 10 11 12

[[2]]
 [1]  6  7  8  9 10 11  1  2  3  4  5 12
  

на основе данных, которые вы показали.

Пара полезных функций этого:

  • мы используем `[[` для подмножества obj в unionFun . Это похоже на function(x) x$a в ответе @Ramnath. Однако нам не нужна анонимная функция (вместо этого мы используем `[[` ). Эквивалент ответа @Ramnath является: lapply(lst, `[[`, 1)
  • чтобы обобщить вышесказанное, мы заменяем 1 выше на n in unionFun() и разрешаем передавать наш список в качестве аргумента obj .

Теперь, когда у нас есть функция, которая обеспечит объединение n -го элемента данного списка, мы можем lapply() перебирать индексы k , применяя наши unionFun() к каждому подэлементу lst , используя тот факт, что длина lst[[1]] одинакова length(lst[[k]]) для всех k .

Если это поможет получить имена n -го элемента в возвращаемом объекте, мы можем сделать:

 > unions <- lapply(seq_along(lst[[1]]), FUN = unionFun, obj = lst)
> names(unions) <- names(lst[[1]])
> unions
$a
 [1]  1  2  3  4  5  6  7  8  9 10 11 12

$b
 [1]  6  7  8  9 10 11  1  2  3  4  5 12
  

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

1. Потрясающе — Очень, очень полезно! Здорово, что такие люди, как вы, тратят время на объяснение своего решения.

Ответ №2:

Вот одно из решений

 # generate dummy data
x1 = sample(letters[1:5], 20, replace = T)
x2 = sample(letters[1:5], 20, replace = T)
df = data.frame(x1, x2, stringsAsFactors = F)

# find unique elements in each column
union_df = apply(df, 2, unique)
  

Дайте мне знать, работает ли это

РЕДАКТИРОВАТЬ: Вот решение для списков, использующее предоставленные вами данные

 mylist = list(structure(list(a = 1:5, b = 6:11), .Names = c("a", "b")), 
              structure(list(a = 6:11, b = 1:5), .Names = c("a", "b")), 
              structure(list(a = 12, b = 12), .Names = c("a", "b")))
list_a = lapply(mylist, function(x) x$a)
list_b = lapply(mylist, function(x) x$b)

union_a = Reduce(union, list_a)
union_b = Reduce(union, list_b)
  

Если у вас в списке более 2 элементов, мы могли бы обобщить этот код.

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

1. спасибо, я был бы очень заинтересован в решении, которое использует функции *apply, кроме apply (поскольку это похоже на использование цикла for). Также df, похоже, не является списком списков?

2. Спасибо за ваше решение! Я приму решение Гэвина, поскольку оно обобщает вашу идею.

Ответ №3:

Вот другой способ: используйте do.call/rbind для выстраивания списков по «имени» во фрейм данных, затем apply unique/do.call для каждого столбца этого фрейма данных. (Я немного изменил ваши данные, чтобы объединения ‘a’ и ‘b’ имели разную длину, чтобы убедиться, что это работает правильно).

 lst <- list(structure(list(a = 1:5, b = 6:11), .Names = c("a", "b")), 
    structure(list(a = 6:10, b = 1:5), .Names = c("a", "b")), 
    structure(list(a = 12, b = 12), .Names = c("a", "b")))

> apply(do.call(rbind, lst),2, function( x ) unique( do.call( c, x)))
$a
 [1]  1  2  3  4  5  6  7  8  9 10 12

$b
 [1]  6  7  8  9 10 11  1  2  3  4  5 12
  

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

1. Это хорошее решение @Prasad. do.call Бит возвращает интересный объект.

2. @Gavin, спасибо… да, первый do.call возвращает странно выглядящий фрейм данных, элементами которого являются списки.

Ответ №4:

Ваши данные

 df <- list(structure(list(a = 1:5, b = 6:11), .Names = c("a", "b")), 
           structure(list(a = 6:11, b = 1:5), .Names = c("a", "b")), 
           structure(list(a = 12, b = 12), .Names = c("a", "b")))
  

Это дает вам уникальные значения вложенных списков:

 library(plyr)
df.l <- llply(df, function(x) unlist(unique(x)))

R> df.l
[[1]]
 [1]  1  2  3  4  5  6  7  8  9 10 11

[[2]]
 [1]  6  7  8  9 10 11  1  2  3  4  5

[[3]]
[1] 12
  

Редактировать

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

 df <- list(structure(list(a = 1:5, b = 6:11), .Names = c("a", "b")), 
           structure(list(a = 6:11, b = 1:5), .Names = c("a", "b")), 
           structure(list(a = 12, b = 12, c = 10:14), .Names = c("a", "b", "c")))


f.x <- function(x.list) {
  x.names <- names(x.list)
  i <- combn(x.names, 2)
  l <- apply(i, 2, function(y) x.list[y])
  llply(l, unlist)
}
  

Теперь вы можете применить функцию к своим данным.

 all.l <- llply(df, f.x)
llply(all.l, function(x) llply(x, unique))

R> [[1]]
[[1]][[1]]
 [1]  1  2  3  4  5  6  7  8  9 10 11


[[2]]
[[2]][[1]]
 [1]  6  7  8  9 10 11  1  2  3  4  5


[[3]]
[[3]][[1]]
[1] 12

[[3]][[2]]
[1] 12 10 11 13 14

[[3]][[3]]
[1] 12 10 11 13 14
  

Однако вложенная структура не очень удобна для пользователя. Это можно было бы немного изменить…

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

1. я думаю, что OP хочет объединение k-го элемента всех списков. ваше решение возвращает объединение всех элементов в каждом списке. возможно, вы захотите изменить свой вызов plyr

2. @Ramnath Спасибо, Рамнат, я немного изменил это. И оставил место для дополнительных улучшений 😉

Ответ №5:

Согласно документации «unlist» — это рекурсивная функция, следовательно, независимо от уровня вложенности предоставленных списков вы можете получить все элементы, передав их в unlist. Вы можете получить объединение подсписков следующим образом.

 lst <- list(structure(list(a = 1:5, b = 6:11), .Names = c("a", "b")), 
structure(list(a = 6:11, b = 1:5), .Names = c("a", "b")), 
structure(list(a = 12, b = 12), .Names = c("a", "b")))

lapply(lst, function(sublst) unique(unlist(sublst)))

[[1]]
[1]  1  2  3  4  5  6  7  8  9 10 11

[[2]]
[1]  6  7  8  9 10 11  1  2  3  4  5

[[3]]
[1] 12