在R中重新塑造数据(宽格式 -> 长格式)

3

我希望将 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个结果measure。

M1a2hB 是第一个measure从时间2到基线的绝对变化,M1a3hB 是从时间3到基线的绝对变化。同样,M1r2hB是第一个outcome从时间2到基线的相对变化,M1r3hB是一个outcome从时间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         ...

任何提示?如有需要澄清,请随时提出。谢谢!期待回复!
附言:我已尝试运行先前帖子中的一些代码(如果感兴趣,请参见下文),但它们似乎不同,因为df是三维数据,而df2包括额外的时间列。 在R中,使用ggplot2或基本图绘制宽格式数据。是否有一种方法可以在不融化宽格式数据框的情况下使用ggplot2? 将重复测量数据从R宽格式转换为长格式

你应该很容易制作一些样本数据来复制你实际遇到的问题。这将使其他人更容易地提出答案或让你知道是否已经在SO上有了答案。 - A5C1D2H2I1M1N2O1R2T1
@AnandaMahto - 我不完全理解你的意思,因为我已经发布了复制实际问题的示例数据。 - Aby
4个回答

1
我们可以使用sub从列名中提取模式,使用'nm1'将该向量的序列split,将其用作melt中的measure,以从“宽”格式转换为“长”格式。
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))

非常感谢,很高兴您理解我的问题,您的代码在样本数据上运行良好:D请问能否在加载库后解释您的代码的三个步骤?学以致用。正如您所知,我有大约100个“ID”,和270个具有不同列名的“M”,我应该如何操作?关于names(df1),我的第一个结果指标(M1a2hB)在第3列,然后(M1a3hB)在261列,M1r2hB在519列,M1r3hB在777列。第二个结果指标(M2a2hB)在第4列,然后(M2a3hB)在第262列,M2r2hB在520列,M2r3hB在778列,以此类推。我认为,仅按ID排序(不带组)可能有效。 - Aby
1
@AmitBansal 我只能回答你提供的例子(而且还有这些 ... 让它变得很困难)。我假设你有相同的模式。无论如何,这个回答解决了带有示例的问题。 - akrun

0

以下是使用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)

0
内置的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

如果您有n <- 270个测量值,以及m <- 2个时间点(2小时,3小时),请更改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"))

我假设你的示例中的 df2 中的 time == 1 是指基线测量,而不是未提及的 1小时,因为它们似乎全部为零。为了清晰起见,我将显示基线为 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),]

0

你可以使用我的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-----中看到,数据仍然是宽数据。我们仍然可以将其转换为长数据。我们使用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,则会发生不匹配。您还可以创建一个新的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或者您可以为long2创建一个新的id,这将确保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,newcolumn
  • 第三:ID,newcolumn,testway

j是新列

  • 第一:newcolumn
  • 第二:testway
  • 第三:testnumber

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函数来从左、中和右子串中获取新列。

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:是新变量的名称,您想要堆叠时间或序列号
  • value.var.prefix:是值变量的前缀

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