2013-05-03 11 views
16

Modifica - La domanda è stata originariamente intitolato < < lungo per ampi dati rimodellamento in R >>Calcola media e deviazione standard dal gruppo per più variabili in un data.frame


Sono solo imparando R e cercando di trovare modi per applicarlo per aiutare gli altri nella mia vita. Come test case, sto lavorando per rimodellare alcuni dati e ho difficoltà a seguire gli esempi che ho trovato online. Quello che sto iniziando con un look simile a questo:

ID Obs 1 Obs 2 Obs 3 
1 43  48  37 
1 27  29  22 
1 36  32  40 
2 33  38  36 
2 29  32  27 
2 32  31  35 
2 25  28  24 
3 45  47  42 
3 38  40  36 

E quello che voglio finire con sarà simile a questa:

ID Obs 1 mean Obs 1 std dev Obs 2 mean Obs 2 std dev 
1 x   x    x   x 
2 x   x    x   x 
3 x   x    x   x 

e così via. Ciò di cui non sono sicuro è se ho bisogno di ulteriori informazioni nei miei dati di lunga durata, o cosa. Immagino che la parte matematica (trovare la media e le deviazioni standard) sarà la parte facile, ma non sono stato in grado di trovare un modo che sembra funzionare per rimodellare correttamente i dati per iniziare su quel processo.

Grazie mille per qualsiasi aiuto.

+3

Solo un commento: non penso che sia ciò che di solito si intende per gente passando dal formato lungo al formato largo. – Frank

+0

Molti hanno commentato, ma sono sorpreso che nessuno si sia preoccupato di fissare un titolo così fuorviante (ora finito). – flodel

risposta

15

Ci sono diversi modi per farlo. reshape2 è un pacchetto utile. Personalmente, mi piace usare data.table

seguito è uno step-by-step

Se myDF è il vostro data.frame:

library(data.table) 
DT <- data.table(myDF) 

DT 

# this will get you your mean and SD's for each column 
DT[, sapply(.SD, function(x) list(mean=mean(x), sd=sd(x)))] 

# adding a `by` argument will give you the groupings 
DT[, sapply(.SD, function(x) list(mean=mean(x), sd=sd(x))), by=ID] 

# If you would like to round the values: 
DT[, sapply(.SD, function(x) list(mean=round(mean(x), 3), sd=round(sd(x), 3))), by=ID] 

# If we want to add names to the columns 
wide <- setnames(DT[, sapply(.SD, function(x) list(mean=round(mean(x), 3), sd=round(sd(x), 3))), by=ID], c("ID", sapply(names(DT)[-1], paste0, c(".men", ".SD")))) 

wide 

    ID Obs.1.men Obs.1.SD Obs.2.men Obs.2.SD Obs.3.men Obs.3.SD 
1: 1 35.333 8.021 36.333 10.214  33.0 9.644 
2: 2 29.750 3.594 32.250 4.193  30.5 5.916 
3: 3 41.500 4.950 43.500 4.950  39.0 4.243 

Inoltre, questo può o non può essere utile

> DT[, sapply(.SD, summary), .SDcols=names(DT)[-1]] 
     Obs.1 Obs.2 Obs.3 
Min. 25.00 28.00 22.00 
1st Qu. 29.00 31.00 27.00 
Median 33.00 32.00 36.00 
Mean 34.22 36.11 33.22 
3rd Qu. 38.00 40.00 37.00 
Max. 45.00 48.00 42.00 
17

Ecco probabilmente t egli modo più semplice per andare su di esso (con una reproducible example):

library(plyr) 
df <- data.frame(ID=rep(1:3, 3), Obs_1=rnorm(9), Obs_2=rnorm(9), Obs_3=rnorm(9)) 
ddply(df, .(ID), summarize, Obs_1_mean=mean(Obs_1), Obs_1_std_dev=sd(Obs_1), 
    Obs_2_mean=mean(Obs_2), Obs_2_std_dev=sd(Obs_2)) 

    ID Obs_1_mean Obs_1_std_dev Obs_2_mean Obs_2_std_dev 
1 1 -0.13994642  0.8258445 -0.15186380  0.4251405 
2 2 1.49982393  0.2282299 0.50816036  0.5812907 
3 3 -0.09269806  0.6115075 -0.01943867  1.3348792 

EDIT: L'approccio seguito consente di risparmiare un sacco di battitura quando si tratta di molte colonne.

ddply(df, .(ID), colwise(mean)) 

    ID  Obs_1  Obs_2  Obs_3 
1 1 -0.3748831 0.1787371 1.0749142 
2 2 -1.0363973 0.0157575 -0.8826969 
3 3 1.0721708 -1.1339571 -0.5983944 

ddply(df, .(ID), colwise(sd)) 

    ID  Obs_1  Obs_2  Obs_3 
1 1 0.8732498 0.4853133 0.5945867 
2 2 0.2978193 1.0451626 0.5235572 
3 3 0.4796820 0.7563216 1.4404602 
+1

C'è un'altra osservazione che ti sei perso. Mentre questo è il modo di andare con meno colonne, penso che diventi brutto molto rapidamente. – Arun

+0

'options (width = 300)' – mike

8

