web-dev-qa-db-ja.com

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

「ユーザー」のアクションを収集する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.matrixなどの使用も検討しましたが、プロセスを高速化する大きな成功はありませんでした。

73
MartinT

リストの各要素にアクセスするには、たとえば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 
91
Simon O'Hanlon

この答えに出くわし、実際には両方のグループ(その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
8
Aus_10

バージョン0.8.0から、dplyrgroup_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)
6
tmfmnk