2012-02-06 75 views
11

Có cờ gỡ lỗi có điều kiện mà tôi bỏ lỡ từ Matlab: dbstop if infnandescribed here. Nếu được đặt, điều kiện này sẽ dừng thực thi mã khi gặp phải Inf hoặc NaN (IIRC, Matlab không có NA).Cách buộc lỗi nếu các giá trị không có giá trị (NA, NaN hoặc Inf) gặp phải

Làm thế nào tôi có thể đạt được điều này trong R một cách hiệu quả hơn kiểm tra tất cả các đối tượng sau mỗi hoạt động gán?

Tại thời điểm này, cách duy nhất tôi thấy để làm điều này là thông qua hacks như sau:

  1. Chèn một thử nghiệm sau khi tất cả những nơi mà những giá trị này có thể gặp phải (ví dụ như một bộ phận, nơi phân chia bởi 0 có thể xảy ra). Thử nghiệm sẽ là sử dụng is.finite(), described in this Q & A, trên mọi phần tử.
  2. Sử dụng body() để sửa đổi mã để gọi một hàm riêng biệt, sau mỗi thao tác hoặc có thể chỉ mỗi phép gán, kiểm tra tất cả các đối tượng (và có thể tất cả các đối tượng trong mọi môi trường).
  3. Sửa mã nguồn của R (?!?)
  4. Cố gắng sử dụng tracemem để xác định các biến đã thay đổi và chỉ kiểm tra những giá trị này có giá trị không tốt.
  5. (Mới - xem chú thích 2) Sử dụng một số loại trình xử lý cuộc gọi/gọi lại để gọi hàm kiểm tra.

Tùy chọn thứ nhất là những gì tôi đang làm hiện nay. Điều này là tẻ nhạt, bởi vì tôi không thể đảm bảo tôi đã kiểm tra tất cả mọi thứ. Tùy chọn thứ 2 sẽ kiểm tra mọi thứ, ngay cả khi một đối tượng chưa được cập nhật. Đó là một sự lãng phí thời gian. Tùy chọn thứ 3 sẽ liên quan đến việc sửa đổi các nhiệm vụ của NA, NaN, và các giá trị vô hạn (+/- Inf), để một lỗi được tạo ra. Điều đó có vẻ như nó tốt hơn để lại R Core. Tùy chọn thứ 4 giống như thứ 2 - tôi cần một cuộc gọi đến một chức năng riêng biệt liệt kê tất cả các vị trí bộ nhớ, chỉ để ID những người đã thay đổi, và sau đó kiểm tra các giá trị; Tôi thậm chí không chắc chắn điều này sẽ làm việc cho tất cả các đối tượng, như một chương trình có thể làm một sửa đổi tại chỗ, mà có vẻ như nó sẽ không gọi chức năng duplicate.

Có cách tiếp cận nào tốt hơn mà tôi bị thiếu không? Có lẽ một số công cụ thông minh của Mark Bravington, Luke Tierney, hoặc một cái gì đó tương đối cơ bản - một cái gì đó giống như một tham số options() hoặc một lá cờ khi biên dịch R?

Ví dụ mã Dưới đây là một số mã ví dụ rất đơn giản để kiểm tra, kết hợp hàm addTaskCallback do Josh O'Brien đề xuất. Mã không bị gián đoạn, nhưng lỗi xảy ra trong trường hợp đầu tiên, trong khi không có lỗi xảy ra trong trường hợp thứ hai (ví dụ: badDiv(0,0,FALSE) không hủy bỏ). Tôi vẫn đang điều tra các cuộc gọi lại, vì điều này có vẻ đầy hứa hẹn.

badDiv <- function(x, y, flag){ 
    z = x/y 
    if(flag == TRUE){ 
     return(z) 
    } else { 
     return(FALSE) 
    } 
} 

addTaskCallback(stopOnNaNs) 
badDiv(0, 0, TRUE) 

addTaskCallback(stopOnNaNs) 
badDiv(0, 0, FALSE) 

Note 1. Tôi muốn được thỏa mãn với một giải pháp cho các hoạt động R tiêu chuẩn, mặc dù rất nhiều tính toán của tôi liên quan đến đối tượng sử dụng thông qua data.table hoặc bigmemory (ví dụ: bộ nhớ đĩa dựa trên ma trận ánh xạ). Chúng dường như có các hành vi bộ nhớ hơi khác so với các hoạt động ma trận chuẩn và data.frame.

