Kiểm tra nhanh NaN trong NumPy


120

Tôi đang tìm cách nhanh nhất để kiểm tra sự xuất hiện của NaN ( np.nan) trong mảng NumPy X. np.isnan(X)thì khỏi phải bàn, vì nó xây dựng một mảng hình dạng boolean X.shape, có khả năng rất lớn.

Tôi đã thử np.nan in X, nhưng điều đó dường như không hiệu quả bởi vì np.nan != np.nan. Có cách nào nhanh chóng và tiết kiệm bộ nhớ để thực hiện việc này không?

(Đối với những người sẽ hỏi "khổng lồ như thế nào": Tôi không thể nói. Đây là xác thực đầu vào cho mã thư viện.)


xác thực đầu vào của người dùng không hoạt động trong trường hợp này? Như đã kiểm tra NaN trước khi chèn
Woot4Moo

@ Woot4Moo: không, thư viện lấy mảng hoặc scipy.sparsema trận NumPy làm đầu vào.
Fred Foo,

2
Nếu bạn đang làm điều này rất nhiều, tôi đã nghe những điều tốt đẹp về nút cổ chai ( pypi.python.org/pypi/Bottleneck )
mờ

Câu trả lời:


160

Giải pháp của Ray là tốt. Tuy nhiên, trên máy của tôi, nó nhanh hơn khoảng 2,5 lần để sử dụng numpy.sumthay cho numpy.min:

In [13]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 244 us per loop

In [14]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 97.3 us per loop

Không giống như min, sumkhông yêu cầu phân nhánh, điều này trên phần cứng hiện đại có xu hướng khá đắt. Đây có lẽ là lý do tại sao sumnhanh hơn.

sửa Bài kiểm tra trên được thực hiện với một NaN duy nhất ngay giữa mảng.

Điều thú vị cần lưu ý minlà sự có mặt của NaN chậm hơn so với khi không có mặt của chúng. Nó cũng có vẻ chậm hơn khi các NaN tiến gần đến phần bắt đầu của mảng. Mặt khác, sumthông lượng của có vẻ không đổi bất kể có NaN hay không và chúng nằm ở đâu:

In [40]: x = np.random.rand(100000)

In [41]: %timeit np.isnan(np.min(x))
10000 loops, best of 3: 153 us per loop

In [42]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop

In [43]: x[50000] = np.nan

In [44]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 239 us per loop

In [45]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.8 us per loop

In [46]: x[0] = np.nan

In [47]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 326 us per loop

In [48]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop

1
np.minnhanh hơn khi mảng không chứa NaN, đây là đầu vào mong đợi của tôi. Nhưng tôi đã quyết định chấp nhận cái này dù sao, bởi vì nó bắt infneginflà tốt.
Fred Foo,

2
Điều này chỉ bắt infhoặc -infnếu đầu vào chứa cả hai và nó có vấn đề nếu đầu vào chứa các giá trị lớn nhưng hữu hạn bị tràn khi được thêm cùng nhau.
user2357112 hỗ trợ Monica

4
min và max không cần phân nhánh cho dữ liệu dấu chấm động trên chip x86 có khả năng sse. Vì vậy, kể từ khi numpy 1,8 phút sẽ không chậm hơn tổng, trên amd phenom của tôi, nó thậm chí còn nhanh hơn 20%.
jtaylor

1
Trên Intel Core i5 của tôi, với 1.9.2 numpy trên OSX, np.sumvẫn nhanh hơn khoảng 30% np.min.
Matthew Brett,

np.isnan(x).any(0)nhanh hơn một chút so với np.sumnp.mintrên máy của tôi, mặc dù có thể có một số bộ nhớ đệm không mong muốn.
jsignell

28

Tôi nghĩ np.isnan(np.min(X))nên làm những gì bạn muốn.


Hmmm ... đây luôn là O (n) khi có thể là O (1) (đối với một số mảng).
user48956

17

Ngay cả khi tồn tại một câu trả lời được chấp nhận, tôi sẽ muốn chứng minh những điều sau (với Python 2.7.2 và Numpy 1.6.0 trên Vista):

In []: x= rand(1e5)
In []: %timeit isnan(x.min())
10000 loops, best of 3: 200 us per loop
In []: %timeit isnan(x.sum())
10000 loops, best of 3: 169 us per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 134 us per loop

In []: x[5e4]= NaN
In []: %timeit isnan(x.min())
100 loops, best of 3: 4.47 ms per loop
In []: %timeit isnan(x.sum())
100 loops, best of 3: 6.44 ms per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 138 us per loop

Do đó, cách thực sự hiệu quả có thể phụ thuộc nhiều vào hệ điều hành. Dù sao dot(.)dựa có vẻ là một trong những ổn định nhất.


