2017-08-18 19 views
5

Điều này được lấy cảm hứng từ: python: Combined masking in numpy.Cách nhanh nhất để tìm giá trị không hữu hạn

Nhiệm vụ là tạo một mảng boolean của tất cả các giá trị không phải là hữu hạn. Ví dụ:

>>> arr = np.array([0, 2, np.inf, -np.inf, np.nan]) 
>>> ~np.isfinite(arr) 
array([False, False, True, True, True], dtype=bool) 

Với tôi, có vẻ đây là cách nhanh nhất để tìm các giá trị không hữu hạn, nhưng có vẻ như có cách nhanh hơn. Cụ thể là np.isnan(arr - arr) cũng nên làm như vậy:

>>> np.isnan(arr - arr) 
array([False, False, True, True, True], dtype=bool) 

Thời gian chúng ta thấy rằng nó nhanh gấp hai lần!

arr = np.random.rand(100000) 

%timeit ~np.isfinite(arr) 
10000 loops, best of 3: 198 µs per loop 

%timeit np.isnan(arr - arr) 
10000 loops, best of 3: 85.8 µs per loop 

Vì vậy, câu hỏi của tôi có hai phần:

  1. Tại sao np.isnan(arr - arr) lừa nhanh hơn so với "rõ ràng" ~np.isfinite(arr) phiên bản? Có đầu vào mà nó không hoạt động?

  2. Có cách nào nhanh hơn để tìm tất cả các giá trị không hữu hạn không?

+2

Thật thú vị, trong máy của tôi (Linux 64 bit, Python 3, NumPy 1.13.1) tùy chọn "hiển nhiên" mất ~ 77,3 µs và tùy chọn 'isnan' 135 µs.Nếu tôi tăng kích thước của 'arr' lên 10000000, sự khác biệt thậm chí còn lớn hơn, 12.5ms so với 89.8ms. – jdehesa

+0

Đây là trên các cửa sổ 64 bit, python 2.7 và vón cục 1.11.3. –

+0

@JonasAdler Bạn có thể thêm mã thời gian để người khác có thể tạo lại (hoặc không) kết quả của bạn không? –

Trả lời

3

Thật khó để trả lời vì np.isnannp.isfinite có thể sử dụng các hàm C khác nhau tùy thuộc vào bản dựng. Và tùy thuộc vào hiệu suất (mà cũng có thể phụ thuộc vào trình biên dịch, hệ thống và làm thế nào NumPy chính nó được xây dựng) của các chức năng C thời gian sẽ khác nhau.


Các ufuncs cho cả tham khảo một built-in npy_ func (source (1.11.3)):

/**begin repeat1 
* #kind = isnan, isinf, isfinite, signbit, copysign, nextafter, spacing# 
* #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit, npy_copysign, nextafter, spacing# 
**/ 

Và các chức năng này được xác định dựa trên sự hiện diện của hằng số thời gian biên dịch (source (1.11.3)):

/* use builtins to avoid function calls in tight loops 
* only available if npy_config.h is available (= numpys own build) */ 
#if HAVE___BUILTIN_ISNAN 
    #define npy_isnan(x) __builtin_isnan(x) 
#else 
    #ifndef NPY_HAVE_DECL_ISNAN 
     #define npy_isnan(x) ((x) != (x)) 
    #else 
     #if defined(_MSC_VER) && (_MSC_VER < 1900) 
      #define npy_isnan(x) _isnan((x)) 
     #else 
      #define npy_isnan(x) isnan(x) 
     #endif 
    #endif 
#endif 

/* only available if npy_config.h is available (= numpys own build) */ 
#if HAVE___BUILTIN_ISFINITE 
    #define npy_isfinite(x) __builtin_isfinite(x) 
#else 
    #ifndef NPY_HAVE_DECL_ISFINITE 
     #ifdef _MSC_VER 
      #define npy_isfinite(x) _finite((x)) 
     #else 
      #define npy_isfinite(x) !npy_isnan((x) + (-x)) 
     #endif 
    #else 
     #define npy_isfinite(x) isfinite((x)) 
    #endif 
#endif 

Vì vậy, nó có thể chỉ là trong trường hợp của bạn, np.isfinite phải làm (nhiều) công việc nhiều hơn np.isnan . Nhưng cũng có khả năng là trên một máy tính khác hoặc với một máy xây dựng khác, np.isfinite nhanh hơn hoặc cả hai đều nhanh như nhau.

Vì vậy, có lẽ không phải là một quy tắc cứng "cách nhanh nhất" là gì. Điều đó chỉ phụ thuộc vào quá nhiều yếu tố. Cá nhân tôi sẽ chỉ đi với np.isfinite vì nó có thể được nhanh hơn (và không quá chậm hơn ngay cả trong trường hợp của bạn) và nó làm cho ý định rõ ràng hơn nhiều.


Chỉ trong trường hợp bạn thực sự đang tối ưu hóa hiệu suất, bạn luôn có thể thực hiện việc phủ nhận tại chỗ. Điều đó có thể làm giảm thời gian và bộ nhớ bằng cách tránh một mảng tạm thời:

import numpy as np 
arr = np.random.rand(1000000) 

def isnotfinite(arr): 
    res = np.isfinite(arr) 
    np.bitwise_not(res, out=res) # in-place 
    return res 

np.testing.assert_array_equal(~np.isfinite(arr), isnotfinite(arr)) 
np.testing.assert_array_equal(~np.isfinite(arr), np.isnan(arr - arr)) 

%timeit ~np.isfinite(arr) 
# 3.73 ms ± 4.16 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 
%timeit isnotfinite(arr) 
# 2.41 ms ± 29.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 
%timeit np.isnan(arr - arr) 
# 12.5 ms ± 772 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

cũng Lưu ý rằng giải pháp np.isnannhiều chậm hơn trên máy tính của tôi (Windows 10 64bit Python 3.5 NumPy 1.13.1 Anaconda xây dựng)

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