Pivot / Reshape data in R [duplicate]
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
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
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*]
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
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
Może możesz spróbować reshapejak poniżej
reshape(
setNames(data, gsub("(\\d+)$", "\\.\\1", names(data))),
direction = "long",
varying = 2:ncol(data)
)