Lưu ý 2. Ý tưởng gọi lại có vẻ hứa hẹn hơn một chút, vì điều này không yêu cầu tôi viết các hàm làm thay đổi mã R, ví dụ: qua ý tưởng body().

Lưu ý 3.Tôi không biết có hay không có một số cách đơn giản để kiểm tra sự hiện diện của các giá trị không hữu hạn, ví dụ: meta thông tin về các đối tượng mà chỉ mục nơi NA, Infs, vv được lưu trữ trong đối tượng, hoặc nếu chúng được lưu trữ tại chỗ. Cho đến nay, tôi đã thử gói inspect của Simon Urbanek và không tìm thấy cách nào để loại bỏ sự hiện diện của các giá trị không phải số.

Theo dõi: Simon Urbanek đã chỉ ra trong nhận xét rằng thông tin đó không có sẵn dưới dạng thông tin meta cho các đối tượng.

Lưu ý 4. Tôi vẫn đang thử nghiệm các ý tưởng được trình bày. Ngoài ra, theo đề xuất của Simon, việc kiểm tra sự hiện diện của các giá trị không hữu hạn sẽ nhanh nhất trong C/C++; nên vượt qua cả mã R được biên dịch, nhưng tôi mở mọi thứ. Đối với các tập dữ liệu lớn, ví dụ: theo thứ tự 10-50GB, đây sẽ là một khoản tiết kiệm đáng kể so với việc sao chép dữ liệu. Người ta có thể nhận được những cải thiện hơn nữa thông qua việc sử dụng nhiều lõi, nhưng đó là một chút tiên tiến hơn.

+0

Một số chức năng nguyên thủy đã chức năng này được xây dựng trong, tức là, họ trở về một lỗi hoặc một cảnh báo nếu được cung cấp hoặc cung cấp một tổ chức phi kết quả vô hạn. Lấy ví dụ, 'sin (Inf) '. Có lẽ đó là điều mà bạn có thể khám phá. –

+0

Vâng, không phải luôn luôn trường hợp mà một Inf hoặc NaN * nên * dừng chức năng/mã của bạn (NA là một trường hợp riêng biệt bởi vì nó cố ý sử dụng tất cả các thời gian như là một 'phụ' hoặc 'đánh dấu'). Tôi thường chạy một số hoạt động mà sản xuất một số giá trị Inf, nói trong vùng tín hiệu thấp của một số ma trận. Tôi nghi ngờ bạn sẽ nhận được thông tin tốt hơn bằng cách sử dụng 'is.infinite' và/hoặc' is.nan' trên các biến nghi ngờ anyway. –

+0

@CarlWitthoft Trong mã + kịch bản dữ liệu tôi hiện đang làm việc, giá trị vấn đề chính xác là ba - NA, NaN và Inf. Trong những trường hợp khác, tôi chắc chắn cần NA, nhưng không phải hôm nay. :) Tôi cần mã để hủy bỏ (nó khá tốn kém tính toán, b/c của khối lượng dữ liệu) ngay khi chúng xảy ra. Do đó, lý do tôi thực sự muốn kích hoạt một lỗi (hoặc ít nhất là một cảnh báo). – Iterator

Trả lời

7

Tôi lo ngại không có phím tắt như vậy. Về lý thuyết trên unix có SIGFPE mà bạn có thể bẫy trên, nhưng trong thực tế

  1. không có cách nào tiêu chuẩn để cho phép hoạt động FP để bẫy nó (thậm chí C99 không bao gồm một điều khoản cho điều đó) - đó là đánh giá cao system-specifc (ví dụ: feenableexcept trên Linux, fp_enable_all trên AIX v.v.) hoặc yêu cầu sử dụng bộ kết hợp cho CPU mục tiêu của bạn
  2. Các hoạt động FP thường được thực hiện trong các đơn vị vectơ như SSE để bạn không thể chắc chắn rằng FPU có liên quan và
  3. R chặn một số thao tác trên những thứ như NaN s, NA và xử lý chúng một cách riêng biệt để chúng không hoạt động mã FP của anh ta

