J'ai du mal à trouver le moyen le plus élégant et le plus flexible de passer des données du format long au format large lorsque j'ai plus d'une variable de mesure à apporter.
Par exemple, voici un simple bloc de données au format long. ID
est le sujet, TIME
est une variable de temps, et X
et Y
sont des mesures faites de ID
à 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
Si je voulais simplement transformer les valeurs de TIME
en en-têtes de colonne contenant l'include X
, je sais que je peux utiliser cast()
à partir du package 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
Mais ce que je veux vraiment faire, c'est aussi apporter Y
comme une autre variable de mesure, et faire en sorte que les noms de colonne reflètent à la fois le nom de la variable de mesure et la valeur de temps:
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, peu m'importe si tous les X
sont d'abord suivis par les Y
, ou s'ils sont entrelacés comme X_1
, Y_1
, X_2
, Y_2
, Etc.)
Je peux me rapprocher de cela en transtypé - en multipliant les données longues par deux et en fusionnant les résultats, bien que les noms des colonnes aient besoin d'un peu de travail, et j'aurais besoin de les modifier si j'avais besoin d'ajouter un 3ème ou 4ème variable en plus de X
et Y
:
merge(
cast(my.df, ID ~ TIME, value="X"),
cast(my.df, ID ~ TIME, value="Y"),
by="ID", suffixes=c("_X","_Y")
)
On dirait qu'une combinaison de fonctions dans reshape2
Et/ou plyr
devrait être en mesure de le faire plus élégamment que ma tentative, ainsi que de gérer plus proprement plusieurs variables de mesure. Quelque chose comme cast(my.df, ID ~ TIME, value=c("X","Y"))
, qui n'est pas valide. Mais je n'ai pas pu le comprendre.
Afin de gérer plusieurs variables comme vous le souhaitez, vous devez melt
les données que vous avez avant de les caster.
library("reshape2")
dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME)
qui donne
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 basé sur le commentaire:
Le bloc de données
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))))
donne un résultat différent (toutes les entrées sont 2) car la combinaison ID
/TIME
n'indique pas une ligne unique. En fait, il y a deux lignes avec chacune des combinaisons ID
/TIME
. reshape2
suppose une valeur unique pour chaque combinaison possible de variables et appliquera une fonction récapitulative pour créer une seule variable s'il existe plusieurs entrées. Voilà pourquoi il y a l'avertissement
Aggregation function missing: defaulting to length
Vous pouvez obtenir quelque chose qui fonctionne si vous ajoutez une autre variable qui rompt cette redondance.
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)
Cela fonctionne car cycle
/ID
/time
définit désormais uniquement une ligne dans my.df
.
reshape(my.df,
idvar = "ID",
timevar = "TIME",
direction = "wide")
donne
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
En utilisant le data.table_1.9.5
, cela peut être fait sans melt
car il peut gérer plusieurs value.var
Colonnes. Vous pouvez l'installer à 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
Remarque -Sept 2019 : dans tidyr , l'approche gather()
+ spread()
(décrit dans cette réponse) a plus ou moins été remplacé par l'approche pivot_wider()
(décrite dans ` cette nouvelle réponse tidyr ). Pour des informations actuelles sur la transition, voir vignette pivotante .
Voici une solution avec le package tidyr , qui a essentiellement remplacé reshape et reshape2 . Comme avec ces deux packages, la stratégie consiste à allonger d'abord l'ensemble de données, puis à l'étendre.
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.
Après l'appel tidyr::gather()
, l'ensemble de données intermédiaire est:
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
Le résultat final est:
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()
est une alternative, suggérée par @JWilliman. Ceci est fonctionnellement équivalent à la combinaison dplyr::mutate()
et dplyr::select()
ci-dessus, lorsque le paramètre remove
est vrai ( qui est la valeur par défaut).
Si vous n'êtes pas habitué à ce type de manipulation, la tidyr::unite()
peut être un petit obstacle car c'est une fonction de plus que vous devez apprendre et mémoriser. Cependant, ses avantages incluent (a) un code plus concis ( c'est-à-dire , quatre lignes sont remplacées par une) et (b) moins d'endroits pour répéter les noms de variables ( ie , vous n'avez pas à répéter/modifier les variables dans la clause dplyr::select()
).
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.
La fonction pivot_wider()
est l'approche de 2ème génération de tidyr (publiée dans tidyr 1.0.0).
library(magrittr); requireNamespace("tidyr");
my.df %>%
tidyr::pivot_wider(
names_from = c(TIME), # Can accommodate more variables, if needed.
values_from = c(X, Y)
)
Résultat :
# A tibble: 3 x 11
ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
<fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
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
Ceci est probablement préférable à approche tidyr précédente (qui utilise une combinaison de gather()
et spread()
=).
Plus de capacités sont décrites dans la vignette pivotante . Cet exemple est particulièrement concis car vos spécifications souhaitées correspondent aux valeurs par défaut de id_cols
Et names_sep
.