#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
inunionFun()
и разрешаем передавать наш список в качестве аргумента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