Mendeteksi apakah array NumPy berisi setidaknya satu nilai non-numerik?

103

Saya perlu menulis fungsi yang akan mendeteksi jika input berisi setidaknya satu nilai yang non-numerik. Jika nilai non-numerik ditemukan, saya akan memunculkan kesalahan (karena kalkulasi seharusnya hanya mengembalikan nilai numerik). Jumlah dimensi dari larik input tidak diketahui sebelumnya - fungsi harus memberikan nilai yang benar terlepas dari ndim. Sebagai komplikasi tambahan, masukan bisa berupa float tunggal numpy.float64atau bahkan sesuatu yang aneh seperti array berdimensi nol.

Cara yang jelas untuk mengatasinya adalah dengan menulis fungsi rekursif yang melakukan iterasi pada setiap objek yang dapat diulang dalam larik hingga menemukan non-iterabe. Ini akan menerapkan numpy.isnan()fungsi di atas setiap objek yang tidak dapat diulang. Jika setidaknya satu nilai non-numerik ditemukan maka fungsi tersebut akan segera mengembalikan False. Sebaliknya, jika semua nilai dalam iterable adalah numerik, pada akhirnya akan mengembalikan True.

Itu berfungsi dengan baik, tetapi cukup lambat dan saya berharap NumPy memiliki cara yang jauh lebih baik untuk melakukannya. Apa alternatif yang lebih cepat dan numpyish?

Ini mockup saya:

def contains_nan( myarray ):
    """
    @param myarray : An n-dimensional array or a single float
    @type myarray : numpy.ndarray, numpy.array, float
    @returns: bool
    Returns true if myarray is numeric or only contains numeric values.
    Returns false if at least one non-numeric value exists
    Not-A-Number is given by the numpy.isnan() function.
    """
    return True
Salim Fadhley
sumber
3
Deskripsi Anda untuk contains_nanterlihat mencurigakan: "Mengembalikan nilai salah jika setidaknya ada satu nilai non-numerik". Saya akan berharap contains_nanuntuk kembali Truejika array berisi NaN.
Samuel Tardieu
Bagaimana dengan input seperti array(['None', 'None'], dtype=object)? Haruskah masukan seperti itu hanya menimbulkan pengecualian?
Finn Årup Nielsen
JANGAN gunakan float('nan') in x. Tidak bekerja.
Charlie Parker

Jawaban:

183

Ini harus lebih cepat daripada iterasi dan akan berfungsi apa pun bentuknya.

numpy.isnan(myarray).any()

Edit: 30x lebih cepat:

import timeit
s = 'import numpy;a = numpy.arange(10000.).reshape((100,100));a[10,10]=numpy.nan'
ms = [
    'numpy.isnan(a).any()',
    'any(numpy.isnan(x) for x in a.flatten())']
for m in ms:
    print "  %.2f s" % timeit.Timer(m, s).timeit(1000), m

Hasil:

  0.11 s numpy.isnan(a).any()
  3.75 s any(numpy.isnan(x) for x in a.flatten())

Bonus: ini berfungsi dengan baik untuk jenis NumPy non-array:

>>> a = numpy.float64(42.)
>>> numpy.isnan(a).any()
False
>>> a = numpy.float64(numpy.nan)
>>> numpy.isnan(a).any()
True
Paul
sumber
1
dengan numpy 1.7 versi flatten () hanya dua kali lebih cepat dari yang pertama
Christian Geier
Mengapa sesuatu seperti float('nan') in xtidak berfungsi? Saya mencobanya dan python kembali ke Falsemana x = [1,2,3,float('nan')].
Charlie Parker
1
@CharlieParker alasan yang sama mengapa float ('nan') == float ('nan') akan mengembalikan False. NaN tidak sama dengan NaN. Di sini info lebih lanjut: stackoverflow.com/questions/10034149/…
Muppet
1
@ mab: Itu karena memanggil numpy.anygenexp hanya mengembalikan genexp; Anda tidak benar-benar melakukan perhitungan yang Anda kira. Jangan pernah memanggil numpy.anygenexp.
user2357112 mendukung Monica
Dalam skenario debugging yang sebenarnya, saya juga merekomendasikan untuk melihat np.isfinitealih-alih np.isnanmendeteksi luapan numerik, ketidakstabilan, dll.
Ben Usman
18

