R - Заполнение пустого списка из операций над другим списком - PullRequest
0 голосов
/ 18 июня 2019

Я пишу R-программу для анализа древовидной структуры.В моем примере ниже, в дереве есть 10 узлов, и предки каждого узла (родительские узлы этого узла, а также родительские узлы этого узла и т. Д.) Хранятся в списке, называемом предками.Пользователь запросит вектор имен узлов, и я пытаюсь создать список, который будет заполнен предками этого запроса.Каждый элемент в списке будет содержать список потомков запроса для каждого из вызванных предков.Пожалуйста, смотрите ниже пример

Допустим, у меня есть следующая структура.

enter image description here Так что список предков будет выглядеть так

Ancestors <- list()
Ancestors$'p1' <- c('p2', 'p3', 'p4', 'p5', 'p8', 'p9', 'p10')
Ancestors$'p2' <- c('p4', 'p5', 'p8', 'p9', 'p10')
Ancestors$'p3' <- c('p4', 'p5', 'p9', 'p10')
Ancestors$'p4' <- c('p5', 'p9', 'p10')
Ancestors$'p5' <- c('p9', 'p10')
Ancestors$'p6' <- c('p4', 'p5', 'p9', 'p10')
Ancestors$'p7' <- c('p5', 'p9', 'p10')
Ancestors$'p8' <- c('p5', 'p9', 'p10')
Ancestors$'p9' <- NA
Ancestors$'p10' <- NA

Скажем, запрос:

query <- c('p5', 'p4', 'p1')

Тогда список, который я хотел бы составить, будет

# lst <- list()
# 
# lst$'p2'
#   'p1'
# lst$'p3'
#   'p1'
# lst$'p4'
#   'p1'
# lst$'p5'
#   'p1', 'p4'
# lst$'p8'
#   'p1'
# lst$'p9'
#   'p1', 'p4', 'p5'
# lst$'p10'
#   'p1', 'p4', 'p5'

(2,3,4,5,8,9,10) все предки, которые существуют для условий запроса.Вот список, который я хотел бы составить.Затем для каждого из названных элементов я хотел бы записать список условий запроса, которые являются потомками элемента списка.Я извиняюсь за запутанный пример.Надеюсь, это имеет смысл.

Вот что я попробовал до сих пор

lst <- list()

lapply(query, function(x) {
  theAncestors <- Ancestors[[x]]
  sapply(theAncestors, function(y) {
    lst[[y]][[1]] <- c(lst[[y]][[1]], x)
  })
})

Но этот список не заполняется.Все, что происходит, это то, что он печатает

[[1]]
  p9  p10 
"p5" "p5" 

[[2]]
  p5   p9  p10 
"p4" "p4" "p4" 

[[3]]
  p2   p3   p4   p5   p8   p9  p10 
"p1" "p1" "p1" "p1" "p1" "p1" "p1" 

, что немного отличается от того, что я хочу.Кроме того, когда я пытаюсь вывести lst, он все еще пуст.Так что этот код даже не влияет на lst.Итак, как я могу получить желаемый результат?Я думал об использовании цикла for, но я думаю, что они очень медленные в R. Моя настоящая проблема, вероятно, будет состоять из сотен или тысяч терминов запроса и еще многих терминов-предков.Так что это будет очень долго.Поэтому я думаю, что цикл for, вероятно, не сработает.

Редактировать: Я понял это.Мой код теперь:

lst <- list()

aLst <- unlist(lapply(query, function(x) {
  theAncestors <- Ancestors[[x]]
  sapply(theAncestors, function(y) {
    lst[[y]][1] <- c(lst[[y]][[1]], x)
  })
}))
aLst <- split(unname(aLst), names(aLst))

Это распечатывает

$p10
[1] "p5" "p4" "p1"

$p2
[1] "p1"

$p3
[1] "p1"

$p4
[1] "p1"

$p5
[1] "p4" "p1"

$p8
[1] "p1"

$p9
[1] "p5" "p4" "p1"

Что я и хотел

1 Ответ

2 голосов
/ 18 июня 2019

Причина, по которой он просто печатает, заключается в том, что ваш lapply ни к чему не привязан.Причина, по которой он не заполняется lst, немного сложнее и связана с областью действия функции - здесь есть очень подробное объяснение: http://adv -r.had.co.nz / Environments.html # function-envs.

Суть в том, что lst не изменяется - его копия изменяется внутри функции, но она модифицируется в среде, которая выбрасывается после завершения вызова функции.Есть несколько способов обойти это - первое - использовать <<- вместо <-.Этот оператор «глубокого присваивания» выглядит глубже, чем <- и будет изменять вещи вне области действия функции.

Во-вторых, я думаю, что подходить к вашей проблеме немного по-другому - взяв список Ancestors и queryсначала вы можете сделать:

query_members <- Ancestors[query]

query_members

# $`p4`
# [1] "p5"  "p9"  "p10"

# $p5
# [1] "p9"  "p10"

# $p1
# [1] "p2"  "p3"  "p4"  "p5"  "p8"  "p9"  "p10"

для поднабора элементов, которые вы хотите.Теперь вам нужно «инвертировать» это в некотором смысле.Для начала возьмем уникальных предков членов вашего запроса:

query_ancestors <- sort(unique(unlist(query_members)))

query_ancestors

# [1] "p10" "p2"  "p3"  "p4"  "p5"  "p8"  "p9" 

Теперь у вас есть кое-что, что вы можете lapply, потому что оно имеет ту же структуру, что и желаемый вывод.Вам просто нужно ответить на вопрос «для каждого из этих предков, какой элемент запроса является потомком?»

Итак, вы можете написать небольшую функцию вроде:

get_descendants <- function(query_ancestor, query_members) {

  sort(names(Filter(function(x) { query_ancestor %in% x }, query_members)))

}

# test it out
get_descendants("p5", query_members)

# [1] "p1" "p4"

Теперь мы можем lapply и задайте имена, используя query_ancestors:

lst <- lapply(query_ancestors, get_descendants, query_members = query_members)
names(lst) <- query_ancestors
lst

# $`p10`
# [1] "p1" "p4" "p5"

# $p2
# [1] "p1"

# $p3
# [1] "p1"

# $p4
# [1] "p1"

# $p5
# [1] "p1" "p4"

# $p8
# [1] "p1"

# $p9
# [1] "p1" "p4" "p5"

Собрав все это вместе, вы можете написать замечательную функцию, которая оборачивает все это и позволяет вам сосредоточиться на запросе и списке предков:

list_ancestors <- function(query, Ancestors) {

  query_members <- Ancestors[query]
  query_ancestors <- sort(unique(unlist(query_members)))

  lst <- lapply(query_ancestors, function(element, members) {
    sort(names(Filter(function(x) element %in% x, members)))
  }, members = query_members)

  names(lst) <- query_ancestors

  lst

}

# so for example with just p7
list_ancestors("p7", Ancestors)

# $`p10`
# [1] "p7"

# $p5
# [1] "p7"

# $p9
# [1] "p7"

Надеюсь, это поможет!

...