web-dev-qa-db-fra.com

Remodelage large à long avec plusieurs colonnes de valeurs

J'ai besoin de remodeler ma table large en format long mais en conservant plusieurs champs pour chaque enregistrement, par exemple:

dw <- read.table(header=T, text='
 sbj f1.avg f1.sd f2.avg f2.sd  blabla
   A   10    6     50     10      bA
   B   12    5     70     11      bB
   C   20    7     20     8       bC
   D   22    8     22     9       bD
 ')

# Now I want to melt this table, keeping both AVG and SD as separate fields for each measurement, to get something like this:

 #    sbj var avg  sd  blabla
 #     A   f1  10  6     bA
 #     A   f2  50  10    bA
 #     B   f1  12  5     bB
 #     B   f2  70  11    bB
 #     C   f1  20  7     bC
 #     C   f2  20  8     bC
 #     D   f1  22  8     bD
 #     D   f2  22  9     bD

J'ai une connaissance de base de l'utilisation de melt et reshape, mais il n'est pas évident pour moi comment appliquer un tel remodelage dans mon cas. Je serais reconnaissant pour tout conseil ou pointer vers un autre SO poste si quelque chose de similaire a déjà été demandé.

26
Vasily A

reshape le fait avec les arguments appropriés.

varying répertorie les colonnes qui existent au format large, mais qui sont divisées en plusieurs lignes au format long. v.names est l'équivalent au format long. Entre les deux, un mappage est créé.

De ?reshape:

De plus, la tentative n'est pas tentée si v.names est donné explicitement. Notez que l'ordre des variables varie comme x.1, y.1, x.2, y.2.

Compte tenu de ces varying et v.names arguments, reshape est assez intelligent pour voir que j'ai spécifié que l'index est avant le point ici (c'est-à-dire, l'ordre 1.x, 1.y, 2.x, 2.y). Notez que les données d'origine ont les colonnes dans cet ordre, nous pouvons donc spécifier varying=2:5 pour cet exemple de données, mais ce n'est pas sûr en général.

Étant donné les valeurs de times et v.names, reshape divise les colonnes varying sur un . caractère (l'argument sep par défaut) pour créer les colonnes dans la sortie.

times spécifie les valeurs à utiliser dans la colonne var créée et v.names sont collés sur ces valeurs pour obtenir les noms de colonne au format large pour le mappage au résultat.

Enfin, idvar est spécifié comme étant la colonne sbj, qui identifie les enregistrements individuels au format large (merci @thelatemail).

reshape(dw, direction='long', 
        varying=c('f1.avg', 'f1.sd', 'f2.avg', 'f2.sd'), 
        timevar='var',
        times=c('f1', 'f2'),
        v.names=c('avg', 'sd'),
        idvar='sbj')

##      sbj blabla var avg sd
## A.f1   A     bA  f1  10  6
## B.f1   B     bB  f1  12  5
## C.f1   C     bC  f1  20  7
## D.f1   D     bD  f1  22  8
## A.f2   A     bA  f2  50 10
## B.f2   B     bB  f2  70 11
## C.f2   C     bC  f2  20  8
## D.f2   D     bD  f2  22  9
25
Matthew Lundberg

Une autre option en utilisant le nouveau package tidyr de Hadley.

library(tidyr)
library(dplyr)

dw <- read.table(header=T, text='
 sbj f1.avg f1.sd f2.avg f2.sd  blabla
   A   10    6     50     10      bA
   B   12    5     70     11      bB
   C   20    7     20     8       bC
   D   22    8     22     9       bD
 ')

dw %>% 
  gather(v, value, f1.avg:f2.sd) %>% 
  separate(v, c("var", "col")) %>% 
  arrange(sbj) %>% 
  spread(col, value)
26
Maiasaura

Cela semble faire ce que vous voulez, sauf que le f est supprimé des éléments dans time.

reshape(dw, idvar = "sbj", varying = list(c(2,4),c(3,5)), v.names = c("ave", "sd"), direction = "long")

    sbj blabla time ave sd
A.1   A     bA    1  10  6
B.1   B     bB    1  12  5
C.1   C     bC    1  20  7
D.1   D     bD    1  22  8
A.2   A     bA    2  50 10
B.2   B     bB    2  70 11
C.2   C     bC    2  20  8
D.2   D     bD    2  22  9
7
Mark Miller

melt de la version> = 1.9.6 de data.table, le fait en spécifiant l'index de colonne dans measure.vars en tant que list.

 melt(setDT(dw), measure.vars=list(c(2,4), c(3,5)), 
     variable.name='var', value.name=c('avg', 'sd'))[, 
      var:= paste0('f',var)][order(sbj)]
#   sbj blabla var avg sd
#1:   A     bA  f1  10  6
#2:   A     bA  f2  50 10
#3:   B     bB  f1  12  5
#4:   B     bB  f2  70 11
#5:   C     bC  f1  20  7
#6:   C     bC  f2  20  8
#7:   D     bD  f1  22  8
#8:   D     bD  f2  22  9

Ou vous pouvez utiliser la nouvelle fonction patterns:

melt(setDT(dw), 
     measure = patterns("avg", "sd"),
     variable.name = 'var', value.name = c('avg', 'sd'))
#    sbj blabla var avg sd
# 1:   A     bA   1  10  6
# 2:   B     bB   1  12  5
# 3:   C     bC   1  20  7
# 4:   D     bD   1  22  8
# 5:   A     bA   2  50 10
# 6:   B     bB   2  70 11
# 7:   C     bC   2  20  8
# 8:   D     bD   2  22  9
7
akrun

Pour ajouter aux options disponibles ici, vous pouvez également envisager merged.stack de mon package "splitstackshape":

library(splitstackshape)
merged.stack(dw, var.stubs = c("avg", "sd"), sep = "var.stubs", atStart = FALSE)
#    sbj blabla .time_1 avg sd
# 1:   A     bA     f1.  10  6
# 2:   A     bA     f2.  50 10
# 3:   B     bB     f1.  12  5
# 4:   B     bB     f2.  70 11
# 5:   C     bC     f1.  20  7
# 6:   C     bC     f2.  20  8
# 7:   D     bD     f1.  22  8
# 8:   D     bD     f2.  22  9

Vous pouvez également faire un peu plus de nettoyage sur le ".time_1" variable, comme ceci.

merged.stack(dw, var.stubs = c("avg", "sd"), 
             sep = "var.stubs", atStart = FALSE)[, .time_1 := sub(
               ".", "", .time_1, fixed = TRUE)][]
#    sbj blabla .time_1 avg sd
# 1:   A     bA      f1  10  6
# 2:   A     bA      f2  50 10
# 3:   B     bB      f1  12  5
# 4:   B     bB      f2  70 11
# 5:   C     bC      f1  20  7
# 6:   C     bC      f2  20  8
# 7:   D     bD      f1  22  8
# 8:   D     bD      f2  22  9

Vous remarquerez l'utilisation du atStart = FALSE argument. C'est parce que vos noms sont un peu dans un ordre différent que les fonctions liées au remodelage semblent aimer. En général, le "talon" devrait venir en premier, puis les "heures", comme ceci:

dw2 <- dw
setnames(dw2, gsub("(.*)\\.(.*)", "\\2.\\1", names(dw2)))
names(dw2)
# [1] "sbj"    "avg.f1" "sd.f1"  "avg.f2" "sd.f2"  "blabla"

Si les noms étaient dans ce format, les deux R de base reshape et merged.stack bénéficier d'une syntaxe plus directe:

merged.stack(dw2, var.stubs = c("avg", "sd"), sep = ".")
reshape(dw2, idvar = c("sbj", "blabla"), varying = 2:5, 
        sep = ".", direction = "long")
6