2016-02-10 26 views
8

Tôi đang cố đếm số cột không chứa NA cho mỗi hàng và đặt giá trị đó vào cột mới cho hàng đó.R: data.table count! NA mỗi hàng

Ví dụ dữ liệu:

library(data.table) 

a = c(1,2,3,4,NA) 
b = c(6,NA,8,9,10) 
c = c(11,12,NA,14,15) 
d = data.table(a,b,c) 

> d 
    a b c 
1: 1 6 11 
2: 2 NA 12 
3: 3 8 NA 
4: 4 9 14 
5: NA 10 15 

đầu ra của tôi mong muốn sẽ bao gồm một cột mới num_obs trong đó có số lượng phi NA mục mỗi dòng:

a b c num_obs 
1: 1 6 11  3 
2: 2 NA 12  2 
3: 3 8 NA  2 
4: 4 9 14  3 
5: NA 10 15  2 

Tôi đã đọc hàng giờ tại và cho đến nay, tốt nhất tôi đã đưa ra là lặp qua các hàng, mà tôi biết là không bao giờ được khuyến khích trong R hoặc data.table. Tôi chắc chắn có một cách tốt hơn để làm điều này, xin vui lòng khai sáng cho tôi.

cách crappy của tôi:

len = (1:NROW(d)) 
for (n in len) { 
    d[n, num_obs := length(which(!is.na(d[n])))] 
} 
+1

Giống như 'd [, num_obs: = sum (! Is.na (.SD)), bởi = 1: nrow (d)] []' hoặc 'd [, num_obs: = rowSums (! Is.na (d))] [] '? (Không chắc chắn sẽ nhanh hơn.) – A5C1D2H2I1M1N2O1R2T1

+0

Có! điều này làm việc. Bạn có thể giải thích nó? Tôi nghĩ .SD sẽ bằng toàn bộ tập dữ liệu nếu tôi sử dụng 'by = 1: nrow (d)'. Làm thế nào nó làm điều này bằng hàng? chỉnh sửa: Ngoài ra, chính xác những gì thêm chuỗi rỗng '[]' làm gì? – Reilstein

Trả lời

13

Hãy thử này sử dụng Reduce đến chuỗi lại với nhau + cuộc gọi:

d[, num_obs := Reduce(`+`, lapply(.SD,function(x) !is.na(x)))] 

Nếu tốc độ là rất quan trọng, bạn có thể eek ra một cảm ứng hơn với đề nghị Ananda để hardcode số cột được đánh giá:

d[, num_obs := 4 - Reduce("+", lapply(.SD, is.na))] 

Đo điểm chuẩn sử dụng dữ liệu lớn hơn của Ananda. Có thể d từ trên cao:

fun1 <- function(indt) indt[, num_obs := rowSums(!is.na(indt))][] 
fun3 <- function(indt) indt[, num_obs := Reduce(`+`, lapply(.SD,function(x) !is.na(x)))][] 
fun4 <- function(indt) indt[, num_obs := 4 - Reduce("+", lapply(.SD, is.na))][] 

library(microbenchmark) 
microbenchmark(fun1(copy(d)), fun3(copy(d)), fun4(copy(d)), times=10L) 

#Unit: milliseconds 
#   expr  min  lq  mean median  uq  max neval 
# fun1(copy(d)) 3.565866 3.639361 3.912554 3.703091 4.023724 4.596130 10 
# fun3(copy(d)) 2.543878 2.611745 2.973861 2.664550 3.657239 4.011475 10 
# fun4(copy(d)) 2.265786 2.293927 2.798597 2.345242 3.385437 4.128339 10 
+0

Làm tốt lắm Thela :-) – A5C1D2H2I1M1N2O1R2T1

+0

Tùy chọn tốt. Cộng một. – akrun

+0

Có thể điều này sẽ đẩy nhanh tốc độ mô tả của OP: 'indt [, num_obs: = 4 - Reduce (" + ", lapply (.SD, is.na))] []'. Tôi đã mã hóa cứng "4" để tránh tính toán lại theo hàng. – A5C1D2H2I1M1N2O1R2T1

6

Hai tùy chọn một cách nhanh chóng tôi suy nghĩ là:

d[, num_obs := sum(!is.na(.SD)), by = 1:nrow(d)][] 
d[, num_obs := rowSums(!is.na(d))][] 

Các công trình đầu tiên bằng cách tạo ra một "nhóm" của chỉ một hàng cho mỗi nhóm (1:nrow(d)) . Không có điều đó, nó sẽ chỉ tổng hợp các giá trị NA trong toàn bộ bảng.

Phương pháp thứ hai sử dụng chức năng cơ sở R rất hiệu quả, rowSums.

Dưới đây là một chuẩn mực về dữ liệu lớn hơn:

set.seed(1) 
nrow = 10000 
ncol = 15 
d <- as.data.table(matrix(sample(c(NA, -5:10), nrow*ncol, TRUE), nrow = nrow, ncol = ncol)) 

fun1 <- function(indt) indt[, num_obs := rowSums(!is.na(indt))][] 
fun2 <- function(indt) indt[, num_obs := sum(!is.na(.SD)), by = 1:nrow(indt)][] 

library(microbenchmark) 
microbenchmark(fun1(copy(d)), fun2(copy(d))) 
# Unit: milliseconds 
#   expr  min   lq  mean  median   uq  max neval 
# fun1(copy(d)) 3.727958 3.906458 5.507632 4.159704 4.475201 106.5708 100 
# fun2(copy(d)) 584.499120 655.634889 684.889614 681.054752 712.428684 861.1650 100 

Bằng cách này, trống [] chỉ là in kết quả data.table. Điều này là bắt buộc khi bạn muốn trả về đầu ra từ các hàm set* trong "data.table".

+0

Điều này thật tuyệt vời, cảm ơn @AnandaMahto! Tôi là một chút bối rối về cách bạn tạo ra một nhóm của một hàng bằng cách sử dụng '1: nrow (d)'. Suy nghĩ ban đầu của tôi là câu lệnh này sẽ tạo một nhóm từ hàng 1 đến hàng cuối cùng của tệp. Tôi rõ ràng thiếu cách hoạt động của câu lệnh 'by' hoặc một cái gì đó. – Reilstein

+2

Điểm chuẩn của tôi cho thấy điều này nhanh hơn một lần nữa: 'fun3 <- function (indt) indt [, num_obs: = Reduce (\' + \ ', lapply (.SD, function (x)! Is.na (x)))] [] ' – thelatemail

+0

Vâng trên bộ dữ liệu lớn hơn của tôi, khoảng 115 so với 0,06s, do đó, một cải tiến đáng kể! Cảm ơn bạn đã thêm điều này! – Reilstein

Các vấn đề liên quan