Điều đó nói rằng, bạn có thể tự hack R sẽ bắt gặp một số ngoại lệ cho nền tảng và CPU nếu bạn đã cố gắng hết sức (tắt SSE, v.v.). Nó không phải là một cái gì đó chúng tôi sẽ xem xét xây dựng thành R, nhưng cho một mục đích đặc biệt nó có thể được doable.

Tuy nhiên, nó vẫn không bắt được hoạt động NaN/NA trừ khi bạn thay đổi mã nội bộ R. Ngoài ra, bạn sẽ phải kiểm tra từng gói đơn bạn đang sử dụng vì chúng có thể đang sử dụng các hoạt động FP trong mã C của chúng và cũng có thể xử lý riêng biệt NA/NaN.

Nếu bạn chỉ lo lắng về những thứ như chia cho số không hoặc trên/underflows, ở trên sẽ làm việc và có lẽ là gần nhất với một cái gì đó giống như một giải pháp.

Chỉ cần kiểm tra kết quả của bạn có thể không đáng tin cậy vì bạn không biết kết quả có dựa trên một số phép tính trung gian NaN đã thay đổi giá trị tổng hợp mà không cần phải là NaN. Nếu bạn sẵn sàng loại bỏ trường hợp như vậy, thì bạn có thể đơn giản đi bộ đệ quy thông qua các đối tượng kết quả của bạn hoặc không gian làm việc. Điều đó không nên cực kỳ kém hiệu quả, bởi vì bạn chỉ cần lo lắng về REALSXP và không phải bất cứ điều gì khác (trừ khi bạn không thích NA hoặc là - sau đó bạn sẽ có nhiều công việc hơn).


Đây là một ví dụ mã mà có thể được sử dụng để đi qua đối tượng R đệ quy:

static int do_isFinite(SEXP x) { 
    /* recurse into generic vectors (lists) */ 
    if (TYPEOF(x) == VECSXP) { 
     int n = LENGTH(x); 
     for (int i = 0; i < n; i++) 
      if (!do_isFinite(VECTOR_ELT(x, i))) return 0; 
    } 
    /* recurse into pairlists */ 
    if (TYPEOF(x) == LISTSXP) { 
     while (x != R_NilValue) { 
      if (!do_isFinite(CAR(x))) return 0; 
      x = CDR(x); 
     } 
     return 1; 
    } 
    /* I wouldn't bother with attributes except for S4 
     where attributes are slots */ 
    if (IS_S4_OBJECT(x) && !do_isFinite(ATTRIB(x))) return 0; 
    /* check reals */ 
    if (TYPEOF(x) == REALSXP) { 
     int n = LENGTH(x); 
     double *d = REAL(x); 
     for (int i = 0; i < n; i++) if (!R_finite(d[i])) return 0; 
    } 
    return 1; 
} 

SEXP isFinite(SEXP x) { return ScalarLogical(do_isFinite(x)); } 

# in R: .Call("isFinite", x) 
+0

Darn nó, tôi đã chờ đợi cho những đám mây để một phần, các thiên thần để hát, và cho bạn để đăng bài. Khi tôi đọc "Tôi sợ ...", tôi nghĩ "Oh yeah, đợi cho đến khi Simon xuất hiện, anh chàng này thật sai ... hãy xem đây là ai ..." :) Vì sao tôi không đăng bài trên R-Devel - R-Core là đáng sợ. :) – Iterator

+0

Quan trọng hơn, tuy nhiên, tôi đã nhìn vào callbacks, điều kiện xử lý, và gói 'kiểm tra' của bạn. 1: Có phải trường hợp cấu trúc bên trong của đối tượng không tiết lộ có hay không có Infs hoặc NAs, đối với các FP? I E. có bất kỳ thông tin meta nào về sự hiện diện/vị trí của các giá trị không hữu hạn không? 2: Nếu có thông tin như vậy, tôi có thể sử dụng các trình xử lý cuộc gọi để gọi một cuộc gọi để kiểm tra các giá trị xấu sau khi thực hiện mỗi câu lệnh không? – Iterator

+0

