R:从长格式表到“对角线”格式

时间:2016-06-06 12:22:24

标签: r format

想象一下,我有这样的长格式数据:

ID   T     X      Y     Z
1    1     x1    y1    z1
1    2     x2    y2    z2
1    3     x3    y3    z3
2    1     ....

有几个输入变量(这里只有X和Y)和输出变量(这里是Z) 我只展示了第一行,属于和个人1,但我们会在这之下有更多的数据。

如何将其转换为以下格式:

1    1     x1                y1                   z1
1    2     x1    x2          y1    y2             z2
1    3     x1    x2    x3    y1    y2    y3       z3 
2    1     ...             .... 

缺失的空间是NAs o。
任何带有data.table,dplyr或base R的解决方案都是受欢迎的。

我这样做的原因是因为我想要这样的回归:

y1 = a10 + a11·X1
y2 = a20 + a21·X1 + a22·X2
y3 = a30 + a31·X1 + a32·X2 + a33·X3

如果您想要一个可重复的示例:

set.seed(1)
ID <- rep(1:4,each=4)
XX <- round(runif(16),3)
YY <- round(runif(16),3)
TT <- rep(1:4, 4)
ZZ <- ave(XX*TT,ID, FUN = cumsum)
data.frame(ID,TT,XX, YY, ZZ)

   ID TT    XX    YY    ZZ
1   1  1 0.266 0.718 0.266
2   1  2 0.372 0.992 1.010
3   1  3 0.573 0.380 2.729
4   1  4 0.908 0.777 6.361
5   2  1 0.202 0.935 0.202
6   2  2 0.898 0.212 1.998
7   2  3 0.945 0.652 4.833
8   2  4 0.661 0.126 7.477
9   3  1 0.629 0.267 0.629
10  3  2 0.062 0.386 0.753
11  3  3 0.206 0.013 1.371
12  3  4 0.177 0.382 2.079
13  4  1 0.687 0.870 0.687
14  4  2 0.384 0.340 1.455
15  4  3 0.770 0.482 3.765
16  4  4 0.498 0.600 5.757

如果我想要结果怎么办?

1    1     x1                y1                   z1
1    2     x2    x1          y2    y1             z2
1    3     x3    x2    x1    y3    y2    y1       z3 
2    1     ...             .... 

3 个答案:

答案 0 :(得分:3)

创建一个函数,该函数创建一个用值逐行填充的矩阵,并将上三角形设置为零:

fun <- function(x) {
  m <- matrix(x, length(x), length(x), byrow = TRUE)
  m[upper.tri(m)] <- 0
  as.data.frame(m)
}

#test it
fun(1:4)
#  V1 V2 V3 V4
#1  1  0  0  0
#2  1  2  0  0
#3  1  2  3  0
#4  1  2  3  4

library(data.table) #for its by
setDT(DF)
DF[, paste0("x", 1:4) := fun(XX), by = ID]
DF[, paste0("y", 1:4) := fun(YY), by = ID]
#    ID TT    XX    YY    ZZ    x1    x2    x3    x4    y1    y2    y3    y4
# 1:  1  1 0.266 0.718 0.266 0.266 0.000 0.000 0.000 0.718 0.000 0.000 0.000
# 2:  1  2 0.372 0.992 1.010 0.266 0.372 0.000 0.000 0.718 0.992 0.000 0.000
# 3:  1  3 0.573 0.380 2.729 0.266 0.372 0.573 0.000 0.718 0.992 0.380 0.000
# 4:  1  4 0.908 0.777 6.361 0.266 0.372 0.573 0.908 0.718 0.992 0.380 0.777
# 5:  2  1 0.202 0.935 0.202 0.202 0.000 0.000 0.000 0.935 0.000 0.000 0.000
# 6:  2  2 0.898 0.212 1.998 0.202 0.898 0.000 0.000 0.935 0.212 0.000 0.000
# 7:  2  3 0.945 0.652 4.833 0.202 0.898 0.945 0.000 0.935 0.212 0.652 0.000
# 8:  2  4 0.661 0.126 7.477 0.202 0.898 0.945 0.661 0.935 0.212 0.652 0.126
# 9:  3  1 0.629 0.267 0.629 0.629 0.000 0.000 0.000 0.267 0.000 0.000 0.000
#10:  3  2 0.062 0.386 0.753 0.629 0.062 0.000 0.000 0.267 0.386 0.000 0.000
#11:  3  3 0.206 0.013 1.371 0.629 0.062 0.206 0.000 0.267 0.386 0.013 0.000
#12:  3  4 0.177 0.382 2.079 0.629 0.062 0.206 0.177 0.267 0.386 0.013 0.382
#13:  4  1 0.687 0.870 0.687 0.687 0.000 0.000 0.000 0.870 0.000 0.000 0.000
#14:  4  2 0.384 0.340 1.455 0.687 0.384 0.000 0.000 0.870 0.340 0.000 0.000
#15:  4  3 0.770 0.482 3.765 0.687 0.384 0.770 0.000 0.870 0.340 0.482 0.000
#16:  4  4 0.498 0.600 5.757 0.687 0.384 0.770 0.498 0.870 0.340 0.482 0.600

