【问题标题】:Reformat table in R在 R 中重新格式化表格
【发布时间】:2015-09-14 17:27:35
【问题描述】:

我有一个如下表(同一ID的不同行将具有相同的性别和年龄值但不同的类别和子类别值):

  ID product.category sub.category gender   age
1  1             food      chicken      M young
2  1          kitchen       napkin      M young
3  1             food        steak      M young
4  2       electronic        phone      F   mid
5  3            cloth        shirt      M   old
6  3          kitchen         bowl      M   old
7  4             alch         beer      F young

通过组合具有相同 ID 的不同行,我想将表格改写如下:

  ID product.category1 sub.category1 product.category2 sub.category2 product.category3 sub.category3 gender   age
1  1              food       chicken           kitchen        napkin              food         steak      M young
2  2        electronic         phone              null          null              null          null      F   mid
3  3             cloth         shirt           kitchen          bowl              null          null      M   old
4  4              alch          beer              null          null              null          null      F young

如何在 R 中做到这一点?

#

新数据集:文本变量实际上是笔记的文本列

text    Category    Subcategory variable1   variable2   variable3   variable4   date
aaaaa   c1  s11 v1  N   RETAIL  Y   2014-01
aaaaa   c2  s22 v1  N   LEASE   Y   2014-01
aaaaa   c3  s31 v1  N   LEASE   Y   2014-01
bbbbb   c1  s12 v2  N   LEASE   Y   2014-01
ccccc   c2  s21 v1  N   LEASE   Y   2014-01
ddddd   c2  s21 v1  N   RETAIL  Y   2014-01
ddddd   c3  s31 v1  N   LEASE   Y   2014-01
eeeee   c1  s11 v1  N   RETAIL  Y   2014-01
fffff   c2  s21 v2  U   RETAIL  Y   2014-01

谢谢

