Teilen Sie einen großen Datenrahmen basierend auf dem gemeinsamen Wert in der Spalte in eine Liste von Datenrahmen auf

85

Ich habe einen Datenrahmen mit 10 Spalten, in dem Aktionen von "Benutzern" gesammelt werden, wobei eine der Spalten eine ID enthält (nicht eindeutig, Benutzer identifizierend) (Spalte 10). Die Länge des Datenrahmens beträgt ungefähr 750000 Zeilen. Ich versuche, einzelne Datenrahmen zu extrahieren (um eine Liste oder einen Vektor von Datenrahmen zu erhalten), die durch die Spalte mit der Kennung "Benutzer" aufgeteilt sind, um die Aktionen eines einzelnen Akteurs zu isolieren.

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

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

Folgendes funktioniert bei einer kleinen Stichprobe (1000 Zeilen) sehr gut für mich:

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

und dann zum Beispiel über Pfade [1] auf das gewünschte Element zugreifen.

Wenn Sie auf den ursprünglichen großen Datenrahmen oder sogar eine Matrixdarstellung anwenden, wird mein Computer (4 GB RAM, MacOSX 10.6, R 2.15) erstickt und nie fertiggestellt (ich weiß, dass eine neuere R-Version vorhanden ist, aber ich glaube, dass dies nicht das Hauptproblem ist ).

Es scheint, dass die Aufteilung performanter ist und nach langer Zeit abgeschlossen ist, aber ich weiß nicht (minderwertiges R-Wissen), wie die resultierende Liste von Vektoren in einen Vektor von Matrizen zerlegt werden soll.

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

Ich habe darüber nachgedacht, auch big.matrixetc zu verwenden, aber ohne großen Erfolg würde dies den Prozess beschleunigen.

MartinT
quelle

Antworten:

103

Sie können genauso einfach auf jedes Element in der Liste zugreifen, indem Sie z path[[1]]. Sie können keine Matrizen in einen Atomvektor einfügen und auf jedes Element zugreifen. Eine Matrix ist ein Atomvektor mit Dimensionsattributen. Ich würde die von zurückgegebene Listenstruktur verwenden split, für die sie entwickelt wurde. Jedes Listenelement kann Daten unterschiedlicher Typen und Größen enthalten, sodass es sehr vielseitig ist und Sie *applyFunktionen verwenden können, um jedes Element in der Liste weiter zu bearbeiten. Beispiel unten.

#  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

Greifen Sie mit dem [[Operator wie folgt auf jedes Element zu :

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

Oder verwenden Sie eine *applyFunktion, um weitere Operationen für jedes Listenelement auszuführen. Um beispielsweise den Mittelwert der data2Spalte zu ermitteln, können Sie sapply wie folgt verwenden:

sapply( out , function(x) mean( x$data2 ) )
#   1    2 
#3.75 6.25 
Simon O'Hanlon
quelle
2
Ich habe mich über die Leistung von gewundert dlply(df, .(userid))und festgestellt, dass es schlecht ist im Vergleich zu splitauch ohne die Laufzeit von require(plyr), danke und OP!
Francis
17

Ab Version 0.8.0 dplyrbietet eine praktische Funktion namens 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

So schließen Sie die Gruppierungsspalte nicht ein:

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

Ich bin über diese Antwort gestolpert und wollte eigentlich BEIDE Gruppen (Daten, die diesen einen Benutzer enthalten, und Daten, die alles außer diesem einen Benutzer enthalten). Für die Einzelheiten dieses Beitrags nicht erforderlich, aber ich dachte, ich würde hinzufügen, falls jemand das gleiche Problem wie ich googelt.

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

So sieht es aus:

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