【问题标题】:Reshape data from long to a short format by a variable, and rename columns通过变量将数据从长格式重塑为短格式,并重命名列
【发布时间】:2013-04-08 14:39:44
【问题描述】:

如何更优雅地使用plyrreshape2aggregate 函数和/或data.table

library(plyr)

set.seed(1) 
x <- data.frame(Ind = paste0("Ind", 1:10), Treatment = c(rep("Treat",10),rep("Cont",10)),
value = rnorm(20,60,8))

tr <- subset(x, Treatment == "Treat")
tr <- rename(tr, c("value" = "Treat"))

ct <- subset(x, Treatment == "Cont")
ct <- rename(ct, c("value" = "Cont"))

merge(ct[-2], tr[-2], by = "Ind", all = T, sort = F)

# Do not run, data.frame:
     Ind     Cont    Treat
1   Ind1 72.09425 54.98837
2   Ind2 63.11875 61.46915
3   Ind3 55.03008 53.31497
4   Ind4 42.28240 72.76225
5   Ind5 68.99945 62.63606
6   Ind6 59.64053 53.43625
7   Ind7 59.87048 63.89943
8   Ind8 67.55069 65.90660
9   Ind9 66.56977 64.60625
10 Ind10 64.75121 57.55689

【问题讨论】:

  • 这也可能与plyr 有关吗?那和data.table 是我用得最少的软件包,但对于这类问题似乎非常有用的全能工具。
  • 我已经用 plyr 解决方案更新了我的答案,但对于这个特定问题,我不确定 plyr 是最好的“工具”。
  • 感谢大家提供各种令人费解的解决方案。这正是我想要的 =) 现在我可以回顾这篇文章并找到一种最适合特定数据框重塑挑战的方法。我将@AnandaMahto 的答案勾选为“已接受”,因为它有最多的选择。但是,对于这种特殊情况,@DidzisElferts 使用 reshape2 包的解决方案可能是我最喜欢的。 @Arun 的 data.table 解决方案也值得仔细研究。

标签: r aggregate data.table plyr reshape


【解决方案1】:

要添加到您的选项中...

这是我们的起始数据:

set.seed(1) # Nice for reproducible examples
x <- data.frame(Ind = paste0("Ind", 1:10), 
                Treatment = c(rep("Treat",10),rep("Cont",10)),
                value = rnorm(20,60,8))

xtabs

请注意,输出是matrix,而不是data.frame

xtabs(value ~ Ind + Treatment, x)
#        Treatment
# Ind         Cont    Treat
#   Ind1  72.09425 54.98837
#   Ind10 64.75121 57.55689
#   Ind2  63.11875 61.46915
#   Ind3  55.03008 53.31497
#   Ind4  42.28240 72.76225
#   Ind5  68.99945 62.63606
#   Ind6  59.64053 53.43625
#   Ind7  59.87048 63.89943
#   Ind8  67.55069 65.90660
#   Ind9  66.56977 64.60625

reshape

reshape(x, direction = "wide", idvar="Ind", timevar="Treatment")
#      Ind value.Treat value.Cont
# 1   Ind1    54.98837   72.09425
# 2   Ind2    61.46915   63.11875
# 3   Ind3    53.31497   55.03008
# 4   Ind4    72.76225   42.28240
# 5   Ind5    62.63606   68.99945
# 6   Ind6    53.43625   59.64053
# 7   Ind7    63.89943   59.87048
# 8   Ind8    65.90660   67.55069
# 9   Ind9    64.60625   66.56977
# 10 Ind10    57.55689   64.75121

如果您想同时使用reshape 选项更改名称:

setNames(reshape(x, direction = "wide", idvar="Ind", timevar="Treatment"), 
         c("Ind", "Treat", "Cont"))

split + merge

同样,setNames 可以在这里使用,或者您可以在之后重命名列。

temp <- split(x[-2], x$Treatment)
merge(temp[[1]], temp[[2]], by = "Ind", suffixes = names(temp))
#      Ind valueCont valueTreat
# 1   Ind1  72.09425   54.98837
# 2  Ind10  64.75121   57.55689
# 3   Ind2  63.11875   61.46915
# 4   Ind3  55.03008   53.31497
# 5   Ind4  42.28240   72.76225
# 6   Ind5  68.99945   62.63606
# 7   Ind6  59.64053   53.43625
# 8   Ind7  59.87048   63.89943
# 9   Ind8  67.55069   65.90660
# 10  Ind9  66.56977   64.60625