【问题讨论】:

    标签: r reshape


    【解决方案1】:

    我们使用reshape2 包中的meltdcast 的组合。

    library(dplyr)
    library(reshape2)
    m2 <- melt(df, c("ID", "gender", "age")) %>% group_by(ID, variable) %>% 
      mutate(variable2 = paste0(variable, seq_along(value)))
    newdf <- dcast(m2[!names(m2) %in% "variable"], ...~variable2, value.var="value", fill="null")
    

    我们首先通过产品类别和子类别来融合原始数据框。接下来使用 dplyr,我们按 id 列和 product 列(现在默认称为“变量”)进行分组,并创建一个名为 variable2 的新列。这只是类别标题的粘贴和观察的连续计数。

    现在我们有了一个新列,我们可以通过它来分散数据。我们使用dcast 在新的 variable2 列上“变宽”。还有一个名为 fill 的参数,我们将其设置为等于 "null",告诉 dcast 用什么来填充缺失值。


    下面我们根据所需的输出对列重新排序。这个诀窍是值得注意的,即使它很小。创建一个交织序列很有趣。我们的输出将按字母顺序排列(“p1”、“p2”、“p3”、“s1”、“s2”、“s3”)。我们想要一个将它们编织在一起的序列。挑战在于获得类似 (1,4,2,5,3,6) 的东西。所以我们使用:

    c(rbind(1:3, 4:6))
    [1] 1 4 2 5 3 6
    

    很酷吧?我们利用了 rbind 将在按行输入值时按列展开的事实。在我们的例子中,写1:3 无济于事,因为数据中可能有更多产品。但是我们知道有两个标题“产品类别”和“子子类别”。我们将 variable2 的唯一值除以 2 并使用它。

    n <- nrow(unique(m2[,"variable2"]))
    newdf[c(1:3,(c(rbind(1:(n/2), (n/2+1):n))+3))]
    #   ID gender   age product.category1 sub.category1 product.category2
    # 1  1      M young              food       chicken           kitchen
    # 2  2      F   mid        electronic         phone              null
    # 3  3      M   old             cloth         shirt           kitchen
    # 4  4      F young              alch          beer              null
    #   sub.category2 product.category3 sub.category3
    # 1        napkin              food         steak
    # 2          null              null          null
    # 3          bowl              null          null
    # 4          null              null          null
    

    更新

    使用提供的新数据集,相同的代码结构适用于新列名。

    m2 <- melt(df, measure.vars=c("Category", "Subcategory")) %>% group_by(text, variable) %>%
      mutate(variable2 = paste0(variable, seq_along(value)))
    
    newdf <- dcast(m2[!names(m2) %in% "variable"], ... ~ variable2, value.var="value", fill="null")
    n <- nrow(unique(m2[,"variable2"]))
    newdf2 <- newdf[c(1:5, c(rbind(1:(n/2), (n/2+1):n))+5)]
    newdf2
    #    text variable1 variable3 variable4    date Category1 Subcategory1 Category2
    # 1 aaaaa        v1     LEASE         Y 2014-01      null         null        c2
    # 2 aaaaa        v1    RETAIL         Y 2014-01        c1          s11      null
    # 3 bbbbb        v2     LEASE         Y 2014-01        c1          s12      null
    # 4 ccccc        v1     LEASE         Y 2014-01        c2          s21      null
    # 5 ddddd        v1     LEASE         Y 2014-01      null         null        c3
    # 6 ddddd        v1    RETAIL         Y 2014-01        c2          s21      null
    # 7 eeeee        v1    RETAIL         Y 2014-01        c1          s11      null
    # 8 fffff        v2    RETAIL         Y 2014-01        c2          s21      null
    #   Subcategory2 Category3 Subcategory3
    # 1          s22        c3          s31
    # 2         null      null         null
    # 3         null      null         null
    # 4         null      null         null
    # 5          s31      null         null
    # 6         null      null         null
    # 7         null      null         null
    # 8         null      null         null
    

    【讨论】:

    • 非常感谢!非常详细且易于理解:)
    • 嗨皮埃尔,我试过你的代码,它给我的错误是 "Error: unknown column 'variable'" ,这是我的代码 "m3 % group_by(text, variable) %>% mutate(variable2 = paste0(variable, seq_along(value)))" 谢谢
    • 做了一些修改,现在看来上述问题没问题...但我收到一个错误“错误:无法分配大小为 5.6 Gb 的向量”,这很奇怪,因为它不应该这么大。 .....
    • 我尝试了一些子集,它生成了输出....但是有些奇怪的是,对于某些记录,它甚至有 category100,但是 category1-99 都是空白的,你知道为什么我得到错误的结果?谢谢
    • 没有实际数据很难确定哪里出了问题。你能发布有代表性的样本吗?
    【解决方案2】:

    data.table dcast您可以使用 reshape2 或 data.table 包中的 dcast

    library(data.table)
    setDT(DT)
    
    DT[, obsno := 1:.N, by=ID]
    res <- dcast(DT, ID+gender+age~obsno, value.var=c("product.category","sub.category"))
    

    这给了

       ID gender   age product.category_1 product.category_2 product.category_3 sub.category_1 sub.category_2 sub.category_3
    1:  1      M young               food            kitchen               food        chicken         napkin          steak
    2:  2      F   mid         electronic                 NA                 NA          phone             NA             NA
    3:  3      M   old              cloth            kitchen                 NA          shirt           bowl             NA
    4:  4      F young               alch                 NA                 NA           beer             NA             NA
    

    要按您想要的顺序查看列,您可以执行类似

    的操作
    res[, c(1:3,4,7,5,8,6,9), with=FALSE]
    

    tidyr 包可能也可以使用类似的方法(尽管它不会被称为“dcast”)。

    我建议坚持使用长格式(您原来的格式)进行任何分析。除了浏览数据之外,您正在寻找的这种宽格式对于任何事情都非常麻烦。


    第二个例子对于OP的第二个例子,我会做

    DT2[, obsno := 1:.N, by=text]
    dcast(DT2, ...~obsno, value.var=c("Category", "Subcategory"))
    

    从@PierreLafortune 的回答中复制...~ 技巧。结果是

        text variable1 variable2 variable3 variable4    date Category_1 Category_2 Category_3 Subcategory_1 Subcategory_2 Subcategory_3
    1: aaaaa        v1         N     LEASE         Y 2014-01         NA         c2         c3            NA           s22           s31
    2: aaaaa        v1         N    RETAIL         Y 2014-01         c1         NA         NA           s11            NA            NA
    3: bbbbb        v2         N     LEASE         Y 2014-01         c1         NA         NA           s12            NA            NA
    4: ccccc        v1         N     LEASE         Y 2014-01         c2         NA         NA           s21            NA            NA
    5: ddddd        v1         N     LEASE         Y 2014-01         NA         c3         NA            NA           s31            NA
    6: ddddd        v1         N    RETAIL         Y 2014-01         c2         NA         NA           s21            NA            NA
    7: eeeee        v1         N    RETAIL         Y 2014-01         c1         NA         NA           s11            NA            NA
    8: fffff        v2         U    RETAIL         Y 2014-01         c2         NA         NA           s21            NA            NA
    

    【讨论】:

    • 嗨弗兰克,当我尝试你的代码时,它给了我错误:“错误:value.var (product.categorysub.category) not found in input”或“.subset2(x, i, exact = exact) : subscript out of bounds” 这是我的代码,请告诉我应该如何修改它:“setDT(output) output[, obsno := 1:.N, by=text] res
    • 嗯,我不确定它为什么会在那里失败。您可以在较小的数据子集上尝试它,例如 output2 &lt;- output[1:10],然后只需查看 dcast(output2, text~obsno, value.var=c("Category","Subcategory")) 本身即可诊断代码失败的位置。
    • 我只选择了 10 行作为输出 1 并尝试了这个,但仍然不起作用....我将用新的细节更新我的问题....请看一下。谢谢
    • 嗨弗兰克,我在问题中更新了新的表格格式(我厌倦了代码但没有工作的那个)......请看一看。谢谢
    • @KarlTian 我已经为您发布的新案例添加了代码。它似乎有效。
    【解决方案3】:

    dplyrtidyr 的替代方案:

    newdf <- df %>% gather(variable, value, product.category, sub.category) %>%
      group_by(ID, variable) %>%
      mutate(variable2 = paste0(variable, seq_along(value))) %>%
      ungroup() %>%
      select(-variable) %>%
      spread(variable2 , value)
    

    给出:

    > newdf
    Source: local data frame [4 x 9]
    
         ID gender    age product.category1 product.category2 product.category3 sub.category1 sub.category2 sub.category3
      (int) (fctr) (fctr)             (chr)             (chr)             (chr)         (chr)         (chr)         (chr)
    1     1      M  young              food           kitchen              food       chicken        napkin         steak
    2     2      F    mid        electronic                NA                NA         phone            NA            NA
    3     3      M    old             cloth           kitchen                NA         shirt          bowl            NA
    4     4      F  young              alch                NA                NA          beer            NA            NA
    

    在第二个示例数据集上也可以这样做:

    newdat <- dat %>% gather(variable, value, Category, Subcategory) %>%
      group_by(text, variable) %>%
      mutate(var2 = paste0(variable, seq_along(value))) %>%
      ungroup() %>%
      select(-variable) %>%
      spread(var2 , value)
    

    给出:

    > newdat
    Source: local data frame [8 x 12]
    
        text variable1 variable2 variable3 variable4    date Category1 Category2 Category3 Subcategory1 Subcategory2 Subcategory3
      (fctr)    (fctr)    (fctr)    (fctr)    (fctr)  (fctr)     (chr)     (chr)     (chr)        (chr)        (chr)        (chr)
    1  aaaaa        v1         N     LEASE         Y 2014-01        NA        c2        c3           NA          s22          s31
    2  aaaaa        v1         N    RETAIL         Y 2014-01        c1        NA        NA          s11           NA           NA
    3  bbbbb        v2         N     LEASE         Y 2014-01        c1        NA        NA          s12           NA           NA
    4  ccccc        v1         N     LEASE         Y 2014-01        c2        NA        NA          s21           NA           NA
    5  ddddd        v1         N     LEASE         Y 2014-01        NA        c3        NA           NA          s31           NA
    6  ddddd        v1         N    RETAIL         Y 2014-01        c2        NA        NA          s21           NA           NA
    7  eeeee        v1         N    RETAIL         Y 2014-01        c1        NA        NA          s11           NA           NA
    8  fffff        v2         U    RETAIL         Y 2014-01        c2        NA        NA          s21           NA           NA
    

    【讨论】:

    • @KarlTian 查看更新后的答案,现在它还包括您的第二个(新)数据集的解决方案
    • 谢谢 Jaap:) 会试试的
    • 嗨 Jaap,@Jaap,我试过你的,它有效....但我想要的结果有所不同:对于第 1 行和第 5 行,这可能是类别 1,2 ,3 类似于 row1: c2, c3,na row1: c2, c3,na 这意味着无论非空值类别的顺序如何,都将它们放在“NA”之前?谢谢
    • @KarlTian 我不完全确定你的意思,但你可以更改数据框中观察的顺序,例如arrange(Category1, Category2, Category3)。这将首先通过Category1 订购newdat,然后是Category2,最后是Category3
    猜你喜欢
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    • 2016-10-07
    • 1970-01-01
    • 2021-11-09
    • 2016-01-16
    相关资源
    最近更新 更多