Ecco un altro introito sulle data.table risposte, utilizzando @ i dati di Carson, che è un po 'più leggibile (e anche un po' più veloce, a causa dell'utilizzo di lapply invece di sapply):

library(data.table) 
set.seed(1) 
dt = data.table(ID=c(1:3), Obs_1=rnorm(9), Obs_2=rnorm(9), Obs_3=rnorm(9)) 

dt[, c(mean = lapply(.SD, mean), sd = lapply(.SD, sd)), by = ID] 
# ID mean.Obs_1 mean.Obs_2 mean.Obs_3 sd.Obs_1 sd.Obs_2 sd.Obs_3 
#1: 1 0.4854187 -0.3238542 0.7410611 1.1108687 0.2885969 0.1067961 
#2: 2 0.4171586 -0.2397030 0.2041125 0.2875411 1.8732682 0.3438338 
#3: 3 -0.3601052 0.8195368 -0.4087233 0.8105370 0.3829833 1.4705692 
+0

il secondo dovrebbe usare 'sd' e si usa' .SD' due volte .. c'è un problema di prestazioni dovuto a questo? qualche idea? – Arun

+0

@Arun, grazie, corretto il bit 'sd'. Non so se ci sia un successo nelle prestazioni per questo, fammi controllare – eddi

+0

@Arun sembra che ci sia un ~ 10% di successo nelle prestazioni, ma la buona notizia è che non aumenta con più categorie – eddi

26

Questo è un problema di aggregazione, non un problema di rimodellamento come la domanda originariamente suggerita: desideriamo aggregare ogni colonna in una deviazione media e standard per ID. Ci sono molti pacchetti che gestiscono tali problemi. Nella base di R può essere fatto utilizzando aggregate simili (supponendo DF è il frame di dati in ingresso):

ag <- aggregate(. ~ ID, DF, function(x) c(mean = mean(x), sd = sd(x))) 

Nota 1: Un commentatore sottolineato che ag è un frame di dati per cui alcune colonne sono matrici. Sebbene all'inizio possa sembrare strano, in realtà semplifica l'accesso. ag ha lo stesso numero di colonne dell'input DF.La prima colonna ag[[1]] è ID e la colonna iod del resto ag[[i+1]] (o equivalente a livello di mercato ag[-1][[i]]) è la matrice delle statistiche per la colonna di osservazione di input. Se si desidera accedere alla statistica j dell'osservazione, è quindi ag[[i+1]][, j] che può anche essere scritto come ag[-1][[i]][, j].

D'altra parte, supponiamo che ci siano k colonne statistiche per ogni osservazione nell'input (dove k = 2 nella domanda). Quindi, se appiattiamo l'output per accedere alla statistica jth della colonna di osservazione ith, dobbiamo usare il più complesso ag[[k*(i-1)+j+1]] o equivalente ag[-1][[k*(i-1)+j]].

Ad esempio, confrontare la semplicità della prima espressione rispetto al secondo:

ag[-1][[2]] 
##  mean  sd 
## [1,] 36.333 10.2144 
## [2,] 32.250 4.1932 
## [3,] 43.500 4.9497 

ag_flat <- do.call("data.frame", ag) # flatten 
ag_flat[-1][, 2 * (2-1) + 1:2] 
## Obs_2.mean Obs_2.sd 
## 1  36.333 10.2144 
## 2  32.250 4.1932 
## 3  43.500 4.9497 

Nota 2: L'ingresso in forma riproducibile è:

Lines <- "ID Obs_1 Obs_2 Obs_3 
1 43  48  37 
1 27  29  22 
1 36  32  40 
2 33  38  36 
2 29  32  27 
2 32  31  35 
2 25  28  24 
3 45  47  42 
3 38  40  36" 
DF <- read.table(text = Lines, header = TRUE) 
+2

Forse importante notare: Mentre l'output di questo sembrerà un 'data.frame' con due colonne per ogni colonna che viene aggregata (risultante in 7 colonne con i dati di esempio), se si visualizza la struttura, vedrai che in realtà sono solo quattro colonne, con le colonne aggregate come * matrici *. È possibile risolvere il problema con un 'do.call (data.frame, aggregato (. ~ ID, DF, funzione (x) c (mean = mean (x), sd = sd (x)))) '. – A5C1D2H2I1M1N2O1R2T1

+0

@Ananda Mahto, buon punto. Ho aggiunto alcuni comitati che elaborano Questo. –

6

posso aggiungere la soluzione dplyr .

set.seed(1) 
df <- data.frame(ID=rep(1:3, 3), Obs_1=rnorm(9), Obs_2=rnorm(9), Obs_3=rnorm(9)) 

library(dplyr) 
df %>% group_by(ID) %>% summarise_each(funs(mean, sd)) 

#  ID Obs_1_mean Obs_2_mean Obs_3_mean Obs_1_sd Obs_2_sd Obs_3_sd 
# (int)  (dbl)  (dbl)  (dbl)  (dbl)  (dbl)  (dbl) 
# 1  1 0.4854187 -0.3238542 0.7410611 1.1108687 0.2885969 0.1067961 
# 2  2 0.4171586 -0.2397030 0.2041125 0.2875411 1.8732682 0.3438338 
# 3  3 -0.3601052 0.8195368 -0.4087233 0.8105370 0.3829833 1.4705692