2014-09-19 20 views
61

Ho dati da un sondaggio online in cui gli intervistati passano attraverso un ciclo di domande 1-3 volte. Il software di indagine (Qualtrics) registra questi dati in più colonne, cioè Q3.2 nel sondaggio avrà colonne Q3.2.1., Q3.2.2. e Q3.2.3.:Raccogliere più serie di colonne

df <- data.frame(
    id = 1:10, 
    time = as.Date('2009-01-01') + 0:9, 
    Q3.2.1. = rnorm(10, 0, 1), 
    Q3.2.2. = rnorm(10, 0, 1), 
    Q3.2.3. = rnorm(10, 0, 1), 
    Q3.3.1. = rnorm(10, 0, 1), 
    Q3.3.2. = rnorm(10, 0, 1), 
    Q3.3.3. = rnorm(10, 0, 1) 
) 

# Sample data 

    id  time Q3.2.1.  Q3.2.2. Q3.2.3.  Q3.3.1. Q3.3.2.  Q3.3.3. 
1 1 2009-01-01 -0.2059165 -0.29177677 -0.7107192 1.52718069 -0.4484351 -1.21550600 
2 2 2009-01-02 -0.1981136 -1.19813815 1.1750200 -0.40380049 -1.8376094 1.03588482 
3 3 2009-01-03 0.3514795 -0.27425539 1.1171712 -1.02641801 -2.0646661 -0.35353058 
... 

voglio combinare tutti i QN.N * colonne in ordinate singole colonne QN.N, che termina poi con qualcosa di simile:

id  time loop_number  Q3.2  Q3.3 
1 1 2009-01-01   1 -0.20591649 1.52718069 
2 2 2009-01-02   1 -0.19811357 -0.40380049 
3 3 2009-01-03   1 0.35147949 -1.02641801 
... 
11 1 2009-01-01   2 -0.29177677 -0.4484351 
12 2 2009-01-02   2 -1.19813815 -1.8376094 
13 3 2009-01-03   2 -0.27425539 -2.0646661 
... 
21 1 2009-01-01   3 -0.71071921 -1.21550600 
22 2 2009-01-02   3 1.17501999 1.03588482 
23 3 2009-01-03   3 1.11717121 -0.35353058 
... 

la biblioteca tidyr ha la funzione gather(), che funziona grande per combinare uno set di colonne:

library(dplyr) 
library(tidyr) 
library(stringr) 

df %>% gather(loop_number, Q3.2, starts_with("Q3.2")) %>% 
    mutate(loop_number = str_sub(loop_number,-2,-2)) %>% 
    select(id, time, loop_number, Q3.2) 


    id  time loop_number  Q3.2 
1 1 2009-01-01   1 -0.20591649 
2 2 2009-01-02   1 -0.19811357 
3 3 2009-01-03   1 0.35147949 
... 
29 9 2009-01-09   3 -0.58581232 
30 10 2009-01-10   3 -2.33393981 

Il frame di dati risultante ha 30 righe, come previsto (10 individui, 3 loop ciascuno). Tuttavia, la raccolta di una seconda serie di colonne non funziona correttamente: rende correttamente le due colonne combinate Q3.2 e Q3.3, ma finisce con 90 righe anziché 30 (tutte le combinazioni di 10 individui, 3 cicli di Q3.2 e 3 cicli di Q3.3, le combinazioni aumenteranno sostanzialmente per ciascun gruppo di colonne nei dati effettivi):

df %>% gather(loop_number, Q3.2, starts_with("Q3.2")) %>% 
    gather(loop_number, Q3.3, starts_with("Q3.3")) %>% 
    mutate(loop_number = str_sub(loop_number,-2,-2)) 


    id  time loop_number  Q3.2  Q3.3 
1 1 2009-01-01   1 -0.20591649 1.52718069 
2 2 2009-01-02   1 -0.19811357 -0.40380049 
3 3 2009-01-03   1 0.35147949 -1.02641801 
... 
89 9 2009-01-09   3 -0.58581232 -0.13187024 
90 10 2009-01-10   3 -2.33393981 -0.48502131 

c'è un modo per utilizzare più chiamate a gather() simili, combinando piccoli sottoinsiemi di colonne simili mantenendo il numero corretto di righe?

