重塑 R 中的数据(宽 -> 长)

Reshaping data in R (wide -> Long)

我想将 df1 转换为 df2

旧样本数据框df1

df1 <- structure(list(ID = 1:2,                Group = c(1L, 1L),
                      M1a2hB = c(0.2, 0.3),    M1a3hB = c(0.4, 0.6),
                      M2a2hB = c(0.3, 0.4),    M2a3hB = c(0.6, 0.6),
                      M1r2hB = c(200L, 300L),  M1r3hB = c(400L, 600L),
                      M2r2hB = c(300L, 400L),  M2r3hB = c(600L, 600L)),
                 .Names = c("ID", "Group", "M1a2hB", "M1a3hB", "M2a2hB",
                            "M2a3hB","M1r2hB", "M1r3hB","M2r2hB", "M2r3hB"),
                 class = "data.frame", row.names = c(NA, -2L))

ID Group M1a2hB M1a3hB M2a2hB M2a3hB.... M1r2hB M1r3hB M2r2hB M2r3hB ...
1   1      0.2  0.4    0.3   0.6    ...     200    400   300    600    ...
2   1      0.3  0.6    0.4   0.6    ...     300    600   400    600    ...

此处,df1 有 100 个 ID 和 1100 个列。每个结果 measure 都有两列用于绝对变化,两列用于相对变化。有将近 270 个结果 m 措施。

M1a2hB 是第一次测量从时间 2 到基线的绝对变化,M1a3hB 是时间 3 到基线的绝对变化。类似地,M1r2hB 是第一个结果从时间 2 到基线的相对变化,M1r3hB 是结果从时间 3 到基线的相对变化。

df2:

ID Group time  M1a           M2a        ...  M1r           M2r        ...
1  1     1     0.0           0.0        ...  000           000         ...
1  1     2     0.2           0.3        ...  200           300         ...
1  1     3     0.4           0.6        ...  400           600         ...
2  1     1     0.0           0.0        ...  000           000         ...
2  1     2     0.3           0.4        ...  300           400         ...
2  1     3     0.6           0.6        ...  600           600         ...

有什么建议吗?随时要求任何澄清。谢谢!期待!

p.s。我已经尝试 运行 以前帖子中的一些代码(如果有兴趣请看下面),但它们看起来不同,因为 df 是三维数据,而 df2 包含额外的时间列

In R, plotting wide form data with ggplot2 or base plot. Is there a way to use ggplot2 without melting wide form data frame?

Reshaping repeated measures data in R wide to long

我们可以使用 sub 从列名称中提取模式,split 使用 'nm1' 该向量的序列,将其用作 measure in [=15] =] 从 'wide' 转换为 'long' 格式。

library(data.table)
nm1 <- sub("\d+[[:alpha:]]+$", '', names(df1)[-(1:2)])
lst <- split(seq_along(nm1)+2, nm1)
melt(setDT(df1), measure = lst, 
       value.name= names(lst), variable.name= 'time')[order(ID)]
#   ID Group time M1a M1r M2a M2r
#1:  1     1    1 0.2 200 0.3 300
#2:  1     1    2 0.4 400 0.6 600
#3:  2     1    1 0.3 300 0.4 400
#4:  2     1    2 0.6 600 0.6 600

数据

df1 <- structure(list(ID = 1:2, Group = c(1L, 1L),
  M1a2hB = c(0.2, 0.3
), M1a3hB = c(0.4, 0.6), M2a2hB = c(0.3, 0.4),
 M2a3hB = c(0.6, 
0.6), M1r2hB = c(200L, 300L), M1r3hB = c(400L, 600L), 
M2r2hB = c(300L, 
400L), M2r3hB = c(600L, 600L)), .Names = c("ID", "Group", "M1a2hB", 
"M1a3hB", "M2a2hB", "M2a3hB", "M1r2hB", "M1r3hB",
"M2r2hB", "M2r3hB"
), class = "data.frame", row.names = c(NA, -2L))

这里是使用 tidyr 的答案:

library(dplyr)
library(tidyr)
library(rex)

string_interpretation = 
  rex(capture("M", 
              digits, 
              or("a", "r")), 
      capture(digits))

result = 
  df1 %>%
  gather(string, value, -ID, -Group) %>%
  extract(string, c("variable", "time"), string_interpretation) %>%
  spread(variable, value)

