Tách một khung dữ liệu lớn thành danh sách các khung dữ liệu dựa trên giá trị chung trong cột


86

Tôi có một khung dữ liệu với 10 cột, thu thập các hành động của "người dùng", trong đó một trong các cột chứa ID (không phải duy nhất, xác định người dùng) (cột 10). độ dài của khung dữ liệu là khoảng 750000 hàng. Tôi đang cố gắng trích xuất các khung dữ liệu riêng lẻ (để lấy danh sách hoặc vectơ của các khung dữ liệu) được chia theo cột chứa mã định danh "người dùng", để tách biệt các hành động của một tác nhân.

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

dẫn đến

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

Những điều sau đây hoạt động rất tốt đối với tôi trên một mẫu nhỏ (1000 hàng):

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

và sau đó truy cập phần tử tôi muốn bằng các đường dẫn [1] chẳng hạn.

Khi áp dụng trên khung dữ liệu lớn ban đầu hoặc thậm chí là biểu diễn ma trận, điều này làm cho máy của tôi bị nghẹt (RAM 4GB, MacOSX 10.6, R 2.15) và không bao giờ hoàn thành (tôi biết rằng có phiên bản R mới hơn, nhưng tôi tin rằng đây không phải là vấn đề chính ).

Có vẻ như việc chia tách có hiệu suất cao hơn và sau một thời gian dài mới hoàn thành, nhưng tôi không biết (kiến thức R kém hơn) làm thế nào để ghép danh sách kết quả của các vectơ thành một vectơ ma trận.

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

Tôi cũng đã xem xét sử dụng big.matrixvv, nhưng không có nhiều thành công sẽ tăng tốc quá trình.

Câu trả lời:


103

Bạn có thể dễ dàng truy cập từng phần tử trong danh sách bằng cách sử dụng vd path[[1]]. Bạn không thể đặt một tập hợp các ma trận vào một vector nguyên tử và truy cập từng phần tử. Ma trận là một vectơ nguyên tử với các thuộc tính thứ nguyên. Tôi sẽ sử dụng cấu trúc danh sách được trả về split, nó được thiết kế để làm gì. Mỗi phần tử danh sách có thể chứa dữ liệu với nhiều kiểu và kích thước khác nhau, vì vậy nó rất linh hoạt và bạn có thể sử dụng các *applyhàm để thao tác thêm trên từng phần tử trong danh sách. Ví dụ bên dưới.

#  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

Truy cập từng phần tử bằng [[toán tử như sau:

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

Hoặc sử dụng một *applyhàm để thực hiện các thao tác tiếp theo trên mỗi phần tử danh sách. Ví dụ, để lấy giá trị trung bình của data2cột, bạn có thể sử dụng sapply như sau:

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

2
Tôi đã tự hỏi hiệu suất của dlply(df, .(userid))và thấy rằng nó là tệ so với splitthậm chí không liên quan đến thời gian chạy của require(plyr), cảm ơn bạn và OP!
Francis

18

Từ phiên bản 0.8.0, dplyrcung cấp một chức năng tiện dụng được gọi là 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

Để không bao gồm cột nhóm:

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

9

Tình cờ gặp câu trả lời này và tôi thực sự muốn CẢ HAI nhóm (dữ liệu chứa một người dùng và dữ liệu chứa mọi thứ trừ một người dùng đó). Không cần thiết đối với các chi tiết cụ thể của bài đăng này, nhưng tôi nghĩ tôi sẽ thêm trong trường hợp ai đó đang tìm kiếm vấn đề giống tôi trên Google.

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

Đây là những gì nó trông giống như:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.