使用多个测量变量重新塑造数据框架

22

我正在使用类似于这个的数据框:

df<-data.frame(student=c(rep(1,5),rep(2,5)), month=c(1:5,1:5),  
      quiz1p1=seq(20,20.9,0.1),quiz1p2=seq(30,30.9,0.1),  
      quiz2p1=seq(80,80.9,0.1),quiz2p2=seq(90,90.9,0.1))      

print(df)  

   student month quiz1p1 quiz1p2 quiz2p1 quiz2p2  
1     1     1    20.0    30.0    80.0    90.0  
2     1     2    20.1    30.1    80.1    90.1  
3     1     3    20.2    30.2    80.2    90.2  
4     1     4    20.3    30.3    80.3    90.3
5     1     5    20.4    30.4    80.4    90.4
6     2     1    20.5    30.5    80.5    90.5
7     2     2    20.6    30.6    80.6    90.6
8     2     3    20.7    30.7    80.7    90.7
9     2     4    20.8    30.8    80.8    90.8
10    2     5    20.9    30.9    80.9    90.9

描述五个月内学生获得的成绩-在两次测验中,每次测验分为两个部分。

我需要将这两个测验分别放在不同的行中-以便每个学生在每个月份都有两行,一行代表一个测验,而且每个测验还要分成两列,对应于每个测验部分。 当我使用melt功能来转化表格时:

melt.data.frame(df, c("student", "month"))

我也把测验的两个部分分别放在不同的行里。

dcast(dfL,student+month~variable)

当然,这让我回到了起点,我找不到一种方法将表格转换为所需的形式。 有没有办法使melt命令的功能类似于:

melt.data.frame(df, measure.var1=c("quiz1p1","quiz2p1"), 
                measure.var2=c("quiz1p2","quiz2p2"))  

7
样本数据,清晰问题。+1。对于第一个提问的人来说,做得很好。欢迎来到SO。 - Roman Luštrik
相关问题:https://dev59.com/1l4d5IYBdhLWcg3wDe31 - landroni
3个回答

12

以下是使用R语言基础包中的reshape()函数实现此操作的方法:

df2 <- reshape(df, direction="long",
               idvar = 1:2, varying = list(c(3,5), c(4,6)),
               v.names = c("p1", "p2"), times = c("quiz1", "quiz2"))

## Checking the output    
rbind(head(df2, 3), tail(df2, 3))
#           student month  time   p1   p2
# 1.1.quiz1       1     1 quiz1 20.0 30.0
# 1.2.quiz1       1     2 quiz1 20.1 30.1
# 1.3.quiz1       1     3 quiz1 20.2 30.2
# 2.3.quiz2       2     3 quiz2 80.7 90.7
# 2.4.quiz2       2     4 quiz2 80.8 90.8
# 2.5.quiz2       2     5 quiz2 80.9 90.9

您也可以使用列名(而不是列号)作为idvarvarying的参数。这种方法更冗长,但在我看来似乎更好的实践:

## The same operation as above, using just column *names*
df2 <- reshape(df, direction="long", idvar=c("student", "month"),
               varying = list(c("quiz1p1", "quiz2p1"), 
                              c("quiz1p2", "quiz2p2")), 
               v.names = c("p1", "p2"), times = c("quiz1", "quiz2"))

1
谢谢你的答案。很好地说明了'v.names'和'times'的使用。 - IRTFM
2
@DWin -- 当然没问题。我认为你和我是这些地方使用普通的reshape()的主要支持者。(我想不出任何一个R函数有比它更模糊的手册(或更少的有用示例),所以学习使用它需要一个非常陡峭的学习曲线。) - Josh O'Brien
2
没错。我认为这个问题和答案会是帮助页面示例部分的一个很好的补充。 - IRTFM
每当有人抱怨R语言的帮助页面时,RCore团队总会表示他们欢迎改进。你并不是要求改变base-R,只是想改善它的文档。因此,建议你将改进意见提交到r-devel,并观察反响如何。 - IRTFM
1
@eli-k -- 或许更好的想法是将Hadley的软件包视为更加用户友好而不是更高级。R核心团队包括一些非常出色和经验丰富的程序员,像reshape()这样的函数已经存在了很长时间,因此它已经变得非常成熟。因此,在许多方面,核心R包含了R宇宙中最“先进”的代码。 (顺便说一句,很高兴您欣赏这种替代解决方案。) - Josh O'Brien
显示剩余5条评论

