Tạo khung dữ liệu R theo từng hàng


107

Tôi muốn tạo khung dữ liệu theo từng hàng trong R. Tôi đã thực hiện một số tìm kiếm và tất cả những gì tôi nghĩ ra là đề xuất tạo một danh sách trống, giữ một vô hướng chỉ mục danh sách, sau đó mỗi lần thêm vào danh sách khung dữ liệu một hàng và nâng chỉ mục danh sách lên từng hàng một. Cuối cùng, do.call(rbind,)trong danh sách.

Trong khi điều này hoạt động, nó có vẻ rất cồng kềnh. Không có cách nào dễ dàng hơn để đạt được cùng một mục tiêu?

Rõ ràng là tôi đề cập đến các trường hợp tôi không thể sử dụng một số applychức năng và rõ ràng cần phải tạo khung dữ liệu theo từng hàng. Ít nhất, có cách nào để pushvào cuối danh sách thay vì theo dõi rõ ràng chỉ mục cuối cùng được sử dụng không?


1
Bạn có thể sử dụng append()[mà có lẽ nên được đặt tên là insert] hoặc c()để thêm các mục vào cuối danh sách, mặc dù sẽ không giúp được gì cho bạn ở đây.
hatmatrix

Không có nhiều chức năng trong R mà khung dữ liệu trở lại trừ khi bạn trả lại [row-khôn ngoan] từ lapply(), Map()và như vậy, nhưng bạn cũng có thể muốn xem xét aggregate(), dapply() {heR.Misc}cast() {reshape}để xem nếu nhiệm vụ của bạn không thể được xử lý bởi những các hàm (tất cả đều trả về khung dữ liệu).
hatmatrix

Câu trả lời:


96

Bạn có thể phát triển chúng từng hàng bằng cách thêm hoặc sử dụng rbind().

Điều đó không có nghĩa là bạn nên làm. Cấu trúc phát triển động là một trong những cách kém hiệu quả nhất để viết mã trong R.

Nếu bạn có thể, hãy phân bổ toàn bộ data.frame của bạn lên trước:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

và sau đó trong quá trình hoạt động của bạn, hãy chèn hàng cùng một lúc

DF[i, ] <- list(1.4, "foo")

Điều đó sẽ hoạt động đối với data.frame tùy ý và hiệu quả hơn nhiều. Nếu bạn vượt quá N, bạn luôn có thể thu nhỏ các hàng trống ở cuối.


6
Không phải bạn có ý định đặt N thay vì 10 và liệt kê (1.4, "foo") thay vì c (1.4, "foo") để không ép 1.4 vào chế độ ký tự?
hatmatrix

Có, tôi muốn sử dụng N trong việc tạo data.frame. Ngoài ra, rất tốt để nắm bắt được sự ép buộc vào trò chuyện - tôi đã bỏ lỡ điều đó.
Dirk Eddelbuettel

1
Tốt hơn là bạn nên chỉnh sửa câu trả lời hơn là để nó trong phần bình luận. Tôi bối rối khi cố gắng mò mẫm câu trả lời này.
Người dùng

4
data.tabledường như thậm chí còn nhanh hơn so với phân bổ trước bằng data.frames. Kiểm tra ở đây: stackoverflow.com/a/11486400/636656
Ari B. Friedman

điều này có còn đúng trong R 3.1, nơi điều này sẽ nhanh hơn?
userJT

49

Người ta có thể thêm hàng vào NULL:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

ví dụ

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)

3
nó ra một ma trận, không phải là một khung dữ liệu
Olga

1
@Olga Chỉ khi bạn liên kết các hàng phần tử có kiểu bằng nhau - BTW trong trường hợp đó tốt hơn nên sapply(hoặc vectorise) và chuyển vị.
mbq

1
@mbq Chính xác những gì tôi đang làm. Tôi cũng thấy rằng nếu bạn khởi tạo nó bằng df <-data.frame (), nó sẽ xuất ra một khung dữ liệu.
Olga

9