对于其他结果,您可以使用shift

DF[, paste0("x", 1:4) := shift(XX, 0:3, fill = 0), by = ID]
#    ID TT    XX    YY    ZZ    x1    x2    x3    x4
# 1:  1  1 0.266 0.718 0.266 0.266 0.000 0.000 0.000
# 2:  1  2 0.372 0.992 1.010 0.372 0.266 0.000 0.000
# 3:  1  3 0.573 0.380 2.729 0.573 0.372 0.266 0.000
# 4:  1  4 0.908 0.777 6.361 0.908 0.573 0.372 0.266
# 5:  2  1 0.202 0.935 0.202 0.202 0.000 0.000 0.000
# 6:  2  2 0.898 0.212 1.998 0.898 0.202 0.000 0.000
# 7:  2  3 0.945 0.652 4.833 0.945 0.898 0.202 0.000
# 8:  2  4 0.661 0.126 7.477 0.661 0.945 0.898 0.202
# 9:  3  1 0.629 0.267 0.629 0.629 0.000 0.000 0.000
#10:  3  2 0.062 0.386 0.753 0.062 0.629 0.000 0.000
#11:  3  3 0.206 0.013 1.371 0.206 0.062 0.629 0.000
#12:  3  4 0.177 0.382 2.079 0.177 0.206 0.062 0.629
#13:  4  1 0.687 0.870 0.687 0.687 0.000 0.000 0.000
#14:  4  2 0.384 0.340 1.455 0.384 0.687 0.000 0.000
#15:  4  3 0.770 0.482 3.765 0.770 0.384 0.687 0.000
#16:  4  4 0.498 0.600 5.757 0.498 0.770 0.384 0.687

但是,我得到的印象是我们在这里有一个XY problem。如果你解释了你的实际目标(在一个新的问题中,有一个可重复的例子),可能会提出更好的方法。

答案 1 :(得分:2)

罗兰的答案很好。您还可以使用dcastcumsum

这听起来很复杂,但它实际上更简单,因为它不需要多次调用三角函数,所以它更容易扩展到任意数量的输入变量。:

df <- data.frame(ID, TT, XX, YY, ZZ)
library(data.table)
setDT(df)

input_vars <- c("XX","YY")

dt2 <- dcast(df, ID + TT + ZZ ~ TT, value.var=input_vars, fill=0)
head(dt2)
#    ID TT    ZZ  XX_1  XX_2  XX_3  XX_4  YY_1  YY_2 YY_3  YY_4
# 1:  1  1 0.266 0.266 0.000 0.000 0.000 0.718 0.000 0.00 0.000
# 2:  1  2 1.010 0.000 0.372 0.000 0.000 0.000 0.992 0.00 0.000
# 3:  1  3 2.729 0.000 0.000 0.573 0.000 0.000 0.000 0.38 0.000
# 4:  1  4 6.361 0.000 0.000 0.000 0.908 0.000 0.000 0.00 0.777
# 5:  2  1 0.202 0.202 0.000 0.000 0.000 0.935 0.000 0.00 0.000
# 6:  2  2 1.998 0.000 0.898 0.000 0.000 0.000 0.212 0.00 0.000

# now we just need to get rid of the trailing 0's
# This can be done with a cumulative sum with a keyby ID
cum_cols <- setdiff(names(dt2), c("ID","TT","ZZ"))

dt2[, (cum_cols) := lapply(.SD, cumsum), 
    .SDcols = cum_cols, keyby = ID]

head(dt2)
# looks like:
#    ID TT    ZZ  XX_1  XX_2  XX_3  XX_4  YY_1  YY_2 YY_3  YY_4
# 1:  1  1 0.266 0.266 0.000 0.000 0.000 0.718 0.000 0.00 0.000
# 2:  1  2 1.010 0.266 0.372 0.000 0.000 0.718 0.992 0.00 0.000
# 3:  1  3 2.729 0.266 0.372 0.573 0.000 0.718 0.992 0.38 0.000
# 4:  1  4 6.361 0.266 0.372 0.573 0.908 0.718 0.992 0.38 0.777
# 5:  2  1 0.202 0.202 0.000 0.000 0.000 0.935 0.000 0.00 0.000
# 6:  2  2 1.998 0.202 0.898 0.000 0.000 0.935 0.212 0.00 0.000

答案 2 :(得分:1)

如果有人感兴趣的话,我的拉斯问题得到了我自己的答案,基础R。

ave(df[rep(names(df[3:5]),each=4)], df$ID, FUN=function(x) mapply(
 function(y,z) c(rep(0,z),head(y,length(y)-z)),x,rep(0:3,3)))