Pivot / Reshape data in R [duplicate]

Sep 14 2020

Dziękuję wszystkim za odpowiedzi, myślałem, że jestem mądrzejszy ode mnie i miałem nadzieję, że cokolwiek z tego zrozumiem. Myślę, że popsułem też wizualizację moich danych. Zmieniłem post, aby lepiej pokazać moje przykładowe dane. Przepraszamy za niedogodności i mam nadzieję, że ktoś może mi pomóc.

Mam pytanie dotyczące zmiany kształtu moich danych. Zebrane dane wyglądają następująco:

 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    
  ')

Teraz chciałbym, żeby wyglądało to mniej więcej tak:

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

Jak miałbym się tam dostać? Sprawdziłem kilka rzeczy dotyczących formatu od szerokiego do długiego, ale wydaje się, że to nie pomaga. Jestem całkowicie nowy w Rstudio i Stackoverflow (jeśli nie możesz tego powiedzieć).

Pozdrawiam iz góry dziękuję.

Odpowiedzi

Bernhard Sep 15 2020 at 06:15

W ostatniej edycji poprosiłeś o rozwiązanie, które jest łatwe do zrozumienia. Bardzo prostym podejściem byłoby ułożenie kolumn pomiarowych jedna na drugiej, a kolumn Tdays jedna na drugiej. Chociaż pakiety specjalne sprawiają, że rzeczy są bardzo zwięzłe i eleganckie, dla uproszczenia możemy rozwiązać ten problem bez dodatkowych opakowań. Standard R ma wygodną funkcję o trafnej nazwie stack, która działa w ten sposób:

> 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

Możemy osobno zestawiać pomiary i dni, a następnie łączyć je za pomocą 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)))

Dzięki temu pomiary i dni są starannie połączone, ale pozostawia nas, bez pidktórych możemy dodać, używając repdo powtórzenia oryginału pid4 razy:

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

Jak wygląda głowa

> 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

Jak powiedziałem powyżej, nie jest to kolejność, której się spodziewałeś i możesz spróbować posortować tę ramkę data.frame, jeśli ma to jakiekolwiek znaczenie:

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

prowadzące do końcowego wyniku

> 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
3 BenNorris Sep 14 2020 at 12:03

Oto nieco inna pivot_longer()wersja.

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"Argumentem tworzy nowe kolumny z nazwami kolumn w oparciu o names_patternargument. names_patternArgumentem zajmuje szczególne wejście regex. W tym przypadku oto podział:

(.+)  # 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*]
BappaDas Sep 14 2020 at 11:57

tidyverse rozwiązanie

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)

Daje następujący wynik

# 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
AnupTiwari Sep 14 2020 at 11:43

Biorąc pod uwagę ramkę danych, dfjak poniżej:

     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

Możesz użyć tego rozwiązania, aby uzyskać wymaganą ramkę danych:

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)

Wynik printinstrukcji jest następujący:

   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
ThomasIsCoding Sep 15 2020 at 06:23

Może możesz spróbować reshapejak poniżej

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