Đây là một ví dụ ngớ ngẩn về cách sử dụng do.call(rbind,)trên đầu ra của Map()[tương tự như lapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

Tôi sử dụng cấu trúc này khá thường xuyên.


8

Lý do tôi thích Rcpp đến vậy là tôi không phải lúc nào cũng hiểu R Core suy nghĩ như thế nào, và với Rcpp, tôi không cần phải làm vậy.

Nói một cách triết học, bạn đang ở trong tình trạng tội lỗi liên quan đến mô hình chức năng, mô hình cố gắng đảm bảo rằng mọi giá trị xuất hiện độc lập với mọi giá trị khác; việc thay đổi một giá trị sẽ không bao giờ gây ra thay đổi rõ ràng trong một giá trị khác, theo cách bạn nhận được với con trỏ chia sẻ biểu diễn trong C.

Các vấn đề nảy sinh khi lập trình chức năng ra hiệu cho chiếc tàu nhỏ di chuyển và chiếc tàu nhỏ trả lời "Tôi là một ngọn hải đăng". Thực hiện một loạt các thay đổi nhỏ đối với một đối tượng lớn mà bạn muốn xử lý trong thời gian chờ đợi sẽ đưa bạn vào lãnh thổ của ngọn hải đăng.

Trong C ++ STL, push_back()là một cách sống. Nó không cố gắng hoạt động, nhưng nó cố gắng đáp ứng các thành ngữ lập trình phổ biến một cách hiệu quả .

Với một số thông minh đằng sau hậu trường, đôi khi bạn có thể sắp xếp để có một chân trong mỗi thế giới. Hệ thống tệp dựa trên ảnh chụp nhanh là một ví dụ điển hình (được phát triển từ các khái niệm như liên kết gắn kết, cũng phân chia cả hai bên).

Nếu R Core muốn làm điều này, bộ lưu trữ vectơ bên dưới có thể hoạt động giống như một liên kết gắn kết. Một tham chiếu đến bộ lưu trữ vectơ có thể hợp lệ cho các chỉ số con 1:N, trong khi một tham chiếu khác đến cùng một bộ nhớ có giá trị cho các chỉ số con 1:(N+1). Có thể có bộ nhớ dự trữ chưa được tham chiếu hợp lệ bởi bất kỳ thứ gì nhưng thuận tiện cho việc nhanh chóng push_back(). Bạn không vi phạm khái niệm chức năng khi thêm vào bên ngoài phạm vi mà bất kỳ tham chiếu hiện có nào đều coi là hợp lệ.

Cuối cùng nối các hàng tăng dần, bạn hết dung lượng lưu trữ. Bạn sẽ cần tạo các bản sao mới của mọi thứ, với dung lượng được nhân với một số gia số. Các triển khai STL mà tôi sử dụng có xu hướng nhân bộ nhớ lên 2 khi mở rộng phân bổ. Tôi nghĩ rằng tôi đã đọc trong R Internals rằng có cấu trúc bộ nhớ trong đó bộ nhớ tăng 20%. Dù bằng cách nào, các phép toán tăng trưởng xảy ra với tần số logarit liên quan đến tổng số phần tử được thêm vào. Trên cơ sở khấu hao, điều này thường được chấp nhận.

Khi những mánh khóe đằng sau hậu trường diễn ra, tôi đã thấy tệ hơn. Mỗi khi bạn đặt push_back()một hàng mới vào khung dữ liệu, cấu trúc chỉ mục cấp cao nhất sẽ cần được sao chép. Hàng mới có thể nối vào đại diện được chia sẻ mà không ảnh hưởng đến bất kỳ giá trị chức năng cũ nào. Tôi thậm chí không nghĩ rằng nó sẽ phức tạp nhiều cho người thu gom rác; vì tôi không đề xuất push_front()tất cả các tham chiếu đều là tham chiếu tiền tố cho phía trước của bộ lưu trữ vectơ được cấp phát.


2

Câu trả lời của Dirk Eddelbuettel là tốt nhất; ở đây tôi chỉ lưu ý rằng bạn có thể tránh khỏi việc không chỉ định trước kích thước khung dữ liệu hoặc kiểu dữ liệu, điều này đôi khi hữu ích nếu bạn có nhiều kiểu dữ liệu và nhiều cột:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(df,row2) #now this works as you'd expect.

Ý bạn là df<-rbind(df, row2)?
Timothy C. Quinn

1

Tôi đã tìm ra cách này để tạo khung dữ liệu bằng thô mà không cần ma trận.

Với tên cột tự động

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

Với tên cột

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )

0

Nếu bạn có các vectơ được định sẵn để trở thành hàng, hãy nối chúng bằng cách sử dụng c(), chuyển chúng vào một ma trận từng hàng và chuyển đổi ma trận đó thành khung dữ liệu.

Ví dụ, hàng

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

có thể được chuyển đổi thành khung dữ liệu do đó:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

Phải thừa nhận rằng tôi thấy có 2 hạn chế chính: (1) điều này chỉ hoạt động với dữ liệu ở chế độ đơn và (2) bạn phải biết # cột cuối cùng của mình để điều này hoạt động (tức là, tôi giả sử rằng bạn không làm việc với mảng rách rưới có độ dài hàng lớn nhất là tiên nghiệm chưa biết ).

Giải pháp này có vẻ đơn giản, nhưng từ kinh nghiệm của tôi với chuyển đổi loại trong R, tôi chắc chắn rằng nó tạo ra những thách thức mới. Bất cứ ai có thể bình luận về điều này?


0

Tùy thuộc vào định dạng của hàng mới, bạn có thể sử dụng tibble::add_rownếu hàng mới của bạn đơn giản và có thể được chỉ định trong "cặp giá trị". Hoặc bạn có thể sử dụng dplyr::bind_rows, "cách triển khai hiệu quả mô hình chung của do.call (rbind, dfs)".

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.