使用merged.stack(或reshape)从宽表创建长表

时间:2013-10-17 22:08:44

标签: r dataframe reshape splitstackshape

我有一个如下所示的数据框:

ID rd_test_2011 rd_score_2011 mt_test_2011 mt_score_2011 rd_test_2012 rd_score_2012 mt_test_2012 mt_score_2012
1  A            80            XX           100           NA           NA            BB           45 
2  XX           90            NA           NA            AA           80            XX           80

我想编写一个脚本,对于在yy_test_20xx列中没有NA的ID,创建一个新的数据框,主题取自列标题,测试名称,测试分数和取自的年份列标题。因此,在此示例中,ID 1将具有三个条目。预期的输出将如下所示:

ID   Subject    Test        Score        Year
1    rd         A           80           2011
1    mt         XX          100          2012
1    mt         BB          45           2012
2    rd         XX          90           2011
2    rd         AA          80           2012
2    mt         XX          80           2012

我已经尝试过两种重塑和各种形式的merged.stack,它的工作方式是我得到一个正确的输出路径,但我无法理解输入到足以一路到达那里:

library(splitstackshape)
merged.stack(x, id.vars='id', var.stubs=c("rd_test","mt_test"), sep="_")

我在重塑方面取得了更大的成功(更接近):

y<- reshape(x, idvar="id", ids=1:nrow(x), times=grep("test", names(x), value=TRUE), 
      timevar="year", varying=list(grep("test", names(x), value=TRUE), grep("score",
      names(x), value=TRUE)), direction="long", v.names=c("test", "score"),
      new.row.names=NULL) 

3 个答案:

答案 0 :(得分:2)

使用reshape

 dat.long <- reshape(dat, direction="long",  varying=list(c(2, 4,6), c(3, 5,7)), 
                       times=2011:2013,timevar='Year',
                       sep="_", v.names=c("Test", "Score"))


dat.long[complete.cases(dat.long),]

      ID Year Test Score id
1.2011  1 2011    A    80  1
2.2011  2 2011   XX    90  2
4.2011  4 2011    A    50  4
5.2011  5 2011    C    50  5
1.2012  1 2012   XX   100  1
3.2012  3 2012    A    10  3
4.2012  4 2012   XX    60  4
5.2012  5 2012    A    75  5
2.2013  2 2013   AA    80  2
4.2013  4 2013   AA    99  4

答案 1 :(得分:2)

这将使您的数据格式正确:

df.long = reshape(df, idvar="ID", ids=1:nrow(df), times=grep("Test", names(df), value=TRUE),
 timevar="Year", varying=list(grep("Test", names(df), value=TRUE), 
grep("Score", names(df), value=TRUE)), direction="long", v.names=c("Test", "Score"),
new.row.names=NULL) 

然后省略NA

df.long = df.long[!is.na(df.long$Test),]

然后拆分Year以删除Test_

df.long$Year = sapply(strsplit(df.long$Year, "_"), `[`, 2)

ID排序:

df.long[order(df.long$ID),]

   ID Year Test Score
1   1 2011    A    80
5   1 2012   XX   100
2   2 2011   XX    90
9   2 2013   AA    80
6   3 2012    A    10
3   4 2011    A    50
7   4 2012   XX    60
10  4 2013   AA    99
4   5 2011    C    50
8   5 2012    A    75

答案 2 :(得分:1)

考虑到您的更新,我完全重写了这个答案。如果要查看旧版本,请查看历史记录。

主要问题是您的数据在某种程度上是“双倍宽”的。因此,您实际上可以通过两次“长”方向重塑来解决您的问题。或者,使用melt*cast以非常长的格式融化数据并将其转换为半宽格式。

但是,我仍然建议“splitstackshape”(而不仅仅是因为我写了它)。它可以很好地处理这个问题,但它需要您重新安排数据的names。将首先出现将导致新列名称的名称部分。在您的示例中,这意味着“test”和“score”应该是变量名称的第一部分。

为此,我们可以使用一些gsub重新排列现有名称。

library(splitstackshape)
setnames(mydf, gsub("(rd|mt)_(score|test)_(.*)", "\\2_\\1_\\3", names(mydf)))
names(mydf)
# [1] "ID"            "test_rd_2011"  "score_rd_2011" "test_mt_2011" 
# [5] "score_mt_2011" "test_rd_2012"  "score_rd_2012" "test_mt_2012" 
# [9] "score_mt_2012"
out <- merged.stack(mydf, "ID", var.stubs=c("test", "score"), sep="_")
setnames(out, c(".time_1", ".time_2"), c("Subject", "Year"))
out[complete.cases(out), ]
#    ID Subject Year test score
# 1:  1      mt 2011   XX   100
# 2:  1      mt 2012   BB    45
# 3:  1      rd 2011    A    80
# 4:  2      mt 2012   XX    80
# 5:  2      rd 2011   XX    90
# 6:  2      rd 2012   AA    80

为了他人的利益,这个答案中的“mydf”定义为:

mydf <- structure(list(ID = 1:2, rd_test_2011 = c("A", "XX"), 
    rd_score_2011 = c(80L, 90L), mt_test_2011 = c("XX", NA), 
    mt_score_2011 = c(100L, NA), rd_test_2012 = c(NA, "AA"), 
    rd_score_2012 = c(NA, 80L), mt_test_2012 = c("BB", "XX"), 
    mt_score_2012 = c(45L, 80L)), 
    .Names = c("ID", "rd_test_2011", "rd_score_2011", "mt_test_2011", 
    "mt_score_2011", "rd_test_2012", "rd_score_2012", "mt_test_2012", 
    "mt_score_2012"), class = "data.frame", row.names = c(NA, -2L))