Pivot/Reshape R 中的数据

Pivot/Reshape data in R

谢谢大家的回答,我以为我比现在聪明,希望我能理解其中的任何一个。我想我也搞砸了我的数据可视化。我编辑了我的 post 以更好地显示我的样本数据。很抱歉给您带来不便,我真的希望有人能帮助我。

我对重塑数据有疑问。收集的数据如下所示:

 data <- read.table(header=T, text='
  pid     measurement1     Tdays1     measurement2      Tdays2     measurement3     Tdays3  measurment4    Tdays4 
   1         1356           1435         1483            1405         1563           1374       NA           NA     
   2         943            1848         1173            1818         1300           1785       NA           NA     
   3         1590           185          NA              NA           NA             NA         1585         294    
   4         130            72           443             70           NA             NA         136          79     
   4         140            82           NA              NA           NA             NA         756          89     
   4         220            126          266             124          NA             NA         703          128    
   4         166            159          213             156          476            145        776          166    
   4         380            189          583             173          NA             NA         586          203    
   4         353            231          510             222          656            217        526          240    
   4         180            268          NA              NA           NA             NA         NA           NA       
   4         NA             NA           NA              NA           NA             NA         580          278    
   4         571            334          596             303          816            289        483          371    
  ')

现在我希望它看起来像这样:

PID     Time   Value
 1       1435   1356
 1       1405   1483
 1       1374   1563
 2       1848   943
 2       1818   1173
 2       1785   1300
 3       185    1590
...     ...     ... 

我将如何到达那里?我查过一些关于宽格式到长格式的东西,但它似乎并没有解决问题。我是 Rstudio 和 Whosebug 的新手(如果你还不知道的话)。

此致问候,并提前致谢。

考虑一个数据帧,df如下所示:

     PID T1 measurement1 T2 measurement2 T3 measurement3
     1   1          100  4          200  7           50
     2   2          150  5          300  8           60
     3   3          120  6          210  9           70

您可以使用此解决方案获取所需的数据框:

iters = seq(from = 4, to = length(colnames(df))-1, by = 2)
finalDf = df[, c(1,2,3)]
for(j in iters){
    tobind = df[, c(1,j,j+1)]
    finalDf = rbind(finalDf, tobind)
}

finalDf = finalDf[order(finalDf[,1]),]

print(finalDf)

print语句的输出是这样的:

   PID T1 measurement1
1   1  1          100
4   1  4          200
7   1  7           50
2   2  2          150
5   2  5          300
8   2  8           60
3   3  3          120
6   3  6          210
9   3  9           70

tidyverse解决方案

library(tidyverse)
dw %>% 
  pivot_longer(-PID) %>% 
  mutate(name = gsub('^([A-Za-z]+)(\d+)$', '\1_\2', name )) %>% 
  separate(name, into = c('A', 'B'), sep = '_', convert = T) %>% 
  pivot_wider(names_from = A, values_from = value)

给出以下输出

# A tibble: 9 x 4
    PID     B     T measurement
  <int> <int> <int>       <int>
1     1     1     1         100
2     1     2     4         200
3     1     3     7          50
4     2     1     2         150
5     2     2     5         300
6     2     3     8          60
7     3     1     3         120
8     3     2     6         210
9     3     3     9          70

这是一个略有不同的 pivot_longer() 版本。

library(tidyr)
library(dplyr)
dw %>% 
  pivot_longer(cols = -PID, names_to =".value", names_pattern = "(.+)[0-9]")
# A tibble: 9 x 3
    PID     T measurement
  <dbl> <dbl>       <dbl>
1     1     1         100
2     1     4         200
3     1     7          50
4     2     2         150
5     2     5         300
6     2     8          60
7     3     3         120
8     3     6         210
9     3     9          70

names_to = ".value" 参数根据 names_pattern 参数从列名创建新列。 names_pattern 参数采用特殊的正则表达式输入。在这种情况下,细分如下:

(.+)  # match everything - anything noted like this becomes the ".values"
[0-9] # numeric characters - tells the pattern that the numbers 
      # at the end are excluded from ".values". If you have multiple digit 
      # numbers, use [0-9*]

在上次编辑中,您要求提供易于理解的解决方案。一种非常简单的方法是将测量列堆叠在一起,并将 Tdays 列堆叠在一起。虽然专业包使事情变得非常简洁和优雅,但为了简单起见,我们可以在不使用额外包的情况下解决这个问题。标准 R 有一个方便的函数,恰当地命名为 stack,其工作方式如下:

> exp <-  data.frame(value1 = 1:5, value2 = 6:10)
> stack(exp)
   values    ind
1       1 value1
2       2 value1
3       3 value1
4       4 value1
5       5 value1
6       6 value2
7       7 value2
8       8 value2
9       9 value2
10     10 value2

我们可以单独堆叠测量值和 Tdays,然后通过 cbind:

将它们组合起来
data <- read.table(header=T, text='
  pid     measurement1     Tdays1     measurement2      Tdays2     measurement3     Tdays3  measurement4    Tdays4 
   1         1356           1435         1483            1405         1563           1374       NA           NA     
   2         943            1848         1173            1818         1300           1785       NA           NA     
   3         1590           185          NA              NA           NA             NA         1585         294    
   4         130            72           443             70           NA             NA         136          79     
   4         140            82           NA              NA           NA             NA         756          89     
   4         220            126          266             124          NA             NA         703          128    
   4         166            159          213             156          476            145        776          166    
   4         380            189          583             173          NA             NA         586          203    
   4         353            231          510             222          656            217        526          240    
   4         180            268          NA              NA           NA             NA         NA           NA       
   4         NA             NA           NA              NA           NA             NA         580          278    
   4         571            334          596             303          816            289        483          371    
  ')


cbind(stack(data, c(measurement1, measurement2, measurement3, measurement4)),
      stack(data, c(Tdays1, Tdays2, Tdays3, Tdays4)))

这将测量值和 Tdays 整齐地放在一起,但让我们没有 pid,我们可以使用 rep 添加它以复制原始 pid 4 次:

result <- cbind(pid = rep(data$pid, 4),
                stack(data, c(measurement1, measurement2, measurement3, measurement4)),
                stack(data, c(Tdays1, Tdays2, Tdays3, Tdays4)))

它的头长得像

> head(result)
  pid values          ind values    ind
1   1   1356 measurement1   1435 Tdays1
2   2    943 measurement1   1848 Tdays1
3   3   1590 measurement1    185 Tdays1
4   4    130 measurement1     72 Tdays1
5   4    140 measurement1     82 Tdays1
6   4    220 measurement1    126 Tdays1

正如我上面所说,这不是您期望的顺序,您可以尝试对其进行排序 data.frame,如果有任何问题:

result <- result[order(result$pid), c(1, 4, 2)]
names(result) <- c("pid", "Time", "Value")

导致最终结果

> head(result)
   pid Time Value
1    1 1435  1356
13   1 1405  1483
25   1 1374  1563
37   1   NA    NA
2    2 1848   943
14   2 1818  1173

也许你可以像下面那样尝试reshape

reshape(
  setNames(data, gsub("(\d+)$", "\.\1", names(data))),
  direction = "long",
  varying = 2:ncol(data)
)