内置 base::reshape 可以很好地做到这一点:

df1 <- structure(list(ID = 1:2,                Group = c(1L, 1L),
                      M1a2hB = c(0.2, 0.3),    M1a3hB = c(0.4, 0.6),
                      M2a2hB = c(0.3, 0.4),    M2a3hB = c(0.6, 0.6),
                      M1r2hB = c(200L, 300L),  M1r3hB = c(400L, 600L),
                      M2r2hB = c(300L, 400L),  M2r3hB = c(600L, 600L)),
                 .Names = c("ID", "Group", "M1a2hB", "M1a3hB", "M2a2hB",
                            "M2a3hB","M1r2hB", "M1r3hB","M2r2hB", "M2r3hB"),
                 class = "data.frame", row.names = c(NA, -2L))

df1

#  ID Group M1a2hB M1a3hB M2a2hB M2a3hB M1r2hB M1r3hB M2r2hB M2r3hB
#   1     1    0.2    0.4    0.3    0.6    200    400    300    600
#   2     1    0.3    0.6    0.4    0.6    300    600    400    600

df2 <- reshape(df1, varying=list(c(3,4),c(5,6),c(7,8),c(9,10)),
        v.names=c("M1a", "M2a", "M1r", "M2r"),
        timevar="time", times=2:3, direction="long")

df2

#   ID Group time M1a M2a M1r M2r id
#    1     1    2 0.2 0.3 200 300  1
#    2     1    2 0.3 0.4 300 400  2
#    1     1    3 0.4 0.6 400 600  1
#    2     1    3 0.6 0.6 600 600  2

如果您在 m <- 2 个时间点(2 小时、3 小时)有 n <- 270 个测量值,请将 reshape 的参数更改为

varying=split(1:(n*m*2)+2,rep(1:(n*2), each=m))  # `*2` accounts for doubling by relative and absolute measurements.
                                                 # `+2` accounts for the `ID` and `Group` columns at the beginning 

v.names=c(paste0("M", 1:n, "a"), paste0("M", 1:n, "r"))

我假设 time==1 在您的示例中 df2 指的是基线测量值,而不是未提及的 1h,因为它们似乎全为零。为清楚起见,我将基线显示为 time==0使基线显示在 df2 中的一种 方法是将零值基线测量值添加到 df1

n <- 2  # use n <- 270 for 270 outcomes, measured at each time point, reported both in absolute and relative terms

df1.5 <- data.frame(df1,
    setNames(as.list(rep(0,2*n)), c(paste0("M", 1:n, "a0hB"), paste0("M", 1:n, "r0hB"))))

df2 <- reshape(df1.5, varying=split(1:(n*3*2)+2, c(rep(1:(n*2), each=2), 1:(n*2))),
        v.names=c(paste0("M", 1:n, "a"), paste0("M", 1:n, "r")),
        timevar="time", idvar=c("Group", "ID"), times=c(2,3,0), direction="long")

#  ID Group time M1a M2a M1r M2r
#   1     1    2 0.2 0.3 200 300
#   2     1    2 0.3 0.4 300 400
#   1     1    3 0.4 0.6 400 600
#   2     1    3 0.6 0.6 600 600
#   1     1    0 0.0 0.0   0   0
#   2     1    0 0.0 0.0   0   0

然后排序。

df2.sorted <- df2[order(df2$Group, df2$ID, df2$time),]

你可以用我的r包onetree,已经上传到我的github yikeshu0611.

install.packages("devtools") #if you didnot have devtools packages in r
library(devtools)
install_github("yikeshu0611/onetree") #install onetree package from github

1。一步一步

首先教大家一步步把宽转长。