Tôi có đáng sợ không?;) Xin lỗi vì đã làm bạn thất vọng - IMHO 'SIGFPE' thực sự là con đường để đi (tôi nghi ngờ đó là những gì Matlab sử dụng) nhưng thiếu tiêu chuẩn thực sự bực bội (và Matlab không cần đến các trường hợp đặc biệt). –

7

Ý tưởng phác thảo dưới đây (và thực hiện nó) là rất không hoàn hảo.Tôi do dự thậm chí còn đề nghị nó, nhưng: (a) Tôi nghĩ nó thật thú vị, ngay cả trong tất cả sự xấu xa của nó; và (b) Tôi có thể nghĩ về những tình huống mà nó sẽ hữu ích. Cho rằng nó có vẻ như bạn đang ngay bây giờ bằng tay chèn một kiểm tra sau mỗi tính toán, tôi hy vọng rằng tình hình của bạn là một trong những người.

Mỏ là bản hack hai bước. Đầu tiên, tôi định nghĩa một hàm nanDetector() được thiết kế để phát hiện NaN s trong một số loại đối tượng có thể được trả về bởi các phép tính của bạn. Sau đó, nó sử dụng addTaskCallback() để gọi hàm nanDetector() trên .Last.value sau mỗi nhiệm vụ/tính toán cấp cao nhất được hoàn thành. Khi nó tìm thấy một số NaN trong một trong các giá trị được trả về, nó sẽ phát ra một lỗi mà bạn có thể sử dụng để tránh tính toán thêm.

Trong số những thiếu sót của nó:

  • Nếu bạn làm điều gì đó giống như việc thiết stop(error = recover), thật khó để biết nơi các lỗi đã được kích hoạt, vì các lỗi luôn ném từ bên trong stopOnNaNs().

  • Khi phát hiện lỗi, stopOnNaNs() bị chấm dứt trước khi có thể trả lại TRUE. Kết quả là, nó sẽ bị xóa khỏi danh sách nhiệm vụ và bạn cần phải đặt lại với addTaskCallback(stopOnNaNs) mà bạn muốn sử dụng lại. (Xem 'Arguments' section of ?addTaskCallback để biết thêm chi tiết).

Nếu không có thêm ado, đây đó là:


# Sketch of a function that tests for NaNs in several types of objects 
nanDetector <- function(X) { 
    # To examine data frames 
    if(is.data.frame(X)) { 
     return(any(unlist(sapply(X, is.nan)))) 
    } 
    # To examine vectors, matrices, or arrays 
    if(is.numeric(X)) { 
     return(any(is.nan(X))) 
    } 
    # To examine lists, including nested lists 
    if(is.list(X)) { 
     return(any(rapply(X, is.nan))) 
    } 
    return(FALSE) 
} 

# Set up the taskCallback 
stopOnNaNs <- function(...) { 
    if(nanDetector(.Last.value)) {stop("NaNs detected!\n")} 
    return(TRUE) 
} 
addTaskCallback(stopOnNaNs) 


# Try it out 
j <- 1:00 
y <- rnorm(99) 
l <- list(a=1:4, b=list(j=1:4, k=NaN)) 
# Error in function (...) : NaNs detected! 

# Subsequent time consuming code that could be avoided if the 
# error thrown above is used to stop its evaluation. 
+0

Chó diggity nóng, điều này có một số ý tưởng thú vị dọc theo các dòng tôi vừa mới bắt đầu xem xét, tức là sử dụng callbacks. Có hay không nó sẽ làm việc trong mã của tôi, tôi sẽ phải xem, nhưng đó là hướng dẫn dù sao. Cảm ơn! – Iterator

+1

FWIW nó sẽ thực sự không hiệu quả để thực hiện các kiểm tra trong mã R - nó tầm thường để làm như vậy trong C vì nó về cơ bản chỉ 'if (TYPEOF (x) == REALSXP) {double * d = REAL (x); int n = LENGTH (x); cho (int i = 0; i

+0

@SimonUrbanek Cảm ơn bạn đã hướng dẫn C - điều đó chắc chắn sẽ nhanh chóng. Cách tiếp cận này có dễ phát triển để sử dụng thông qua gói nội tuyến không? Hay tôi có thể ước sao một ngôi sao có thể xuất hiện trong R? :) (Tôi đoán đó là một câu hỏi R-devel. ;-)) – Iterator

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