【问题标题】:Long to wide on multiple columns by data.tabledata.table 在多列上从长到宽
【发布时间】:2018-01-26 15:22:26
【问题描述】:

我对@9​​87654323@ 的meltdcast 的多个列有疑问。我在 StackOverFlow 上浏览过,但很多类似的帖子不是我想要的。我会在下面解释。

首先,data 是关于问题的原因和价值量。这是我的data部分:

ID   Type    Problem1    Value1     Problem2    Value2    Problem3    Value3
1    A       X           500        Y           1000      Z           400
2    A       X           600        Z           700       
3    B       Y           700        Z           100
4    B       W           200        V           200
5    C       Z           500        V           500       
6    C       X           1000       W           100       V           900

其次,ID 是独一无二的。 Type 包含三个(ABC)。有 5 个问题。

ID == 1 为例。它是 Type A,它包含 3 个问题(XYZ)。它的Problem XValue 500Problem YValue 1000Problem ZValue 400。以ID == 5 为例。它是 Type C 并包含 2 个问题(ZV)。它的Problem ZValue 500Problem VValue 500

第三列IDTypeProblem1Problem2Problem3characterValue1Value2Value3numeric

我想要的结果是:

Type    X     Y     Z     W     V
A       1100  1000  1100  0     0   
B       0     700   100   200   200
C       1000  0     500   100   1400  

我不知道如何正确解释这里。我想对Type 进行分组,然后对每个问题的值求和。我认为这是关于从长到宽。我找到了参考 herehere。第二个可能有用。但是,我不知道从哪里开始。有什么建议吗?

# data
dt <- fread("
ID   Type    Problem1    Value1     Problem2    Value2    Problem3    Value3
1    A       X           500        Y           1000      Z           400
2    A       X           600        Z           700       
3    B       Y           700        Z           100
4    B       W           200        V           200
5    C       Z           500        V           500       
6    C       X           1000       W           100       V           900", fill = T)    

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    我们可以先将melt 指定为measure 中的patterns 为“long”格式,然后将dcastfun.aggregate 设置为sum

    dcast(melt(dt, measure = patterns("^Value", "^Problem"), 
        value.name = c("Value", "Problem"))[Problem != ""
         ][, Problem := factor(Problem, levels = c("X", "Y", "Z", "W", "V"))], 
         Type ~Problem, value.var = "Value", sum, na.rm = TRUE)
    #   Type    X    Y    Z   W    V
    #1:    A 1100 1000 1100   0    0
    #2:    B    0  700  100 200  200
    #3:    C 1000    0  500 100 1400
    

    来自data.tablemelt 可以在measure 参数中采用多个patterns。因此,当我们说"^Value" 时,它匹配所有名称以“Value”开头的列 (^),类似地用于“Problem”,并创建两个“value”列。在上面,我们使用value.name 参数将这些列命名为“值”和“问题”。由于数据集有一些空白,长格式也有我们用Problem != "" 删除的空白元素。仅当我们需要按特定顺序排列列时,下一步才重要。因此,我们将“问题”更改为factor 类并按该顺序指定levels。现在,melt 部分已完成。通过指定公式、value.var 列和fun.aggregate(这里是sum),长格式现在更改为带有dcast 的“宽”

    【讨论】:

    • 很好的答案。但是,你能更详细地解释一下吗? data.table 很棒,但要看到像上面答案这样的复杂语法并不简单。谢谢!
    • @akrun 很好的答案!并且很好地使用了传递给 dcast 的函数。
    • @PeterChen 添加了更多描述。希望对你有帮助
    【解决方案2】:

    简单直接的方法,但仍然有效(希望有人可以帮助改进我的解决方案)。

    library(magrittr)
    rbind(
        dt[, .(Type, P = Problem1, V = Value1)],
        dt[, .(Type, P = Problem2, V = Value2)],
        dt[, .(Type, P = Problem3, V = Value3)]) %>%
        .[P != ""] %>%
        dcast(Type ~ P, value.var = "V", sum)
    

    编辑 按照 akrun 的代码进行改进(将函数传递给 dcast)。

    【讨论】:

    • 如果您在 dplyr/tidyr/pipe 世界中,我会对 data.table 答案遵循相同的逻辑并使用收集和传播。
    • dplyr/tidyr/pipe 很棒。但是,data.table 在处理大数据时速度更快。
    【解决方案3】:

    这可以通过 dplyr / tidyr 轻松完成:

    library("dplyr")
    library("tidyr")
    
    # assume x is your dataframe
    bind_rows(
      select(x, ID, Type, Problem = Problem1, Value = Value1),
      select(x, ID, Type, Problem = Problem2, Value = Value2),
      select(x, ID, Type, Problem = Problem3, Value = Value3)
      ) %>%
    filter(!(is.na(Problem))) %>%
    group_by(Type, Problem) %>%
    summarise(Value = sum(Value)) %>%
    spread(Problem, Value, fill = 0)
    

    输出

    # A tibble: 3 x 6
    # Groups:   Type [3]
       Type     V     W     X     Y     Z
    * <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
    1     A     0     0  1100  1000  1100
    2     B   200   200     0   700   100
    3     C  1400   100  1000     0   500
    

    如果列 V - Z 的顺序很重要,这可以通过添加最终选择语句轻松解决。

    【讨论】:

    【解决方案4】:

    这是一个使用 akrun 执行的 melt 函数然后使用矩阵子集来返回所需结果的方法。

    # melt and aggregate the data
    temp <- melt(dt, measure = patterns("^Value", "^Problem"),
                 value.name = c("Value", "Problem"))[
            !is.na(Value), .(Value=sum(Value)), by=.(Type, Problem)]
    
    # set up the storage matrix
    dimNames <- list(sort(unique(temp$Type)), unique(temp$Problem))
    myMat <- matrix(0, length(dimNames[[1]]), length(dimNames[[2]]), dimnames=dimNames)
    
    # fill in the matrix with the desired values
    myMat[cbind(temp$Type, temp$Problem)] <- temp$Value
    

    这会返回矩阵

    myMat
         X    Y   W    Z    V
    A 1100 1000   0 1100    0
    B    0  700 200  100  200
    C 1000    0 100  500 1400
    

    要返回一个data.table,你可以这样做

    data.table(myMat, keep.rownames=TRUE)
       rn    X    Y   W    Z    V
    1:  A 1100 1000   0 1100    0
    2:  B    0  700 200  100  200
    3:  C 1000    0 100  500 1400
    

    【讨论】:

      猜你喜欢
      • 2022-01-22
      • 1970-01-01
      • 2021-09-04
      • 2018-06-24
      • 2016-09-16
      • 2019-12-24
      • 2019-11-22
      • 2020-11-18
      • 1970-01-01
      相关资源
      最近更新 更多