+0

Cosa c'è che non va con 'df%>% gather (numero_culo, Q3.2, starts_with (" Q3. "))' – Alex

+0

Questo mi consente di ottenere una colonna consolidata con 60 righe. Immagino che potrebbe funzionare se includessi una sorta di chiamata a 'seperate()' per dividere i valori di Q3.3 (e oltre) nelle loro colonne. Ma sembra ancora una soluzione hacky davvero rotonda ... – Andrew

+0

usa 'spread' sto lavorando su una soluzione ora: p – Alex

risposta

84

Questo approccio sembra abbastanza naturale per me:

df %>% 
    gather(key, value, -id, -time) %>% 
    extract(key, c("question", "loop_number"), "(Q.\\..)\\.(.)") %>% 
    spread(question, value) 

Prima di raccogliere tutte le colonne domanda, utilizzare extract() a separarsi in question e loop_number, poi spread() domanda di nuovo nelle colonne.

#> id  time loop_number   Q3.2  Q3.3 
#> 1 1 2009-01-01   1 0.142259203 -0.35842736 
#> 2 1 2009-01-01   2 0.061034802 0.79354061 
#> 3 1 2009-01-01   3 -0.525686204 -0.67456611 
#> 4 2 2009-01-02   1 -1.044461185 -1.19662936 
#> 5 2 2009-01-02   2 0.393808163 0.42384717 
+0

Ciao. Ho molte colonne con nomi che terminano in 1 e 2, come age1, age2, weight1, weight2, blood1, blood2 .... Come posso applicare il tuo metodo qui? – skan

+1

Cosa significa questa parte: "(Q. \\ ..) \\. (.)" Cosa dovrei cercare per decodificare cosa sta succedendo lì? – mob

+0

@mob Espressioni regolari – hadley

19

Questa operazione può essere eseguita utilizzando reshape. È comunque possibile con dplyr.

colnames(df) <- gsub("\\.(.{2})$", "_\\1", colnames(df)) 
    colnames(df)[2] <- "Date" 
    res <- reshape(df, idvar=c("id", "Date"), varying=3:8, direction="long", sep="_") 
    row.names(res) <- 1:nrow(res) 

    head(res) 
    # id  Date time  Q3.2  Q3.3 
    #1 1 2009-01-01 1 1.3709584 0.4554501 
    #2 2 2009-01-02 1 -0.5646982 0.7048373 
    #3 3 2009-01-03 1 0.3631284 1.0351035 
    #4 4 2009-01-04 1 0.6328626 -0.6089264 
    #5 5 2009-01-05 1 0.4042683 0.5049551 
    #6 6 2009-01-06 1 -0.1061245 -1.7170087 

o utilizzando dplyr

library(tidyr) 
    library(dplyr) 
    colnames(df) <- gsub("\\.(.{2})$", "_\\1", colnames(df)) 

    df %>% 
    gather(loop_number, "Q3", starts_with("Q3")) %>% 
    separate(loop_number,c("L1", "L2"), sep="_") %>% 
    spread(L1, Q3) %>% 
    select(-L2) %>% 
    head() 
    # id  time  Q3.2  Q3.3 
    #1 1 2009-01-01 1.3709584 0.4554501 
    #2 1 2009-01-01 1.3048697 0.2059986 
    #3 1 2009-01-01 -0.3066386 0.3219253 
    #4 2 2009-01-02 -0.5646982 0.7048373 
    #5 2 2009-01-02 2.2866454 -0.3610573 
    #6 2 2009-01-02 -1.7813084 -0.7838389 
+0

Whoa, funziona perfettamente. tidyr è apparentemente un rimpiazzo/aggiornamento per rimodellare - mi chiedo se @hadley sappia di un modo per fare questa stessa cosa con dplyr o tidyr ... – Andrew

+0

Questa è pura magia. L'unica cosa che ho aggiunto era 'mutate (loop_number = as.numeric (L2))' prima di rilasciare 'L2', ed è perfetto. – Andrew

+0

@Andrew Personalmente preferisco il metodo 'reshape' per il suo codice compatto, sebbene' dplyr' possa essere più veloce per i grandi dataset. – akrun