ddply 来自plry

(我不是普通的“plyr”用户,所以完全不确定这是否是最好的方法)。

library(plyr)
ddply(x, .(Ind), summarize, 
      Treat = value[Treatment == "Treat"], 
      Cont = value[Treatment == "Cont"])
#      Ind    Treat     Cont
# 1   Ind1 54.98837 72.09425
# 2  Ind10 57.55689 64.75121
# 3   Ind2 61.46915 63.11875
# 4   Ind3 53.31497 55.03008
# 5   Ind4 72.76225 42.28240
# 6   Ind5 62.63606 68.99945
# 7   Ind6 53.43625 59.64053
# 8   Ind7 63.89943 59.87048
# 9   Ind8 65.90660 67.55069
# 10  Ind9 64.60625 66.56977

unstack(好像选项还不够!)

unique(data.frame(x[1], unstack(x, value ~ Treatment)))
#      Ind     Cont    Treat
# 1   Ind1 72.09425 54.98837
# 2   Ind2 63.11875 61.46915
# 3   Ind3 55.03008 53.31497
# 4   Ind4 42.28240 72.76225
# 5   Ind5 68.99945 62.63606
# 6   Ind6 59.64053 53.43625
# 7   Ind7 59.87048 63.89943
# 8   Ind8 67.55069 65.90660
# 9   Ind9 66.56977 64.60625
# 10 Ind10 64.75121 57.55689

【讨论】:

  • 我认为您在 ddply 解决方案中不需要 as.vector,但我同意这不是这项工作的最佳工具。
  • @hadley,你是对的(当然)。谢谢!相应地进行了编辑。
【解决方案2】:

这是data.table的方式:

x.dt <- as.data.table(x)
setkey(x.dt, "Ind")
x.dt[, setattr(as.list(value), 'names', c("Treat", "Cont")),by=Ind]
#       Ind    Treat     Cont
#  1:  Ind1 57.73997 54.06263
#  2: Ind10 64.23664 65.98024
#  3:  Ind2 58.71422 58.01650
#  4:  Ind3 52.71239 62.64899
#  5:  Ind4 65.09401 75.51550
#  6:  Ind5 47.04052 61.80900
#  7:  Ind6 61.95129 55.20021
#  8:  Ind7 58.02494 55.41143
#  9:  Ind8 69.38424 57.71132
# 10:  Ind9 62.02491 57.06147

【讨论】:

    【解决方案3】:

    您可以使用库 reshape2 中的函数 dcast()

     dcast(data=x,Ind~Treatment)
         Ind     Cont    Treat
    1   Ind1 53.45988 53.68913
    2  Ind10 54.02344 66.32866
    3   Ind2 57.44591 62.32354
    4   Ind3 67.53185 53.14807
    5   Ind4 52.42713 55.04052
    6   Ind5 63.80633 61.58893
    7   Ind6 59.40308 51.66228
    8   Ind7 67.79597 73.60620
    9   Ind8 58.15420 65.06976
    10  Ind9 61.45161 63.73947
    

    【讨论】:

    • 这很简单......我看到我过度简化了我的问题,因此是一个相当基本的问题。此外,应该注意dcast 似乎仅在列被命名为“值”时才选择值。否则,您必须指定列:dcast(data=x,Ind~Treatment, value.var = "name of the column")
    • @Largh,你为什么不继续用你真正想要解决的问题更新你的问题?
    • @AnandaMahto,因为我认为这对于为问题提供出色解决方案的人来说是不公平的。我当然可以添加一个编辑来扩展问题。
    • @Largh,您的来电。对我来说,这个问题在 SO 方面仍然“年轻”,所以更新没有坏处。
    • @Largh 如果值的列名不同(只是尝试使用其他名称),这也有效。如果有多个值列,则必须添加 value.var="column name"
    猜你喜欢
    • 2017-10-13
    • 1970-01-01
    • 2019-08-13
    • 1970-01-01
    • 2017-03-01
    • 2015-09-19
    • 1970-01-01
    • 2022-01-11
    相关资源
    最近更新 更多