/ / Converter dados de formato longo para formato amplo com várias colunas de medida - r, dataframe, plyr

Converta dados de formato longo para formato amplo com várias colunas de medida - r, dataframe, plyr

Estou com dificuldades para descobrir a maneira mais elegante e flexível de trocar dados de formato longo para formato amplo quando tenho mais de uma variável de medida que desejo acompanhar.

Por exemplo, aqui está um quadro de dados simples em formato longo. ID é o assunto, TIME é uma variável de tempo, e X e Y são medições feitas de ID a TIME:

> my.df <- data.frame(ID=rep(c("A","B","C"), 5), TIME=rep(1:5, each=3), X=1:15, Y=16:30)
> my.df

ID TIME  X  Y
1   A    1  1 16
2   B    1  2 17
3   C    1  3 18
4   A    2  4 19
5   B    2  5 20
6   C    2  6 21
7   A    3  7 22
8   B    3  8 23
9   C    3  9 24
10  A    4 10 25
11  B    4 11 26
12  C    4 12 27
13  A    5 13 28
14  B    5 14 29
15  C    5 15 30

Se eu quisesse apenas transformar os valores de TIME em cabeçalhos de coluna contendo o include X, eu sei que posso usar o cast do pacote reshape (ou dcast de reshape2):

> cast(my.df, ID ~ TIME, value="X")
ID 1 2 3  4  5
1  A 1 4 7 10 13
2  B 2 5 8 11 14
3  C 3 6 9 12 15

Mas o que eu realmente quero fazer é trazer Y como outra variável de medida e ter os nomes das colunas refletindo o nome da variável de medida e o valor de tempo:

  ID X_1 X_2 X_3  X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7   10  13  16  19  22  25  28
2  B   2   5   8   11  14  17  20  23  26  29
3  C   3   6   9   12  15  18  21  24  27  30

(FWIW, eu realmente não me importo se todos os Xs são primeiro seguidos pelos Y "s, ou se eles são intercalados como X_1, Y_1, X_2, Y_2etc.)

Eu posso chegar perto disso cast-ing os dados longos duas vezes e mesclando os resultados, embora os nomes das colunas precisem de algum trabalho, e eu precisaria ajustá-los se eu precisasse adicionar uma terceira ou quarta variável além de X e Y:

merge(
cast(my.df, ID ~ TIME, value="X"),
cast(my.df, ID ~ TIME, value="Y"),
by="ID", suffixes=c("_X","_Y")
)

Parece uma combinação de funções em reshape2 e / ou plyr deve ser capaz de fazer isso de forma mais elegante que a minha tentativa, bem como lidar com variáveis ​​de medida múltiplas de forma mais limpa. Algo como cast(my.df, ID ~ TIME, value=c("X","Y")), o que não é válido, mas eu não consegui descobrir.

Algum R-wizards pode me ajudar? Obrigado.

Respostas:

14 para resposta № 1

Para lidar com várias variáveis ​​como você deseja, você precisa melt os dados que você tem antes de lançá-lo.

library("reshape2")

dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME)

que dá

  ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7  10  13  16  19  22  25  28
2  B   2   5   8  11  14  17  20  23  26  29
3  C   3   6   9  12  15  18  21  24  27  30

EDIT com base no comentário:

O quadro de dados

num.id = 10
num.time=10
my.df <- data.frame(ID=rep(LETTERS[1:num.id], num.time),
TIME=rep(1:num.time, each=num.id),
X=1:(num.id*num.time),
Y=(num.id*num.time)+1:(2*length(1:(num.id*num.time))))

dá um resultado diferente (todas as entradas são 2) porque o ID/TIME combinação não indica uma linha única. Na verdade, existem duas linhas com cada ID/TIME combinações. reshape2 assume um valor único para cada combinação possível das variáveis ​​e aplicará uma função de resumo para criar uma única variável, se houver várias entradas. É por isso que há o aviso

Aggregation function missing: defaulting to length

Você pode obter algo que funcione se você adicionar outra variável que interrompe essa redundância.

my.df$cycle <- rep(1:2, each=num.id*num.time)
dcast(melt(my.df, id.vars=c("cycle", "ID", "TIME")), cycle+ID~variable+TIME)

Isso funciona porque cycle/ID/time agora define exclusivamente uma linha em my.df.


15 para resposta № 2
   reshape(my.df,
idvar = "ID",
timevar = "TIME",
direction = "wide")

  ID X.1 Y.1 X.2 Y.2 X.3 Y.3 X.4 Y.4 X.5 Y.5
1  A   1  16   4  19   7  22  10  25  13  28
2  B   2  17   5  20   8  23  11  26  14  29
3  C   3  18   6  21   9  24  12  27  15  30

11 for answer № 3

Usando o data.table_1.9.5, isso pode ser feito sem o melt como ele pode lidar com vários value.var colunas. Você pode instalá-lo a partir de here

 library(data.table)
dcast(setDT(my.df), ID~TIME, value.var=c("X", "Y"))
#   ID 1_X 2_X 3_X 4_X 5_X 1_Y 2_Y 3_Y 4_Y 5_Y
#1:  A   1   4   7  10  13  16  19  22  25  28
#2:  B   2   5   8  11  14  17  20  23  26  29
#3:  C   3   6   9  12  15  18  21  24  27  30

5 para resposta № 4

Aqui está uma solução com o próximo pacote, que essencialmente substituiu remodelar e reshape2. Como acontece com esses dois pacotes, a estratégia é tornar o conjunto de dados mais longo primeiro e depois mais amplo.

library(magrittr); requireNamespace("tidyr"); requireNamespace("dplyr")
my.df %>%
tidyr::gather_(key="variable", value="value", c("X", "Y")) %>%  # Make it even longer.
dplyr::mutate(                                                  # Create the spread key.
time_by_variable   = paste0(variable, "_", TIME)
) %>%
dplyr::select(ID, time_by_variable, value) %>%                  # Retain these three.
tidyr::spread(key=time_by_variable, value=value)                # Spread/widen.

Depois de tidyr::gather() chamada, o conjunto de dados intermediário é:

ID TIME variable value
1   A    1        X     1
2   B    1        X     2
3   C    1        X     3
...
28  A    5        Y    28
29  B    5        Y    29
30  C    5        Y    30

O resultado final é:

  ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7  10  13  16  19  22  25  28
2  B   2   5   8  11  14  17  20  23  26  29
3  C   3   6   9  12  15  18  21  24  27  30

tidyr::unite() é uma alternativa, sugerida por @JWilliman. Isto é funcionalmente equivalente ao dplyr::mutate() e dplyr::select() combinação acima, quando o remove O parâmetro é verdadeiro (que é o padrão).

Se você não está acostumado a este tipo de manipulação, o tidyr::unite() pode ser um pequeno obstáculo, porque é mais uma função que você tem que aprender e lembrar. No entanto, os benefícios incluem (a) código mais conciso (ou seja, quatro linhas são substituídas por uma) e (b) menos lugares para repetir nomes de variáveis ​​(ou seja, você não precisa repetir / modificar variáveis ​​no dplyr::select() cláusula).

my.df %>%
tidyr::gather_(key="variable", value="value", c("X", "Y")) %>%  # Make it even longer.
tidyr::unite("time_by_variable", variable, TIME, remove=T) %>%  # Create the spread key `time_by_variable` while simultaneously dropping `variable` and `TIME`.
tidyr::spread(key=time_by_variable, value=value)                # Spread/widen.