web-dev-qa-db-fra.com

Fractionner une grande trame de données en une liste de trames de données en fonction de la valeur commune dans la colonne

J'ai un cadre de données avec 10 colonnes, rassemblant les actions des "utilisateurs", où l'une des colonnes contient un ID (non unique, identifiant utilisateur) (colonne 10). la longueur du cadre de données est d'environ 750000 lignes. J'essaie d'extraire des trames de données individuelles (afin d'obtenir une liste ou un vecteur de trames de données) divisées par la colonne contenant l'identifiant "utilisateur", afin d'isoler les actions d'un seul acteur.

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

résultant en

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
...)

Ce qui suit fonctionne très bien pour moi sur un petit échantillon (1000 lignes):

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

puis en accédant à l'élément que je veux par des chemins [1] par exemple.

Lorsque j'applique sur le cadre de données volumineux d'origine ou même sur une représentation matricielle, ma machine (4 Go de RAM, MacOSX 10.6, R 2.15) s'étouffe et ne termine jamais (je sais qu'une version R plus récente existe, mais je pense que ce n'est pas le principal problème ).

Il semble que la scission soit plus performante et se termine après une longue période, mais je ne sais pas (connaissance de R inférieur) comment découper la liste de vecteurs résultante en un vecteur de matrices.

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

J'ai aussi envisagé d'utiliser big.matrix etc., mais sans grand succès, cela accélérerait le processus.

73
MartinT

Vous pouvez tout aussi facilement accéder à chaque élément de la liste en utilisant, par exemple, path[[1]]. Vous ne pouvez pas mettre un ensemble de matrices dans un vecteur atomique et accéder à chaque élément. Une matrice est un vecteur atomique avec des attributs de dimension. J'utiliserais la structure de liste renvoyée par split, c'est pour cela qu'elle a été conçue. Chaque élément de la liste peut contenir des données de types et de tailles différents, ce qui le rend très polyvalent et vous permet d’utiliser *apply fonctions permettant d’agir sur chaque élément de la liste. Exemple ci-dessous.

#  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

Accédez à chaque élément en utilisant le [[ opérateur comme ceci:

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

Ou utilisez un *apply fonction pour effectuer des opérations supplémentaires sur chaque élément de la liste. Par exemple, prenons la moyenne du data2 la colonne que vous pourriez utiliser comme ceci:

sapply( out , function(x) mean( x$data2 ) )
#   1    2 
#3.75 6.25 
91
Simon O'Hanlon

Je suis tombé par hasard sur cette réponse et je voulais à la fois DEUX groupes (des données contenant cet utilisateur et des données contenant tout sauf cet utilisateur). Pas nécessaire pour les détails de ce post, mais je pensais ajouter si quelqu'un cherchait sur Google le même problème que moi.

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']]

Voici à quoi ça ressemble:

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
8
Aus_10

Depuis la version 0.8.0, dplyr offre une fonction pratique appelée 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

Pour ne pas inclure la colonne de regroupement:

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