列の共通値に基づいて、大きなデータフレームをデータフレームのリストに分割します


86

「ユーザー」のアクションを収集する10列のデータフレームがあり、列の1つにID(一意ではなく、ユーザーを識別する)が含まれています(列10)。データフレームの長さは約750000行です。単一のアクターのアクションを分離するために、「ユーザー」識別子を含む列で分割された個々のデータフレームを抽出しようとしています(データフレームのリストまたはベクトルを取得します)。

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

結果として

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

以下は、小さなサンプル(1000行)で非常にうまく機能します。

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

次に、たとえばpaths [1]を使用して必要な要素にアクセスします。

元の大きなデータフレームまたはマトリックス表現に適用すると、これは私のマシン(4GB RAM、MacOSX 10.6、R 2.15)を窒息させ、完了しません(新しいRバージョンが存在することは知っていますが、これが主な問題ではないと思います)。

分割の方がパフォーマンスが高く、長い時間が経過した後のようですが、結果のベクトルのリストを行列のベクトルに分割する方法がわかりません(Rの知識が劣っています)。

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

私はbig.matrixetcの使用も検討しましたが、あまり成功しなかったため、プロセスがスピードアップしました。

回答:


103

たとえば、を使用して、リスト内の各要素に簡単にアクセスできますpath[[1]]。行列のセットを原子ベクトルに入れて、各要素にアクセスすることはできません。行列は、次元属性を持つ原子ベクトルです。によって返されるリスト構造を使用splitします。これは、そのために設計されたものです。各リスト要素はさまざまなタイプとサイズのデータ​​を保持できるため、非常に用途が広く、*apply関数を使用してリスト内の各要素をさらに操作できます。以下の例。

#  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

次の[[ような演算子を使用して、各要素にアクセスします。

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

または、*apply関数を使用して、各リスト要素に対してさらに操作を実行します。たとえば、data2列の平均を取るには、次のようにsapplyを使用できます。

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

2
のパフォーマンスに疑問をdlply(df, .(userid))持っていたのですsplitが、の実行時間を含まなくても、それに比べて悪いことがわかりましたrequire(plyr)、ありがとう、OP!
フランシス

18

バージョン0.8.0からdplyr、次のような便利な関数が提供されます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

グループ化列を含めない場合:

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

9

この答えに出くわし、実際には両方のグループ(その1人のユーザーを含むデータとその1人以外のすべてを含むデータ)が必要でした。この投稿の詳細には必要ありませんが、誰かが私と同じ問題をグーグルで検索している場合に備えて追加したいと思いました。

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

外観は次のとおりです。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.