【问题标题】:Reshape wide to long, preserving ID variable from rownames将宽改成长,保留行名中的 ID 变量
【发布时间】:2018-08-10 22:44:18
【问题描述】:

我正在尝试将以下数据转换为 R 中的长格式:

testdata <- data.frame(rnorm(10),rnorm(10),rnorm(10))
rownames(testdata) <- paste0("ID",1:10) # Ids
colnames(testdata) <- c(2001,2002,2003) # Years
testdata

所以列 = 时间,行 = ID。应该不会太难,但在所有示例中,我发现情况正好相反。这如何在datatablereshape 或任何其他流行的数据框包中完成? 感谢您的任何提示。通过转置我的数据,我知道一种方法,但这似乎是一种相当低效的方法。

【问题讨论】:

  • 尝试melt melt(as.matrix(testdata))tidyverse rownames_to_column(testdata, 'rn') %&gt;% gather(key, val, -rn)
  • @akrun 谢谢,例如melt(as.matrix(testdata)) 有效。但是为什么我需要转换成矩阵呢?对我来说似乎也有点低效,因为显然我必须立即重新转换为数据表。但如果这是要走的路,我会做的。
  • 因为它会将行名作为列删除。如果效率较高,请在执行melt 之前创建一列行名。即setDT(testdata, keep.rownames = TRUE),然后使用melt
  • 在基础 R 中,您可以使用:data.frame(as.table(as.matrix(testdata))) 甚至 cbind(ID=rownames(testdata),stack(testdata))

标签: r reshape reshape2 rowname


【解决方案1】:

@Akron 的有用回答:

reshape2::melt(as.matrix(testdata))

“但是为什么?”部分:

您的行名中有重要信息,这不是通常是存储重要信息的好地方。我们在重塑时需要这些信息。那么问题就变成了,如果我们输入矩阵而不是数据框,为什么melt 会使用该信息?

原因是melt 是一个通用函数,它根据您输入的数据类型分派一个方法(也称为更具体的函数)。所以如果m是一个矩阵并且你调用melt(m),那么R实际上是在执行melt.matrix(m)。相反,如果df 是一个数据帧并且你调用melt(df),那么R 实际上正在执行melt.data.frame(df)。这两个函数——melt.matrix()melt.data.frame()——以不同的方式处理行名;方法melt.matrix 以您想要的方式使用这些行名,而方法melt.data.frame 没有。因此,为了获得所需的输出,您需要将矩阵(而不是数据框)输入到melt

只是为了证明,如果我们将 ID 信息存储在 data.frame 的列中(如下面的testdata2)而不是行名(如上面的testdata),那么我们很适合- 将数据框输入到melt

testdata2 <- data.frame(
    ID       =  1:10,
    year2001 = rnorm(10),
    year2002 = rnorm(10),
    year2003 = rnorm(10) )

reshape2::melt(testdata2, "ID")
reshape2::melt(testdata2, id.vars="ID", measure.vars=2:4) #equivalently, but verbosely

【讨论】:

  • 谢谢你的回答。我不明白为什么行名没有资格存储重要信息。因为如果它们不存在,它们为什么还会存在?
  • R 自 1970 年代以来一直在不断发展,因此“为什么会存在某某”通常是一个冗长而详细的故事,几乎总是以“我们今天会改变它,但为了向后兼容的原因,我们不会。”
  • 关于行名,我支持 Hadley Wickham,他认为“通常最好避免行名,因为它们基本上是一个字符列,与其他列具有不同的语义。”但是,请参阅 this post 了解相反的观点。
【解决方案2】:

只是为了把akrun's comment变成一个完整的答案:

library(data.table)
melt(setDT(testdata, keep.rownames = TRUE), "rn")
      rn variable       value
 1:  ID1     2001 -0.25265860
 2:  ID2     2001  0.50538399
 3:  ID3     2001  0.68216394
 4:  ID4     2001  0.62203871
 5:  ID5     2001  0.59297019
 6:  ID6     2001  0.69383842
 7:  ID7     2001  1.77900432
 8:  ID8     2001 -1.69010623
 9:  ID9     2001 -2.17762905
10: ID10     2001  0.61463127
11:  ID1     2002  0.42120060
12:  ID2     2002 -0.16148732
...

【讨论】:

  • 谢谢,这在上面的 cmets 中是最快的。我相信缺少的是将生成的 DT 的密钥设置为rn,variable,因为这是通常需要的格式
【解决方案3】:

对我来说明显的 hack 似乎是将数据框的行名扩充回常规列;那么你可以使用reshape/reshape2/tidyr::gather中的任何一个

> data.frame(ID=rownames(testdata), testdata, row.names=NULL)
     ID      X2001      X2002       X2003
1   ID1  0.6714540  1.1516917  0.51332801
2   ID2 -1.7309721 -1.8018835  1.54385452
3   ID3 -0.4831349 -1.3965915 -0.72819988
4   ID4  1.2591651  1.2436120  1.01472455
5   ID5  1.2346326 -1.4587475 -1.75097483
6   ID6  0.4279562  0.2595588  1.36560258
7   ID7  0.9990642 -1.0306002 -1.10165672
8   ID8  1.2118510 -0.3577358 -0.11696953
9   ID9  0.3074985  0.5177188 -0.09954961
10 ID10 -1.0418608 -1.8419336 -0.65401215

(请注意,它已将您的非法列名“修复”为 'X2001'、'X2002'...如果您真的想保留它们,请使用 ...check.names=FALSE)

【讨论】:

    【解决方案4】:

    你可以使用 tidyr 库

    library(tidyr)
    cbind(paste0("ID",1:10), gather(testdata, "years", "value"))
    

    【讨论】:

    • 这会从行名中丢弃 ID 变量,这是 OP 的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-13
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    相关资源
    最近更新 更多