1
Tôi nghi ngờ rằng nó không phụ thuộc quá nhiều vào HĐH, cũng như việc triển khai BLAS cơ bản và trình biên dịch C. Cảm ơn, nhưng sản phẩm dấu chấm chỉ có một chút khả năng bị tràn khi xchứa các giá trị lớn và tôi cũng muốn kiểm tra inf.
Fred Foo,

1
Chà, bạn luôn có thể làm sản phẩm chấm với những cái đó và sử dụng isfinite(.). Tôi chỉ muốn chỉ ra khoảng cách hiệu suất rất lớn. Cảm ơn
ăn

Trên máy của tôi cũng vậy.
kawing-chiu

1
Thông minh, không? Như Fred Foo gợi ý, bất kỳ mức tăng hiệu quả nào của phương pháp dựa trên sản phẩm chấm gần như chắc chắn là nhờ cài đặt NumPy cục bộ được liên kết với triển khai BLAS được tối ưu hóa như ATLAS, MKL hoặc OpenBLAS. Đây là trường hợp của Anaconda chẳng hạn. Do đó, sản phẩm chấm này sẽ được sử dụng song song trên tất cả các lõi có sẵn. Điều tương tự cũng không thể xảy ra đối với các phương pháp tiếp cận min- hoặc sumdựa trên, chạy giới hạn trong một lõi duy nhất. Ergo, khoảng cách hiệu suất đó.
Cecil Curry

16

Có hai cách tiếp cận chung ở đây:

  • Kiểm tra từng mục mảng để tìm nanvà lấy any.
  • Áp dụng một số hoạt động tích lũy để bảo toàn nans (like sum) và kiểm tra kết quả của nó.

Mặc dù cách tiếp cận đầu tiên chắc chắn là rõ ràng nhất, nhưng việc tối ưu hóa nặng nề của một số hoạt động tích lũy (đặc biệt là những hoạt động được thực thi trong BLAS chẳng hạn dot) có thể làm cho chúng khá nhanh. Lưu ý rằng dot, giống như một số hoạt động BLAS khác, được đa luồng trong các điều kiện nhất định. Điều này giải thích sự khác biệt về tốc độ giữa các máy khác nhau.

nhập mô tả hình ảnh ở đây

import numpy
import perfplot


def min(a):
    return numpy.isnan(numpy.min(a))


def sum(a):
    return numpy.isnan(numpy.sum(a))


def dot(a):
    return numpy.isnan(numpy.dot(a, a))


def any(a):
    return numpy.any(numpy.isnan(a))


def einsum(a):
    return numpy.isnan(numpy.einsum("i->", a))


perfplot.show(
    setup=lambda n: numpy.random.rand(n),
    kernels=[min, sum, dot, any, einsum],
    n_range=[2 ** k for k in range(20)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

4
  1. Sử dụng bất kỳ()

    if numpy.isnan(myarray).any()

  2. numpy.isfinite có lẽ tốt hơn isnan để kiểm tra

    if not np.isfinite(prop).all()


3

Nếu bạn cảm thấy thoải mái với nó cho phép tạo ra ngắn mạch nhanh (dừng ngay khi tìm thấy NaN) chức năng:

import numba as nb
import math

@nb.njit
def anynan(array):
    array = array.ravel()
    for i in range(array.size):
        if math.isnan(array[i]):
            return True
    return False

Nếu không có NaNchức năng thực sự có thể chậm hơn np.min, tôi nghĩ đó là do np.minsử dụng đa xử lý cho các mảng lớn:

import numpy as np
array = np.random.random(2000000)

%timeit anynan(array)          # 100 loops, best of 3: 2.21 ms per loop
%timeit np.isnan(array.sum())  # 100 loops, best of 3: 4.45 ms per loop
%timeit np.isnan(array.min())  # 1000 loops, best of 3: 1.64 ms per loop

Nhưng trong trường hợp có NaN trong mảng, đặc biệt nếu vị trí của nó ở chỉ số thấp, thì nó nhanh hơn nhiều:

array = np.random.random(2000000)
array[100] = np.nan

%timeit anynan(array)          # 1000000 loops, best of 3: 1.93 µs per loop
%timeit np.isnan(array.sum())  # 100 loops, best of 3: 4.57 ms per loop
%timeit np.isnan(array.min())  # 1000 loops, best of 3: 1.65 ms per loop

Các kết quả tương tự có thể đạt được với Cython hoặc phần mở rộng C, những điều này phức tạp hơn một chút (hoặc có thể dễ dàng hơn bottleneck.anynan) nhưng tối hậu thư thực hiện tương tự như anynanchức năng của tôi .


1

Liên quan đến điều này là câu hỏi làm thế nào để tìm thấy sự xuất hiện đầu tiên của NaN. Đây là cách nhanh nhất để xử lý vấn đề mà tôi biết:

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
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.