Р: Как вы суммируете данные как для листьев, так и для узлов в Data.Tree?

Я использую структуру data.tree для обобщения различной информации по файловым папкам. В каждой папке у меня есть несколько файлов (значение), и что мне нужно сделать для каждой папки, так это суммировать, сколько файлов содержит папка + все подпапки.

Пример данных:

library(data.tree)
data <- data.frame(pathString = c("MainFolder",
                                  "MainFolder/Folder1",
                                  "MainFolder/Folder2",
                                  "MainFolder/Folder3",
                                  "MainFolder/Folder1/Subfolder1",
                                  "MainFolder/Folder1/Subfolder2"),
                   Value = c(1,1,5,2,4,10))
tree <- as.Node(data, Value)
print(tree, "Value")
               levelName Value
1 MainFolder             1
2  ¦--Folder1            1
3  ¦   ¦--Subfolder1     4
4  ¦   °--Subfolder2    10
5  ¦--Folder2            5
6  °--Folder3            2

Мое текущее и ОЧЕНЬ МЕДЛЕННОЕ решение проблемы:

# Function to sum up file counts pr folder + subfolders
total_count <- function(node) {
  results <- sum(as.data.frame(print(node, "Value"))$Value)
  return(results)
}

# Summing up file counts pr folder + subfolders
tree$Do(function(node) node$Value_by_folder <- total_count(node))


# Results
print(tree, "Value", "Value_by_folder")
           levelName Value Value_by_folder
1 MainFolder             1              23
2  ¦--Folder1            1              15
3  ¦   ¦--Subfolder1     4               4
4  ¦   °--Subfolder2    10              10
5  ¦--Folder2            5               5
6  °--Folder3            2               2

У вас есть предложение, как сделать это более эффективно? Я пытался построить рекурсивный метод, а также использовать функции "isLeaf" и "children" на узлах, но не смог заставить его работать.


person Esben Eickhardt    schedule 08.09.2017    source источник


Ответы (2)


Это эффективный способ сделать это. Он использует API data.tree и сохраняет значение в дереве:

MyAggregate <- function(node) {
  if (node$isLeaf) return (node$Value)
  sum(Get(node$children, "Value_by_folder")) + node$Value
}

tree$Do(function(node) node$Value_by_folder <- MyAggregate(node), traversal = "post-order")
person Christoph Glur    schedule 11.09.2017
comment
Спасибо, я проверю два ответа сегодня. Этот выглядит наиболее чистым, но есть ли причина, по которой он не возвращается (сумма (Get (node ​​$ Children, Value_by_folder)) + node $ Value)? - person Esben Eickhardt; 12.09.2017
comment
Нет, это идентично. Функции R всегда возвращают последнее значение. - person Christoph Glur; 12.09.2017
comment
@EsbenEickhardt Не уверен в вашем определении уборщика, но я думаю, что это должно быть медленнее. Можете ли вы сообщить нам результаты сравнительного анализа вашего большого набора данных? - person F. Privé; 12.09.2017
comment
@ F.Privé Ах, извините, я понятнее в том смысле, что в этом решении используются функции из пакета data.tree, а не базовые функции R. Функции и синтаксис, такие как «force» и «‹‹-», требуют более глубокого понимания, чем вспомогательные функции из пакета data.tree. Можно было бы возразить, что лучше понять базу R, чем пакет, который упрощает операции. Ваш ответ был хорошо объяснен, но я все еще думаю, что это решение легче понять и использовать. - person Esben Eickhardt; 12.09.2017
comment
@EsbenEickhardt В интересующей вас части (моя редакция) я ничего из этого не использую. - person F. Privé; 12.09.2017

Ты можешь сделать:

get_value_by_folder <- function(tree) {

  res <- rep(NA_real_, tree$totalCount)

  i <- 0
  myApply <- function(node) {
    i <<- i + 1
    force(k <- i)
    res[k] <<- node$Value + `if`(node$isLeaf, 0, sum(sapply(node$children, myApply)))
  }

  myApply(tree)
  res
}

force важен, потому что ленивое вычисление R мешает порядку, который вы хотите заполнить res.

И вы получаете:

> get_value_by_folder(tree)
[1] 23 15  4 10  5  2

Редактировать: если вы хотите заполнить его непосредственно в дереве.

get_value_by_folder2 <- function(tree) {

  myApply <- function(node) {
    node$Value_by_folder <- node$Value + `if`(node$isLeaf, 0, sum(sapply(node$children, myApply)))
  }

  myApply(tree)
  tree
}

> print(get_value_by_folder2(tree), "Value", "Value_by_folder")
           levelName Value Value_by_folder
1 MainFolder             1              23
2  ¦--Folder1            1              15
3  ¦   ¦--Subfolder1     4               4
4  ¦   °--Subfolder2    10              10
5  ¦--Folder2            5               5
6  °--Folder3            2               2

Обратите внимание, что класс представляет собой среду, поэтому исходный tree изменяется.

> print(tree, "Value", "Value_by_folder")
           levelName Value Value_by_folder
1 MainFolder             1              23
2  ¦--Folder1            1              15
3  ¦   ¦--Subfolder1     4               4
4  ¦   °--Subfolder2    10              10
5  ¦--Folder2            5               5
6  °--Folder3            2               2
person F. Privé    schedule 11.09.2017
comment
Сохраняет ли это значение в дереве? - person Esben Eickhardt; 11.09.2017
comment
@EsbenEickhardt Нет. Я думал, вы хотели, чтобы это был вектор для более удобного использования. Я обновлю свой ответ. - person F. Privé; 11.09.2017