Jika infinity adalah nilai yang mungkin, saya akan menggunakan numpy.isfinite

numpy.isfinite(myarray).all()

Jika nilai di atas terevaluasi menjadi True, maka myarraytidak berisi numpy.nan,, numpy.infatau -numpy.infnilai.

numpy.nanakan baik-baik saja dengan numpy.infnilai, misalnya:

In [11]: import numpy as np

In [12]: b = np.array([[4, np.inf],[np.nan, -np.inf]])

In [13]: np.isnan(b)
Out[13]: 
array([[False, False],
       [ True, False]], dtype=bool)

In [14]: np.isfinite(b)
Out[14]: 
array([[ True, False],
       [False, False]], dtype=bool)
Akavall
sumber
Mengapa sesuatu seperti float('nan') in xtidak berfungsi? Saya mencobanya dan python kembali ke Falsemana x = [1,2,3,float('nan')].
Charlie Parker
1
@CharlieParker karena dua nantidak dianggap sama satu sama lain. Coba float('nan') == float('nan').
Akavall
menarik. Mengapa mereka tidak dianggap setara?
Charlie Parker
1
@CharlieParker, saya rasa saya tidak bisa memberikan jawaban yang sangat bagus di sini. Mungkin inilah yang Anda cari: stackoverflow.com/questions/1565164/…
Akavall
4

Pfft! Mikrodetik! Jangan pernah menyelesaikan masalah dalam mikrodetik yang dapat diselesaikan dalam nanodetik.

Perhatikan bahwa jawaban yang diterima:

  • mengulangi seluruh data, terlepas dari apakah nan ditemukan
  • membuat larik sementara dengan ukuran N, yang berlebihan.

Solusi yang lebih baik adalah mengembalikan True segera ketika NAN ditemukan:

import numba
import numpy as np

NAN = float("nan")

@numba.njit(nogil=True)
def _any_nans(a):
    for x in a:
        if np.isnan(x): return True
    return False

@numba.jit
def any_nans(a):
    if not a.dtype.kind=='f': return False
    return _any_nans(a.flat)

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 573us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 774ns  (!nanoseconds)

dan bekerja untuk n-dimensi:

array1M_nd = array1M.reshape((len(array1M)/2, 2))
assert any_nans(array1M_nd)==True
%timeit any_nans(array1M_nd)  # 774ns

Bandingkan ini dengan solusi asli numpy:

def any_nans(a):
    if not a.dtype.kind=='f': return False
    return np.isnan(a).any()

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 456us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 470us

%timeit np.isnan(array1M).any()  # 532us

Metode keluar awal adalah 3 kali lipat atau percepatan besaran (dalam beberapa kasus). Tidak terlalu lusuh untuk anotasi sederhana.

pengguna48956
sumber
3

Dengan numpy 1.3 atau svn Anda bisa melakukan ini

In [1]: a = arange(10000.).reshape(100,100)

In [3]: isnan(a.max())
Out[3]: False

In [4]: a[50,50] = nan

In [5]: isnan(a.max())
Out[5]: True

In [6]: timeit isnan(a.max())
10000 loops, best of 3: 66.3 µs per loop

Perlakuan nans dalam perbandingan tidak konsisten di versi sebelumnya.


sumber
Mengapa sesuatu seperti float('nan') in xtidak berfungsi? Saya mencobanya dan python kembali ke Falsemana x = [1,2,3,float('nan')].
Charlie Parker
@CharlieParker ... karena perbandingan dengan NAN tidak menghasilkan apa yang Anda harapkan. NAN diperlakukan seperti NULL logis (= tidak tahu). float("nan")==float("nan")give False(meskipun mungkin seharusnya mengembalikan NAN atau None). Demikian pula keanehan dengan NAN dan boolen NULL benar dalam banyak bahasa, termasuk SQL (di mana NULL = NULL tidak pernah benar).
pengguna48956
2

(np.where(np.isnan(A)))[0].shape[0]akan lebih besar daripada 0jika Aberisi setidaknya satu elemen nan, Abisa jadin x m matriks.

Contoh:

import numpy as np

A = np.array([1,2,4,np.nan])

if (np.where(np.isnan(A)))[0].shape[0]: 
    print "A contains nan"
else:
    print "A does not contain nan"
Ting On Chan
sumber