library(onetree)
long1=reshape_toLong(data=df1, 
                      id= "ID", 
                      j="newcolumn", 
       value.var.prefix=c("M1a","M2a","M1r","M2r")

在此命令中,j 是新列的名称。 你会得到 long1 下面的结果

long1

ID Group newcolumn M1a M2a M1r M2r
1     1       2hB 0.2 0.3 200 300
1     1       3hB 0.4 0.6 400 600
2     1       2hB 0.3 0.4 300 400
2     1       3hB 0.6 0.6 600 600

另外,我们在数据中可以看到long1,M1a,M2a------,M1r,M2r-----。该数据仍然是宽数据。我们仍然可以将它转换为 long。我们使用 M1、M2 作为前缀。 a 和 r 作为新列,这是测试方式。命令如下。

long2=reshape_toLong(data = long1,
                       id = c("ID","newcolumn"),
                        j = "testway",
        value.var.prefix = c("M1","M2"))
long2
   ID newcolumn Group testway    M1    M2
1  1       2hB     1       a   0.2   0.3
2  1       2hB     1       r 200.0 300.0
3  1       3hB     1       a   0.4   0.6
4  1       3hB     1       r 400.0 600.0
5  2       2hB     1       a   0.3   0.4
6  2       2hB     1       r 300.0 400.0
7  2       3hB     1       a   0.6   0.6
8  2       3hB     1       r 600.0 600.0

这里,我们使用两个变量ID和newcolumn作为id对象。因为在长数据中,id被当作一个唯一的变量,如果我们只用id,就会出现mismatch。您也可以创建一个新 ID,例如:idnew.

long1$idnew = 1:nrow(long1)
reshape_toLong(data = long1,
                 id = "idnew",
                 j = "testway",
            value.var.prefix = c("M1","M2"))

让我们继续!在数据long2中,可能有M1,M2,--------。所以long2还是宽数据。是的,我们可以更改为长数据。 M 作为前缀,1,2,3,-----作为新列。但是,id应该是ID,newcolumn和testway,或者你可以创建一个新的id到long2,这将确保id唯一。

long3=reshape_toLong(data = long2,
                 id = c("ID","newcolumn","testway"),
                 j = "testnumber",
                 value.var.prefix = "M")
long3
   ID newcolumn testway Group testnumber     M
1   1       2hB       a     1          1   0.2
2   1       2hB       a     1          2   0.3
3   1       2hB       r     1          1 200.0
4   1       2hB       r     1          2 300.0
5   1       3hB       a     1          1   0.4
6   1       3hB       a     1          2   0.6
7   1       3hB       r     1          1 400.0
8   1       3hB       r     1          2 600.0
9   2       2hB       a     1          1   0.3
10  2       2hB       a     1          2   0.4
11  2       2hB       r     1          1 300.0
12  2       2hB       r     1          2 400.0
13  2       3hB       a     1          1   0.6
14  2       3hB       a     1          2   0.6
15  2       3hB       r     1          1 600.0
16  2       3hB       r     1          2 600.0

现在数据long3是一个绝对长的数据。

前缀很重要,我们使用如下前缀

  • 第一个:M1a、M2a、M1r、M2r
  • 第二个:M1、M2
  • 第三名:M

我们更改 id 三次,使其唯一

  • 第一个:ID
  • 第二个:ID,新列
  • 第三个:ID,新列,testway

j 是新列

  • 第一个:新列
  • 第二个:测试
  • 第三名:测试人数

2。快一点

如果每个测量结果有 4 个结果:a2、a3、r2 r3。 a:绝对值,r:相对值,2:时间 2,3:时间 3。然后 1100 列有 275 个测量结果 (1100/4)。因此,我们有 M1a2hB、M2a2hB、M3a2hB------M275a2hB。和M1a3hB,M2a3hB,M3a3hB------M275a3hB,M3就是这样。如果我们使用这样的命令,我们将有一个很长的value.var.prefix。 但是,我们可以使用 paste0 函数使用更快的方式构造前缀。

ma2=paste0("M",1:275,"a")
ma3=paste0("M",1:275,"a")
mr2=paste0("M",1:275,"r")
mr3=paste0("M",1:275,"r")
m=c(ma2,ma3,mr2,mr3)

在df1中,我们只有2个测量结果,所以我们可以使用下面的命令

ma2=paste0("M",1:2,"a")
ma3=paste0("M",1:2,"a")
mr2=paste0("M",1:2,"r")
mr3=paste0("M",1:2,"r")
prefix=c(ma2,ma3,mr2,mr3)

reshape_toLong(data = df1,
                id = "ID",
                 j = "newcolumn",
  value.var.prefix = prefix)

  ID Group newcolumn M1a M2a M1r M2r
1  1     1       2hB 0.2 0.3 200 300
2  1     1       3hB 0.4 0.6 400 600
3  2     1       2hB 0.3 0.4 300 400
4  2     1       3hB 0.6 0.6 600 600

仍然,我们可以使用 M1,M2----- 作为前缀,我们将 a2hB,a3hB,r2hB,r3hB 更改为新列。然后我们将新列子串到不同的列。

m1=paste0("M",1:2)
m2=paste0("M",1:2)
prefix=c(m1,m2)

long4=reshape_toLong(data = df1,
                id = "ID",
                 j = "newcolumn",
  value.var.prefix = prefix)
long4
  ID Group newcolumn    M1    M2
1  1     1      a2hB   0.2   0.3
2  1     1      a3hB   0.4   0.6
3  1     1      r2hB 200.0 300.0
4  1     1      r3hB 400.0 600.0
5  2     1      a2hB   0.3   0.4
6  2     1      a3hB   0.6   0.6
7  2     1      r2hB 300.0 400.0
8  2     1      r3hB 600.0 600.0

long4$testway=Left(long4$newcolumn,1)
long4$time=Right(long4$newcolumn,3)
long4
  ID Group newcolumn    M1    M2 testway time
1  1     1      a2hB   0.2   0.3       a  2hB
2  1     1      a3hB   0.4   0.6       a  3hB
3  1     1      r2hB 200.0 300.0       r  2hB
4  1     1      r3hB 400.0 600.0       r  3hB
5  2     1      a2hB   0.3   0.4       a  2hB
6  2     1      a3hB   0.6   0.6       a  3hB
7  2     1      r2hB 300.0 400.0       r  2hB
8  2     1      r3hB 600.0 600.0       r  3hB

最后,我们只能使用M作为前缀,来获取绝对数据。

long5=reshape_toLong(data = df1,
                       id = "ID",
                        j = "newcolumn",
         value.var.prefix = "M")
long5
   ID Group newcolumn     M
1   1     1     1a2hB   0.2
2   1     1     1a3hB   0.4
3   1     1     2a2hB   0.3
4   1     1     2a3hB   0.6
5   1     1     1r2hB 200.0
6   1     1     1r3hB 400.0
7   1     1     2r2hB 300.0
8   1     1     2r3hB 600.0
9   2     1     1a2hB   0.3
10  2     1     1a3hB   0.6
11  2     1     2a2hB   0.4
12  2     1     2a3hB   0.6
13  2     1     1r2hB 300.0
14  2     1     1r3hB 600.0
15  2     1     2r2hB 400.0
16  2     1     2r3hB 600.0

然后我们可以使用onetree包中的Left、Mid和Right函数从left、mid和right中提取新的列。

long5$testnumber=Left(long5$newcolumn,1)
long5$testway=Mid(long5$newcolumn,2,1)
long5$time=Right(long5$newcolumn,3)
long5
   ID Group newcolumn     M testnumber testway time
1   1     1     1a2hB   0.2          1       a  2hB
2   1     1     1a3hB   0.4          1       a  3hB
3   1     1     2a2hB   0.3          2       a  2hB
4   1     1     2a3hB   0.6          2       a  3hB
5   1     1     1r2hB 200.0          1       r  2hB
6   1     1     1r3hB 400.0          1       r  3hB
7   1     1     2r2hB 300.0          2       r  2hB
8   1     1     2r3hB 600.0          2       r  3hB
9   2     1     1a2hB   0.3          1       a  2hB
10  2     1     1a3hB   0.6          1       a  3hB
11  2     1     2a2hB   0.4          2       a  2hB
12  2     1     2a3hB   0.6          2       a  3hB
13  2     1     1r2hB 300.0          1       r  2hB
14  2     1     1r3hB 600.0          1       r  3hB
15  2     1     2r2hB 400.0          2       r  2hB
16  2     1     2r3hB 600.0          2       r  3hB

这里,我们使用不同的前缀来获取不同的数据。

  • 首先:使用paste0函数构造
  • 第二个:M1、M2、M3------,仍然是paste0功能,但更简单
  • 第三:我们只用M
  • 我们没有更改 id 和 j

3。结论

在reshape_toLong函数中:

  • data: 是你要转换的数据
  • id:是唯一id变量,可以是一个变量,也可以是多个
  • j: 是新变量name,你要堆叠timesequence number[=106] =]
  • value.var.prefix: 是值变量
  • 前缀