【问题标题】:Reshaping data in R (wide -> Long)在 R 中重塑数据(宽 -> 长)
【发布时间】:2016-06-27 16:23:48
【问题描述】:

我想将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 是从时间 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         ...

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

附言我尝试运行之前帖子中的一些代码(如果有兴趣,请参见下文),但它们似乎不同,因为 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

【问题讨论】:

  • 制作一些样本数据来复制您的实际问题应该不会太难。这样一来,其他人就可以更轻松地提出答案,或者让您知道 SO 是否已有答案。
  • @AnandaMahto - 我不完全理解你,因为我已经发布了复制实际问题的示例数据。

标签: r reshape tidyr


【解决方案1】:

我们可以使用subsplit 使用带有“nm1”的向量序列从列名中提取模式,将其用作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 列,519 在 M1r2hB 和 777 在 M1r3hB。第二个结果度量 (M2a2hB) 在第 4 列,然后 (M2a3hB) 在 262 列,520 在 M2r2hB 和 778 在 M2r3hB,依此类推。我认为,单独按 ID(无组)排序可能会起作用
  • @AmitBansal 我只能回答您提供的示例(这些... 也很困难)。我假设你有相同的模式。无论如何,这回答了与示例一起发布的问题。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    内置的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 &lt;- 2 时间点(2h, 3h)有n &lt;- 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),]
    

    【讨论】:

      【解决方案4】:

      可以使用我的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,就会发生不匹配的情况。您也可以创建一个新的 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 更改 3 次,以使其唯一

      • 第一:ID
      • 秒:ID,新列
      • 第三个:ID、新列、测试通道

      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 函数从左、中、右子串得到新的列。

      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,你要堆叠时间序号
      • value.var.prefix:是值变量的前缀

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-25
        相关资源
        最近更新 更多