【问题标题】:R: aggregate values on a treeR:树上的聚合值
【发布时间】:2015-08-09 00:41:53
【问题描述】:

这个问题类似于this,但它有一个 C# 答案,我需要一个 R 答案。

我有大约 650 行的大约 50 个文件,其格式和数据与这个玩具数据非常相似:

dput(y)
structure(list(level1 = c(4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 
4L, 4L, 4L), level2 = c(NA, 41L, 41L, 41L, 41L, 41L, 41L, 41L, 
42L, 42L, 42L, 42L), level3 = c(NA, NA, 4120L, 4120L, 4120L, 
4120L, 4120L, 4120L, NA, 4210L, 4210L, 4210L), level4 = c(NA, 
NA, NA, 412030L, 412030L, 412050L, 412050L, 412050L, NA, NA, 
421005L, 421005L), pid = c(NA, NA, NA, NA, 123456L, NA, 789012L, 
345678L, NA, NA, NA, 901234L), description = c("income", "op.income", 
"manuf.industries", "manuf 1", "client 1", "manuf 2", "client 2", 
"client 3", "non-op.income", "financial", "interest", "bank 1"
), value = c(NA, NA, NA, NA, 15000L, NA, 272860L, 1150000L, NA, 
NA, NA, 378L)), .Names = c("level1", "level2", "level3", "level4", 
"pid", "description", "value"), class = c("data.table", "data.frame"
), row.names = c(NA, -12L), .internal.selfref = <pointer: 0x00000000001a0788>)

value 上具有值的每一行都是树的“叶子”,在列level1 到 4 中标识了分支。我想按分支汇总叶子并将相应的值放入value 专栏。

我的预期输出如下所示:

dput(res)
structure(list(level1 = c(4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 
4L, 4L, 4L), level2 = c(NA, 41L, 41L, 41L, 41L, 41L, 41L, 41L, 
42L, 42L, 42L, 42L), level3 = c(NA, NA, 4120L, 4120L, 4120L, 
4120L, 4120L, 4120L, NA, 4210L, 4210L, 4210L), level4 = c(NA, 
NA, NA, 412030L, 412030L, 412050L, 412050L, 412050L, NA, NA, 
421005L, 421005L), pid = c(NA, NA, NA, NA, 123456L, NA, 789012L, 
345678L, NA, NA, NA, 901234L), description = c("income", "op.income", 
"manuf.industries", "manuf 1", "client 1", "manuf 2", "client 2", 
"client 3", "non-op.income", "financial", "interest", "bank 1"
), value = c(1438238L, 1437860L, 1437860L, 15000L, 15000L, 1422860L, 
272860L, 1150000L, 378L, 378L, 378L, 378L)), .Names = c("level1", 
"level2", "level3", "level4", "pid", "description", "value"), class = c("data.table", 
"data.frame"), row.names = c(NA, -12L), .internal.selfref = <pointer: 0x00000000001a0788>)

我知道这可以通过 for 循环来完成,但我想知道是否有更快、更简单的替代方案(我更喜欢 data.table 或基本解决方案,但任何其他包也可以)。到目前为止我尝试过的:

z4<-y[!is.na(pid),sum(value),by=level4]
setkey(y,"level4");setkey(z4,"level4")
y[z4,][is.na(pid)]

这显示了V1 中的所需值,所以我想看看是否可以将它们分配给value

y[z4,][is.na(pid),value:=i.V1]
Error in eval(expr, envir, enclos) : object 'i.V1' not found

我认为这可能是因为调用 i.V1 在链式 [ 中,而不是在初始 y[z4 调用中。但是,如果我只在z4 上设置子集,我怎么知道我应该分配几个匹配的level4 行中的哪一个(这就是我考虑使用is.na(pid) 的原因,因为y[z4,value:=i.V1] 会产生错误的结果,因为它更新所有匹配 level4) 的值。

如您所见,我严重卡在了这个问题上,并且使用“我的方法”我还有 3 个关卡要做。

有没有更简单的方法来做到这一点?

【问题讨论】:

    标签: r tree data.table aggregate


    【解决方案1】:

    因为每个级别的计算都需要上一级的计算,所以我认为需要循环或递归。这是一个使用基数 R 获取值的递归函数。您当然可以使用 data.table 执行类似的操作,这可能会更有效。

    ## Use y as data.frame
    y <- as.data.frame(y)
    
    ## Recursive function to get values
    f <- function(data, lvl=NULL) {
        if (is.null(lvl)) lvl <- 1                                                 # initialize level
        if (lvl == 5) return (data)                                                # we are done
        cname <- paste0("level", lvl)                                              # name of current level
        nname <- ifelse (lvl == 4, "pid", paste0("level", lvl+1))                  # name of next level
        agg <- aggregate(as.formula(paste("value~", cname)), data=data, sum)       # aggregate data
        inds <- (ms <- match(data[,cname], agg[,cname], F)) & is.na(data[,nname])  # find index of leaves to fill
        data$value[inds] <- agg$value[ms[inds]]                                    # add new values
        f(data, lvl+1)                                                             # recurse
    }
    
    f(data=y)
    #    level1 level2 level3 level4    pid      description   value
    # 1       4     NA     NA     NA     NA           income 1438238
    # 2       4     41     NA     NA     NA        op.income 1437860
    # 3       4     41   4120     NA     NA manuf.industries 1437860
    # 4       4     41   4120 412030     NA          manuf 1   15000
    # 5       4     41   4120 412030 123456         client 1   15000
    # 6       4     41   4120 412050     NA          manuf 2 1422860
    # 7       4     41   4120 412050 789012         client 2  272860
    # 8       4     41   4120 412050 345678         client 3 1150000
    # 9       4     42     NA     NA     NA    non-op.income     378
    # 10      4     42   4210     NA     NA        financial     378
    # 11      4     42   4210 421005     NA         interest     378
    # 12      4     42   4210 421005 901234           bank 1     378
    

    我认为,如果需要,只聚合数据的子集,可以提高聚合步骤的效率。老实说,这很有趣,但循环可能是要走的路。

    【讨论】:

    • 这很好用,但如果你允许我,我想保持开放一段时间,看看是否有人可以用不同的方法想出一个解决方案。谢谢!
    • @PavoDive 这是个好主意。这是一个奇怪的数据结构,所以看看人们肯定会想出什么会很有趣。
    • 数据结构属于财务损益报告。级别有账户:子账户:子子账户等。有些软件导出报表的格式是打印,不分析。在你的帮助下,我能够分析它们,主要是通过prop.tableing
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 1970-01-01
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多