9

Non è affatto correlate a "tidyr" e "dplyr", ma qui è un'altra opzione da considerare: merged.stack da my "splitstackshape" package, V1.4.0 e superiori.

library(splitstackshape) 
merged.stack(df, id.vars = c("id", "time"), 
      var.stubs = c("Q3.2.", "Q3.3."), 
      sep = "var.stubs") 
#  id  time .time_1  Q3.2.  Q3.3. 
# 1: 1 2009-01-01  1. -0.62645381 1.35867955 
# 2: 1 2009-01-01  2. 1.51178117 -0.16452360 
# 3: 1 2009-01-01  3. 0.91897737 0.39810588 
# 4: 2 2009-01-02  1. 0.18364332 -0.10278773 
# 5: 2 2009-01-02  2. 0.38984324 -0.25336168 
# 6: 2 2009-01-02  3. 0.78213630 -0.61202639 
# 7: 3 2009-01-03  1. -0.83562861 0.38767161 
# <<:::SNIP:::>> 
# 24: 8 2009-01-08  3. -1.47075238 -1.04413463 
# 25: 9 2009-01-09  1. 0.57578135 1.10002537 
# 26: 9 2009-01-09  2. 0.82122120 -0.11234621 
# 27: 9 2009-01-09  3. -0.47815006 0.56971963 
# 28: 10 2009-01-10  1. -0.30538839 0.76317575 
# 29: 10 2009-01-10  2. 0.59390132 0.88110773 
# 30: 10 2009-01-10  3. 0.41794156 -0.13505460 
#  id  time .time_1  Q3.2.  Q3.3. 
+0

Ciao. Ho molte colonne con nomi che terminano in 1 e 2, come age1, age2, weight1, weight2, blood1, blood2 .... Come posso applicare il tuo metodo qui? – skan

14

Con il recente aggiornamento melt.data.table, ora possiamo fondere più colonne. Con questo, si può fare:

require(data.table) ## 1.9.5 
melt(setDT(df), id=1:2, measure=patterns("^Q3.2", "^Q3.3"), 
    value.name=c("Q3.2", "Q3.3"), variable.name="loop_number") 
# id  time loop_number   Q3.2  Q3.3 
# 1: 1 2009-01-01   1 -0.433978480 0.41227209 
# 2: 2 2009-01-02   1 -0.567995351 0.30701144 
# 3: 3 2009-01-03   1 -0.092041353 -0.96024077 
# 4: 4 2009-01-04   1 1.137433487 0.60603396 
# 5: 5 2009-01-05   1 -1.071498263 -0.01655584 
# 6: 6 2009-01-06   1 -0.048376809 0.55889996 
# 7: 7 2009-01-07   1 -0.007312176 0.69872938 

è possibile ottenere la versione di sviluppo da here.

+0

la risposta ha richiesto soluzioni 'tidyr' – NiuBiBang

+0

Ciao. Ho molte colonne con nomi che terminano in 1 e 2, come age1, age2, weight1, weight2, blood1, blood2 .... Come posso applicare il tuo metodo qui? – skan

+0

skan, controlla la vignetta di rimodellamento (https://github.com/Rdatatable/data.table/wiki/Getting-started). In bocca al lupo! – Arun

3

Nel caso in cui siete come me, e non può lavorare fuori come utilizzare "espressioni regolari con gruppi di cattura" per extract, il seguente codice di replica la linea di extract(...) in risposta Hadleys':

df %>% 
    gather(question_number, value, starts_with("Q3.")) %>% 
    mutate(loop_number = str_sub(question_number,-2,-2), question_number = str_sub(question_number,1,4)) %>% 
    select(id, time, loop_number, question_number, value) %>% 
    spread(key = question_number, value = value) 

Il problema qui è che il gruppo iniziale forma una colonna chiave che è in realtà una combinazione di due chiavi. Ho scelto di utilizzare mutate nella mia soluzione originale nei commenti per suddividere questa colonna in due colonne con informazioni equivalenti, una colonna loop_number e una colonna question_number. spread può quindi essere utilizzato per trasformare i dati di formato lungo, che sono coppie di valori chiave (question_number, value) in dati di forma estesa.