在 R 中重新格式化 table

Reformat table in R

我有一个 table 如下(具有相同 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 的不同行,我想按如下方式改革 table:

  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变量实际上是notes的文本列

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

谢谢

我们使用包 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")

我们首先通过产品类别和 sub-category 融合原始数据框。接下来使用 dplyr,我们按 id 列和产品列(现在默认称为 "variable")分组,并创建一个名为 variable2 的新列。这只是类别标题和 运行 观察计数的粘贴。

现在我们有了一个新列,我们可以通过它来展开数据。我们在新的 variable2 列上使用 dcast 去 "wide"。还有一个名为 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 在我们按行输入值时展开 column-wise 的事实。在我们的例子中,写入 1:3 无济于事,因为数据中可能有更多产品。但是我们知道有两个标题"product category"和"sub-subcategory"。我们将 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

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

dplyr & tidyr 的替代方案:

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