Numpy: entri unik dan NaN (Trac # 1514)

Dibuat pada 19 Okt 2012  ·  14Komentar  ·  Sumber: numpy/numpy

_Tiket asli http://projects.scipy.org/numpy/ticket/1514 pada 2010-06-18 oleh pengguna trac rspringuel, ditetapkan ke tidak diketahui._

Saat unique beroperasi pada larik dengan beberapa entri NaN, kembaliannya menyertakan NaN untuk setiap entri yang merupakan NaN dalam larik asli.

Contoh:
a = random.randint (5, size = 100) .astype (float)

a [12] = nan #tambahkan satu entri nan
unik (a)
larik ([0., 1., 2., 3., 4., NaN])
a [20] = nan #tambahkan satu detik
unik (a)
larik ([0., 1., 2., 3., 4., NaN, NaN])
a [13] = nan
unik (a) #dan ketiga
larik ([0., 1., 2., 3., 4., NaN, NaN, NaN])

Ini mungkin karena fakta bahwa x == y bernilai False jika x dan y keduanya adalah NaN. Kebutuhan unik untuk memiliki "atau (isnan (x) dan isnan (y))" ditambahkan ke kondisional yang memeriksa keberadaan nilai dalam nilai yang sudah diidentifikasi. Saya tidak tahu apakah kehidupan yang unik di numpy dan tidak dapat menemukannya ketika saya mencarinya, jadi saya tidak dapat membuat perubahan sendiri (atau bahkan memastikan sintaks yang tepat dari kondisional tersebut).

Selain itu, fungsi berikut dapat digunakan untuk menambal perilaku.

def nanunique (x):
a = numpy.unique (x)
r = []
untuk saya di:
jika i di r atau (numpy.isnan (i) dan numpy.any (numpy.isnan (r))):
terus
lain:
r. tambahkan (i)
kembali numpy.array (r)

00 - Bug Other

Komentar yang paling membantu

Saya mengalami masalah yang sama hari ini. Inti dari rutinitas np.unique adalah menghitung mask pada array terurut yang tidak terurai di numpy / lib / arraysetops.py untuk menemukan ketika nilai berubah dalam array yang diurutkan:

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
mask[1:] = aux[1:] != aux[:-1]

Ini bisa diganti dengan sesuatu seperti berikut ini, yang hampir sama dengan komentar jaimefrio dari sekitar 5 tahun yang lalu, tetapi menghindari panggilan argmin:

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
if (aux.shape[0] > 0 and isinstance(aux[-1], (float, np.float16,
                                              np.float32, np.float64))
    and np.isnan(aux[-1])):
    aux_firstnan = np.searchsorted(aux, np.nan, side='left')
    mask[1:aux_firstnan] = (aux[1:aux_firstnan] != aux[:aux_firstnan-1])
    mask[aux_firstnan] = True
    mask[aux_firstnan+1:] = False
else:
    mask[1:] = aux[1:] != aux[:-1]

Menjalankan beberapa% percobaan timeit Saya mengamati paling banyak <10% runtime penalty jika array besar dan ada sangat sedikit NaN (katakanlah 10 NaN dari 1 juta), dan untuk array besar itu sebenarnya berjalan lebih cepat jika ada banyak dari NaN.

Di sisi lain, jika arraynya kecil (misalnya, 10 entri) ada kinerja yang signifikan karena pemeriksaan float dan NaN relatif mahal, dan runtime bisa naik ke beberapa. Ini bahkan berlaku bahkan jika tidak ada NaN karena pemeriksaannya lambat.

Jika array memiliki NaN, maka itu menghasilkan hasil yang berbeda, menggabungkan NaN, yang merupakan titik dari semuanya. Jadi untuk kasus itu, sebenarnya pertanyaannya adalah mendapatkan hasil yang diinginkan (semua NaN digabungkan menjadi satu grup nilai) sedikit lebih lambat vs mendapatkan hasil yang tidak diinginkan (setiap NaN dalam grup nilainya sendiri) sedikit lebih cepat.

Terakhir, perhatikan bahwa patch ini tidak akan memperbaiki pencarian nilai unik yang melibatkan objek gabungan yang berisi NaN, seperti dalam contoh ini:

a = np.array([[0,1],[np.nan, 1], [np.nan, 1]])
np.unique(a, axis=0)

yang masih akan kembali

array([[ 0.,  1.],
       [nan,  1.],
       [nan,  1.]])

Semua 14 komentar

_trac user rspringuel menulis pada 2010-06-18_

Sial, karena harus menggunakan blok kode di atas. Ini hanya benar-benar memengaruhi kode tambalan jadi saya hanya akan memposting ulang itu:

def nanunique(x):
    a = numpy.unique(x)
    r = []
    for i in a:
        if i in r or (numpy.isnan(i) and numpy.any(numpy.isnan(r))):
            continue
        else:
            r.append(i)
    return numpy.array(r)

Tetap.

Saya masih melihat masalah ini dengan master terbaru. Komit mana yang seharusnya memperbaikinya? Kecuali saya melewatkan sesuatu, saya sarankan untuk membuka kembali masalah ini.

Ini mudah untuk diperbaiki untuk float, tapi saya tidak melihat jalan keluar yang mudah untuk dtypes yang kompleks atau terstruktur. Akan membuat PR cepat bersama dan kita bisa mendiskusikan opsi di sana.

@jaimefrio Saya sudah memperbaikinya untuk penggunaan unik

    if issubclass(aux.dtype.type, np.inexact):
        # nans always compare unequal, so encode as integers
        tmp = aux.searchsorted(aux)
    else:
        tmp = aux
    flag = np.concatenate(([True], tmp[1:] != tmp[:-1]))

tetapi sepertinya semua operasi lain juga mengalami masalah. Mungkin kita membutuhkan nan_equal, nan_not_equal ufuncs, atau mungkin sesuatu dalam nanfuntions.

Mencari aux untuk dirinya sendiri adalah trik cerdas! Meskipun mengurutkan _semua_ itu sedikit boros, idealnya kami ingin melihat entri pertama dengan nan, mungkin sesuatu di sepanjang baris, setelah mengkreditkan aux dan flag seperti sekarang, lakukan :

if not aux[-1] == aux[-1]:
    nanidx = np.argmin(aux == aux)
    nanaux = aux[nanidx:].searchsorted(aux[nanidx:])
    flag[nanidx+1:] = nanaux[1:] != nanaux[:-1]

atau sesuatu yang serupa setelah mengoreksi semua kesalahan dengan satu kesalahan yang mungkin saya perkenalkan di sana.

Pendekatan terakhir saya ini akan bekerja untuk tipe float dan kompleks, tetapi gagal untuk tipe d terstruktur dengan bidang floating point. Tapi saya masih berpikir bahwa trik penyortiran pencarian, meskipun akan bekerja untuk semua jenis, terlalu boros. Beberapa pengaturan waktu:

In [10]: a = np.random.randn(1000)

In [11]: %timeit np.unique(a)
10000 loops, best of 3: 69.5 us per loop

In [12]: b = np.sort(a)

In [13]: %timeit b.searchsorted(b)
10000 loops, best of 3: 28.1 us per loop

Itu akan menjadi 40% pencapaian kinerja, yang mungkin OK untuk fungsi nanunique , tapi mungkin tidak untuk kasus umum.

Disebut 2019, masalah OP masih valid dan kodenya bisa direproduksi.

@jaimefrio mengapa kita tidak bisa memiliki opsi yang salah secara default?

Maksud saya, perilaku ini paling-paling membingungkan, dan kinerja bukanlah alasan.

@ Demetrio92 sementara saya menghargai upaya Anda untuk membuat masalah ini bergerak, ironi / sarkasme di internet dapat diartikan berbeda oleh orang yang berbeda, harap tetap baik. Bagi sebagian dari kami, kinerja sangat penting dan kami tidak menambahkan kode begitu saja yang memperlambat segalanya.

PR # 5487 mungkin bisa menjadi tempat yang lebih baik untuk berkomentar atau memberi saran bagaimana melangkah maju.

Edit: perbaiki nomor PR

Masalah ini tampaknya terbuka selama 8 tahun, tetapi saya hanya ingin berpadu dengan +1 agar perilaku default untuk numpy.unique menjadi benar, bukan cepat. Ini melanggar kode saya dan saya yakin orang lain telah / akan menderita karenanya. Kita dapat memiliki opsional "fast = False" dan mendokumentasikan perilaku nan untuk fast dan nans. Saya akan terkejut jika np.unique sangat sering menjadi penghambat kinerja dalam aplikasi yang kritis terhadap waktu.

Saya mengalami masalah yang sama hari ini. Inti dari rutinitas np.unique adalah menghitung mask pada array terurut yang tidak terurai di numpy / lib / arraysetops.py untuk menemukan ketika nilai berubah dalam array yang diurutkan:

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
mask[1:] = aux[1:] != aux[:-1]

Ini bisa diganti dengan sesuatu seperti berikut ini, yang hampir sama dengan komentar jaimefrio dari sekitar 5 tahun yang lalu, tetapi menghindari panggilan argmin:

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
if (aux.shape[0] > 0 and isinstance(aux[-1], (float, np.float16,
                                              np.float32, np.float64))
    and np.isnan(aux[-1])):
    aux_firstnan = np.searchsorted(aux, np.nan, side='left')
    mask[1:aux_firstnan] = (aux[1:aux_firstnan] != aux[:aux_firstnan-1])
    mask[aux_firstnan] = True
    mask[aux_firstnan+1:] = False
else:
    mask[1:] = aux[1:] != aux[:-1]

Menjalankan beberapa% percobaan timeit Saya mengamati paling banyak <10% runtime penalty jika array besar dan ada sangat sedikit NaN (katakanlah 10 NaN dari 1 juta), dan untuk array besar itu sebenarnya berjalan lebih cepat jika ada banyak dari NaN.

Di sisi lain, jika arraynya kecil (misalnya, 10 entri) ada kinerja yang signifikan karena pemeriksaan float dan NaN relatif mahal, dan runtime bisa naik ke beberapa. Ini bahkan berlaku bahkan jika tidak ada NaN karena pemeriksaannya lambat.

Jika array memiliki NaN, maka itu menghasilkan hasil yang berbeda, menggabungkan NaN, yang merupakan titik dari semuanya. Jadi untuk kasus itu, sebenarnya pertanyaannya adalah mendapatkan hasil yang diinginkan (semua NaN digabungkan menjadi satu grup nilai) sedikit lebih lambat vs mendapatkan hasil yang tidak diinginkan (setiap NaN dalam grup nilainya sendiri) sedikit lebih cepat.

Terakhir, perhatikan bahwa patch ini tidak akan memperbaiki pencarian nilai unik yang melibatkan objek gabungan yang berisi NaN, seperti dalam contoh ini:

a = np.array([[0,1],[np.nan, 1], [np.nan, 1]])
np.unique(a, axis=0)

yang masih akan kembali

array([[ 0.,  1.],
       [nan,  1.],
       [nan,  1.]])

"Jika array memiliki NaN maka menghasilkan hasil yang berbeda, menggabungkan NaN, yang merupakan poin dari semuanya."

+1

Fungsi yang mengembalikan daftar yang berisi elemen berulang, _misalnya_ daftar dengan lebih dari 1 NaN, tidak boleh disebut "unik". Jika elemen berulang dalam kasus NaN diinginkan, maka itu hanya kasus khusus yang dinonaktifkan secara default, misalnya numpy.unique(..., keep_NaN=False) .

@ufmayer kirimkan PR!

+1
Saya juga akan mendukung pengembalian NaN hanya sekali

Apakah halaman ini membantu?
0 / 5 - 0 peringkat