7
我认为这可以满足您的需求:
#Break variable into two columns, one for the quiz and one for the part of the quiz
dfL <- transform(dfL, quiz = substr(variable, 1,5), 
                 part = substr(variable, 6,7))

#Adjust your dcast call:
dcast(dfL, student + month + quiz ~ part)
#-----
   student month  quiz   p1   p2
1        1     1 quiz1 20.0 30.0
2        1     1 quiz2 80.0 90.0
3        1     2 quiz1 20.1 30.1
...
18       2     4 quiz2 80.8 90.8
19       2     5 quiz1 20.9 30.9
20       2     5 quiz2 80.9 90.9

感谢您提供的出色解决方案,@Chase。虽然我通常更喜欢内置的解决方案,但是您的解决方案看起来在更复杂的数据框中需要的代码较少。例如,如果每个测验分为六个部分,我不需要在您的代码中添加任何内容,而我将不得不在重塑函数中编写六对列名称。 - eli-k

3

半年前有一个非常类似的问题(链接),我写了以下函数:

melt.wide = function(data, id.vars, new.names) {
  require(reshape2)
  require(stringr)
  data.melt = melt(data, id.vars=id.vars)
  new.vars = data.frame(do.call(
    rbind, str_extract_all(data.melt$variable, "[0-9]+")))
  names(new.vars) = new.names
  cbind(data.melt, new.vars)
}

您可以使用以下函数将数据进行“融合”:
dfL <-melt.wide(df, id.vars=1:2, new.names=c("Quiz", "Part"))
head(dfL)
#   student month variable value Quiz Part
# 1       1     1  quiz1p1  20.0    1    1
# 2       1     2  quiz1p1  20.1    1    1
# 3       1     3  quiz1p1  20.2    1    1
# 4       1     4  quiz1p1  20.3    1    1
# 5       1     5  quiz1p1  20.4    1    1
# 6       2     1  quiz1p1  20.5    1    1
tail(dfL)
#    student month variable value Quiz Part
# 35       1     5  quiz2p2  90.4    2    2
# 36       2     1  quiz2p2  90.5    2    2
# 37       2     2  quiz2p2  90.6    2    2
# 38       2     3  quiz2p2  90.7    2    2
# 39       2     4  quiz2p2  90.8    2    2
# 40       2     5  quiz2p2  90.9    2    2

一旦数据采用这种形式,你就可以更容易地使用 dcast() 来获得所需的任何形式。例如
head(dcast(dfL, student + month + Quiz ~ Part))
#   student month Quiz    1    2
# 1       1     1    1 20.0 30.0
# 2       1     1    2 80.0 90.0
# 3       1     2    1 20.1 30.1
# 4       1     2    2 80.1 90.1
# 5       1     3    1 20.2 30.2
# 6       1     3    2 80.2 90.2

感谢@mrdwab提出这个解决方案。我花了一些时间才理解它应该如何工作,但现在我明白了,我可以看到你的函数和你对问题的一般处理方法在这种情况下以及其他情况下都很有用。 - eli-k
1
@eli-k,不要忘记对于大多数函数,你可以在控制台上简单地写出函数名(例如 > reshape),以查看支持它们的代码。然后,您可以运行函数的不同部分,查看每个步骤所做的工作。这是学习一些有趣编码技巧的有用方式。 - A5C1D2H2I1M1N2O1R2T1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接