Dividi un frame di dati di grandi dimensioni in un elenco di frame di dati in base al valore comune nella colonna


88

Ho un data frame con 10 colonne, che raccolgono le azioni degli "utenti", dove una delle colonne contiene un ID (non univoco, identificativo dell'utente) (colonna 10). la lunghezza del frame di dati è di circa 750000 righe. Sto cercando di estrarre singoli frame di dati (ottenendo così un elenco o un vettore di frame di dati) suddivisi per colonna contenente l'identificatore "utente", per isolare le azioni di un singolo attore.

ID | Data1 | Data2 | ... | UserID
1  | aaa   | bbb   | ... | u_001
2  | aab   | bb2   | ... | u_001
3  | aac   | bb3   | ... | u_001
4  | aad   | bb4   | ... | u_002

risultante in

list(
ID | Data1 | Data2 | ... | UserID
1  | aaa   | bbb   | ... | u_001
2  | aab   | bb2   | ... | u_001
3  | aac   | bb3   | ... | u_001
,
4  | aad   | bb4   | ... | u_002
...)

Quanto segue funziona molto bene per me su un piccolo campione (1000 righe):

paths = by(smallsampleMat, smallsampleMat[,"userID"], function(x) x)

e quindi accedendo all'elemento che desidero tramite percorsi [1], ad esempio.

Quando si applica sul frame di dati di grandi dimensioni originale o anche su una rappresentazione a matrice, questa soffoca la mia macchina (4 GB di RAM, MacOSX 10.6, R 2.15) e non si completa mai (so che esiste una versione R più recente, ma credo che questo non sia il problema principale ).

Sembra che la divisione sia più performante e dopo molto tempo si completa, ma non so (conoscenza R inferiore) come comporre l'elenco risultante di vettori in un vettore di matrici.

path = split(smallsampleMat, smallsampleMat[,10]) 

Ho preso in considerazione anche l'utilizzo di big.matrixecc., Ma senza molto successo ciò velocizzerebbe il processo.

Risposte:


104

Puoi accedere facilmente a ogni elemento nell'elenco usando ad es path[[1]]. Non è possibile inserire un insieme di matrici in un vettore atomico e accedere a ciascun elemento. Una matrice è un vettore atomico con attributi di dimensione. Userei la struttura dell'elenco restituita da split, è ciò per cui è stata progettata. Ogni elemento dell'elenco può contenere dati di diversi tipi e dimensioni, quindi è molto versatile e puoi utilizzare le *applyfunzioni per operare ulteriormente su ciascun elemento nell'elenco. Esempio sotto.

#  For reproducibile data
set.seed(1)

#  Make some data
userid <- rep(1:2,times=4)
data1 <- replicate(8 , paste( sample(letters , 3 ) , collapse = "" ) )
data2 <- sample(10,8)
df <- data.frame( userid , data1 , data2 )

#  Split on userid
out <- split( df , f = df$userid )
#$`1`
#  userid data1 data2
#1      1   gjn     3
#3      1   yqp     1
#5      1   rjs     6
#7      1   jtw     5

#$`2`
#  userid data1 data2
#2      2   xfv     4
#4      2   bfe    10
#6      2   mrx     2
#8      2   fqd     9

Accedi a ogni elemento utilizzando l' [[operatore in questo modo:

out[[1]]
#  userid data1 data2
#1      1   gjn     3
#3      1   yqp     1
#5      1   rjs     6
#7      1   jtw     5

Oppure usa una *applyfunzione per eseguire ulteriori operazioni su ogni elemento della lista. Ad esempio, per prendere la media della data2colonna potresti usare saply in questo modo:

sapply( out , function(x) mean( x$data2 ) )
#   1    2 
#3.75 6.25 

2
Mi chiedevo le prestazioni di dlply(df, .(userid))e ho scoperto che è pessimo rispetto splitanche senza coinvolgere il tempo di esecuzione di require(plyr), grazie e OP!
Francis

21

Dalla versione 0.8.0, dplyroffre una comoda funzione chiamata group_split():

# On sample data from @Aus_10
df %>%
  group_split(g)

[[1]]
# A tibble: 25 x 3
   ran_data1 ran_data2 g    
       <dbl>     <dbl> <fct>
 1     2.04      0.627 A    
 2     0.530    -0.703 A    
 3    -0.475     0.541 A    
 4     1.20     -0.565 A    
 5    -0.380    -0.126 A    
 6     1.25     -1.69  A    
 7    -0.153    -1.02  A    
 8     1.52     -0.520 A    
 9     0.905    -0.976 A    
10     0.517    -0.535 A    
# … with 15 more rows

[[2]]
# A tibble: 25 x 3
   ran_data1 ran_data2 g    
       <dbl>     <dbl> <fct>
 1     1.61      0.858 B    
 2     1.05     -1.25  B    
 3    -0.440    -0.506 B    
 4    -1.17      1.81  B    
 5     1.47     -1.60  B    
 6    -0.682    -0.726 B    
 7    -2.21      0.282 B    
 8    -0.499     0.591 B    
 9     0.711    -1.21  B    
10     0.705     0.960 B    
# … with 15 more rows

Per non includere la colonna di raggruppamento:

df %>%
 group_split(g, keep = FALSE)

C'è un modo per nominare l'elenco utilizzando la colonna di raggruppamento rilasciata? So di poterlo fare: ma mi chiedevo se ci fosse un modo per farlo all'interno di dplyr? Names (f.vars.h1.list) <- unique (f.vars.to.agg.1h $ ActivityGroup)
d3hero23

9

Sono incappato in questa risposta e in realtà volevo ENTRAMBI i gruppi (dati contenenti quell'unico utente e dati contenenti tutto tranne quell'unico utente). Non necessario per le specifiche di questo post, ma ho pensato di aggiungere nel caso qualcuno stesse cercando su Google il mio stesso problema.

df <- data.frame(
     ran_data1=rnorm(125),
     ran_data2=rnorm(125),
     g=rep(factor(LETTERS[1:5]), 25)
 )

test_x = split(df,df$g)[['A']]
test_y = split(df,df$g!='A')[['TRUE']]

Ecco come appare:

head(test_x)
            x          y g
1   1.1362198  1.2969541 A
6   0.5510307 -0.2512449 A
11  0.0321679  0.2358821 A
16  0.4734277 -1.2889081 A
21 -1.2686151  0.2524744 A

> head(test_y)
            x          y g
2 -2.23477293  1.1514810 B
3 -0.46958938 -1.7434205 C
4  0.07365603  0.1111419 D
5 -1.08758355  0.4727281 E
7  0.28448637 -1.5124336 B
8  1.24117504  0.4928257 C
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.