Numpy: mengurangi kasus cornercase (Trac # 236)

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

_Tiket asli http://projects.scipy.org/numpy/ticket/236 pada 2006-08-07 oleh pengguna trac martin_wiechert, ditetapkan ke tidak diketahui._

.reduceat tidak menangani indeks berulang dengan benar. Ketika indeks diulang, elemen netral dari operasi harus dikembalikan. Dalam contoh di bawah ini [0, 10], bukan [1, 10], yang diharapkan.

In [1]:import numpy

In [2]:numpy.version.version
Out[2]:'1.0b1'

In [3]:a = numpy.arange (5)

In [4]:numpy.add.reduceat (a, (1,1))
Out[4]:array([ 1, 10])
01 - Enhancement 23 - Wish List numpy.core

Komentar yang paling membantu

Motivasi utama untuk reduceat adalah untuk menghindari pengulangan lebih dari reduce untuk kecepatan maksimum. Jadi saya tidak sepenuhnya yakin pembungkus for loop di atas reduce akan menjadi tambahan yang sangat berguna untuk Numpy. Itu akan bertentangan dengan tujuan utama reduceat .

Selain itu, logika untuk keberadaan reduceat dan API, sebagai pengganti vektor cepat untuk loop di atas reduce , bersih dan berguna. Saya tidak akan menghentikannya, melainkan memperbaikinya.

Mengenai kecepatan reduceat , mari pertimbangkan contoh sederhana, tetapi mirip dengan beberapa kasus dunia nyata yang saya miliki di kode saya sendiri, di mana saya menggunakan reduceat :

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

Ini adalah perbedaan waktu lebih dari 100x dan menggambarkan pentingnya menjaga efisiensi reduceat .

Singkatnya, saya akan memprioritaskan memperbaiki reduceat daripada memperkenalkan fungsi baru.

Memiliki start_indices dan end_indices , meskipun berguna dalam beberapa kasus, sering kali berlebihan dan saya akan melihatnya sebagai kemungkinan tambahan, tetapi bukan sebagai perbaikan untuk reduceat tidak konsisten tingkah laku.

Semua 49 komentar

_ @ teoliphant menulis pada 2006-08-08_

Sayangnya, mungkin, metode reduceat dari NumPy mengikuti perilaku metode reduceat dari Numerik untuk kasus sudut ini.

Tidak ada fasilitas untuk mengembalikan elemen "identitas" dari operasi dalam kasus persamaan indeks. Perilaku yang ditentukan adalah mengembalikan elemen yang diberikan oleh indeks pertama jika potongan mengembalikan urutan kosong. Oleh karena itu, perilaku yang terdokumentasi dan aktual dari reduceat dalam kasus ini adalah untuk membangun

[a [1], tambahkan. kurangi (a [1:])]

Ini adalah permintaan fitur.

_trac user martin_wiechert menulis pada 2006-08-08_

lihat juga tiket # 835

Milestone diubah menjadi 1.1 oleh @alberts pada 2007-05-12

Milestone diubah menjadi Unscheduled oleh @cournape pada 2009-03-02

Saya pikir ini terkait erat dengan # 835: Jika salah satu indeks adalah len(a) , reduceat tidak dapat menampilkan elemen pada indeks itu, yang diperlukan jika indeks len(a) muncul atau diulangi di akhir indeks.

Beberapa solusi:

  • opsi untuk reduceat untuk tidak menetapkan nilai apa pun dalam output di mana end - start == 0
  • pilihan untuk mengatur keluaran ke nilai tetap yang diberikan di mana end - start == 0
  • parameter where , seperti di ufunc() , yang menutupi keluaran mana yang harus dihitung sama sekali.

Apakah ada pemikiran lain tentang masalah ini? Saya akan tertarik untuk memiliki opsi untuk mengatur output ke nilai identitas (jika ada) di mana end - start == 0.

Saya sangat mendukung perubahan perilaku reduceat seperti yang disarankan dalam terbitan terbuka yang sudah berlangsung lama ini. Sepertinya bug yang jelas atau kesalahan desain yang jelas yang menghalangi kegunaan konstruksi Numpy yang hebat ini.

reduceat harus berfungsi secara konsisten untuk semua indeks. Yaitu, untuk setiap indeks i, ufunc.reduceat(a, indices) harus menghasilkan ufunc.reduce(a[indices[i]:indices[i+1]]) .

Ini juga harus benar untuk kasus indices[i] == indices[i+1] . Saya tidak dapat melihat alasan yang masuk akal mengapa, dalam kasus ini, reduceat harus mengembalikan a[indices[i]] bukan ufunc.reduce(a[indices[i]:indices[i+1]]) .

Lihat juga DI SINI komentar serupa dari pencipta Pandas , Wes McKinney .

Wow, ini memang mengerikan dan rusak.
.
Kami perlu beberapa diskusi di milis, tapi setidaknya saya akan
benar-benar mendukung untuk menjadikan edisi itu FutureWarning di rilis berikutnya
dan memperbaiki perilaku tersebut beberapa rilis kemudian. Kami membutuhkan seseorang untuk mengambil
pimpin untuk memulai diskusi itu dan menulis tambalan. Mungkin itu kamu?

Terima kasih atas tanggapan yang mendukung. Saya dapat memulai diskusi jika ini membantu, tetapi sayangnya saya tidak dapat menambal kode C.

Apa maksud Anda untuk ufunc tanpa identitas, seperti np.maximum?

Untuk fungsi seperti itu, pengurangan kosong seharusnya merupakan kesalahan, seperti yang sudah terjadi
saat Anda menggunakan .reduce () dan bukan .reduceat ().

Memang, perilaku harus didorong oleh konsistensi dengan ufunc.reduce(a[indices[i]:indices[i+1]]) , yang diharapkan setiap pengguna. Jadi ini tidak membutuhkan keputusan desain baru. Ini benar-benar tampak seperti perbaikan bug lama bagi saya. Kecuali siapa pun dapat membenarkan perilaku tidak konsisten saat ini.

@njsmith Saya tidak dapat mendaftar ke daftar Numpy. Saya mengirim alamat saya di sini https://mail.scipy.org/mailman/listinfo/numpy-discussion tetapi saya tidak pernah mendapatkan "email yang meminta konfirmasi". Tidak yakin apakah seseorang memerlukan persyaratan khusus untuk berlangganan ...

@divenex : apakah Anda memeriksa folder spam Anda? (Saya selalu lupa melakukan itu ...) Jika tidak, saya tidak yakin apa yang salah. Jelas tidak boleh ada persyaratan khusus untuk berlangganan selain "memiliki alamat email". Jika Anda masih tidak dapat membuatnya berfungsi maka bicaralah dan kami akan mencoba melacak sysadmin yang relevan ... Kami pasti ingin tahu apakah itu rusak.

Versi reduceat yang konsisten dengan ufunc.reduce(a[indices[i]:indices[i+1]]) akan benar-benar bagus. Itu akan jauh lebih berguna! Entah argumen untuk memilih perilaku atau fungsi baru ( reduce_intervals ? reduce_segments ? ...?) Akan menghindari kerusakan ke belakang yang tidak kompatibel.

Saya mungkin tergoda untuk menghentikan np.ufunc.reduceat alltogether - tampaknya lebih berguna untuk dapat menentukan satu set indeks awal dan akhir, untuk menghindari kasus di mana indices[i] > indices[i+1] . Juga, nama at menunjukkan kemiripan yang jauh lebih besar dengan at daripada yang sebenarnya ada

Apa yang saya usulkan sebagai penggantinya adalah np.piecewise_reduce np.reducebins , mungkin murni-python, yang pada dasarnya melakukan:

def reducebins(func, arr, start=None, stop=None, axis=-1, out=None):
    """
    Compute (in the 1d case) `out[i] = func.reduce(arr[start[i]:stop[i]])`

    If only `start` is specified, this computes the same reduce at `reduceat` did:

        `out[i]  = func.reduce(arr[start[i]:start[i+1]])`
        `out[-1] = func.reduce(arr[start[-1]:])`

    If only `stop` is specified, this computes:

        `out[0] = func.reduce(arr[:stop[0]])`
        `out[i] = func.reduce(arr[stop[i-1]:stop[i]])`

    """
    # convert to 1d arrays
    if start is not None:
        start = np.array(start, copy=False, ndmin=1, dtype=np.intp)
        assert start.ndim == 1
    if stop is not None:
        stop = np.array(stop, copy=False, ndmin=1, dtype=np.intp)
        assert stop.ndim == 1

    # default arguments that do useful things
    if start is None and stop is None:
        raise ValueError('At least one of start and stop must be specified')
    elif stop is None:
        # start only means reduce from one index to the next, and the last to the end
        stop = np.empty_like(start)
        stop[:-1] = start[1:]
        stop[-1] = arr.shape[axis]
    elif start is None:
        # stop only means reduce from the start to the first index, and one index to the next
        start = np.empty_like(stop)
        start[1:] = stop[:-1]
        start[0] = 0
    else:
        # TODO: possibly confusing?
        start, stop = np.broadcast_arrays(start, stop)

    # allocate output - not clear how to do this safely for subclasses
    if not out:
        sh = list(arr.shape)
        sh[axis] = len(stop)
        sh = tuple(sh)
        out = np.empty(shape=sh)

    # below assumes axis=0 for brevity here
    for i, (si, ei) in enumerate(zip(start, stop)):
        func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
    return out

Yang memiliki sifat bagus yaitu:

  • np.add.reduce(arr) sama dengan np.piecewise_reduce(np.add, arr, 0, len(arr))
  • np.add.reduceat(arr, inds) sama dengan np.piecewise_reduce(np.add, arr, inds)
  • np.add.accumulate(arr) sama dengan np.piecewise_reduce(np.add, arr, 0, np.arange(len(arr)))

Sekarang, apakah ini ingin melalui mesin __array_ufunc__ ? Sebagian besar dari apa yang perlu ditangani harus sudah ditutupi oleh func.reduce - satu-satunya masalah adalah baris np.empty , yang merupakan masalah yang dibagikan np.concatenate .

Kedengarannya seperti solusi yang bagus bagi saya dari perspektif API. Bahkan hanya dengan menentukan dua set indeks ke reduceat sudah cukup. Dari perspektif implementasi? Tidak terlalu sulit untuk mengubah PyUFunc_Reduceat untuk mendukung memiliki dua set inds, jika itu memberikan manfaat. Jika kami benar-benar melihat keuntungan dalam mendukung kasus penggunaan serupa akumulasi secara efisien, itu juga tidak akan sulit untuk dilakukan.

Marten mengusulkan sesuatu yang mirip dengan ini dalam diskusi serupa dari ~ 1
tahun lalu, tetapi dia juga menyebutkan kemungkinan untuk menambahkan opsi 'langkah':

http://numpy-discussion.10968.n7.nabble.com/Behavior-of-reduceat-td42667.html

Hal-hal yang saya suka (jadi +1 jika ada yang menghitung) dari proposal Anda:

  • Membuat fungsi baru, daripada mencoba menyelamatkan yang sudah ada
    satu.
  • Membuat argumen indeks awal dan akhir spesifik, bukan
    secara ajaib mengetahui mereka dari array multidimensi.
  • Default untuk indeks None sangat rapi.

Hal-hal yang menurut saya penting untuk dipikirkan dengan matang untuk fungsi baru ini:

  • Haruskah kita menjadikan 'langkah' sebagai pilihan? (Saya akan mengatakan ya)
  • Apakah masuk akal untuk indeks array untuk disiarkan, atau haruskah mereka
    menjadi 1D?
  • Haruskah ini menjadi fungsi np, atau metode ufunc? (Saya pikir saya lebih suka itu
    sebagai metode)

Dan dari departemen gudang sepeda, saya lebih suka:

  • Beri nama yang lebih mudah diingat, tapi saya tidak punya proposal.
  • Gunakan 'start' dan 'stop' (dan 'step' jika kita memutuskan untuk melakukannya)
    konsistensi dengan potongan np.arange dan Python.
  • Menghapus _indices dari nama kwarg.

Jaime

Pada Kamis, 13 Apr 2017 pukul 13.47, Eric Wieser [email protected]
menulis:

Saya mungkin tergoda untuk menghentikan np.ufunc.reduceat semuanya - itu
tampaknya lebih berguna untuk dapat menentukan satu set indeks awal dan akhir, hingga
hindari kasus dimana indeks [i]> indeks [i + 1]. Juga, nama menyarankan a
kemiripan yang jauh lebih besar daripada yang biasanya ada

Yang saya usulkan sebagai penggantinya adalah np.piecewise_reduce, yang pada dasarnya
tidak:

def piecewise_reduce (func, arr, start_indices = None, end_indices = None, axis = -1, out = None):
jika start_indices adalah None dan end_indices adalah None:
indeks_mulai = np.array ([0], dtype = np.intp)
end_indices = np.array (arr.shape [axis], dtype = np.intp)
elif end_indices adalah None:
Indeks_sel = np.empty_like (indeks_mulai)
Indeks_sel [: - 1] = indeks_mulai [1:]
end_indices [-1] = arr.shape [axis]
elif start_indices adalah None:
start_indices = np.empty_like (end_indices)
start_indices [1:] = end_indices
end_indices [0] = 0
lain:
assert len ​​(indeks_mulai) == len (indeks_akhir)

if not out:
    sh = list(arr.shape)
    sh[axis] = len(end_indices)
    out = np.empty(shape=sh)

# below assumes axis=0 for brevity here
for i, (si, ei) in enumerate(zip(start_indices, end_indices)):
    func.reduce(arr[si:ei,...], out=alloc[i, ...], axis=axis)
return out

Yang memiliki sifat bagus yaitu:

  • np.ufunc.reduce sama dengan np.piecewise_reduce (func, arr, 0,
    len (arr))
  • np.ufunc.accumulate sama dengan `np.piecewise_reduce (func, arr,
    np.zeros (len (arr)), np.arange (len (arr)))

Sekarang, apakah ini ingin melalui mesin__array_ufunc__? Kebanyakan
apa yang perlu ditangani harus sudah tercakup dalam func.reduce - the
satu-satunya masalah adalah baris np.empty, yang merupakan masalah yang np.concatenate
saham.

-
Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/numpy/numpy/issues/834#issuecomment-293867746 , atau nonaktifkan
utasnya
https://github.com/notifications/unsubscribe-auth/ADMGdtjSCodONyu6gCpwofdBaJMCIKa-ks5rvgtrgaJpZM4ANcqc
.

-
(__ /)
(Oo)
(> <) Este es Conejo. Copia a Conejo en tu firma y ayúdale en sus planes
de dominación mundial.

Gunakan 'start' dan 'stop'

Selesai

Haruskah kita menjadikan 'langkah' sebagai pilihan

Sepertinya kasus penggunaan yang cukup sempit

Apakah masuk akal untuk indeks array untuk disiarkan, atau harus 1D

Diperbarui. > 1d jelas buruk, tapi saya pikir kita harus mengizinkan 0d dan penyiaran, untuk kasus seperti terakumulasi.

Haruskah ini menjadi fungsi np, atau metode ufunc? (Saya pikir saya lebih suka itu
sebagai metode)

Setiap metode ufunc adalah satu hal lagi yang harus ditangani __array_ufunc__ .

Motivasi utama untuk reduceat adalah untuk menghindari pengulangan lebih dari reduce untuk kecepatan maksimum. Jadi saya tidak sepenuhnya yakin pembungkus for loop di atas reduce akan menjadi tambahan yang sangat berguna untuk Numpy. Itu akan bertentangan dengan tujuan utama reduceat .

Selain itu, logika untuk keberadaan reduceat dan API, sebagai pengganti vektor cepat untuk loop di atas reduce , bersih dan berguna. Saya tidak akan menghentikannya, melainkan memperbaikinya.

Mengenai kecepatan reduceat , mari pertimbangkan contoh sederhana, tetapi mirip dengan beberapa kasus dunia nyata yang saya miliki di kode saya sendiri, di mana saya menggunakan reduceat :

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

Ini adalah perbedaan waktu lebih dari 100x dan menggambarkan pentingnya menjaga efisiensi reduceat .

Singkatnya, saya akan memprioritaskan memperbaiki reduceat daripada memperkenalkan fungsi baru.

Memiliki start_indices dan end_indices , meskipun berguna dalam beberapa kasus, sering kali berlebihan dan saya akan melihatnya sebagai kemungkinan tambahan, tetapi bukan sebagai perbaikan untuk reduceat tidak konsisten tingkah laku.

Saya tidak berpikir mengizinkan indeks start dan stop datang dari array yang berbeda
akan membuat perbedaan besar pada efisiensi jika diterapkan di C.

Pada 13 April 2017 pukul 23:40, divenex [email protected] menulis:

Motivasi utama untuk reduceat adalah untuk menghindari pengulangan yang berlebihan untuk
kecepatan maksimum. Jadi saya tidak sepenuhnya yakin pembungkus for loop over
Mengurangi akan menjadi tambahan yang sangat berguna untuk Numpy. Itu akan melawan
mengurangi tujuan utama.

Apalagi logika untuk mengurangi keberadaan dan API, sebagai vektorisasi cepat
pengganti loop over reduce, bersih dan berguna. saya tidak akan
mencela itu, melainkan memperbaikinya.

Mengenai kecepatan reduceat, mari pertimbangkan contoh sederhana, tetapi serupa dengan
beberapa kasus dunia nyata yang saya miliki di kode saya sendiri, di mana saya menggunakan reduceat:

n = 10.000
arr = np.random.random (n)
inds = np.random.randint (0, n, n // 10)
inds.sort ()
% timeit out = np.add.reduceat (arr, inds) 10000 loop, terbaik 3: 42.1 µs per loop
% timeit out = piecewise_reduce (np.add, arr, inds) 100 loop, terbaik 3: 6,03 ms per loop

Ini adalah perbedaan waktu lebih dari 100x dan menggambarkan pentingnya
menjaga efisiensi pengurangan.

Singkatnya, saya akan memprioritaskan memperbaiki pengurangan daripada memperkenalkan yang baru
fungsi.

Memiliki start_indices dan end_indices, meskipun berguna dalam beberapa kasus, adalah
sering kali berlebihan dan saya akan melihatnya sebagai kemungkinan tambahan, tetapi bukan sebagai perbaikan
untuk perilaku tidak konsisten reduceat saat ini.

-
Anda menerima ini karena Anda berkomentar.
Balas email ini secara langsung, lihat di GitHub
https://github.com/numpy/numpy/issues/834#issuecomment-293898215 , atau nonaktifkan
utasnya
https://github.com/notifications/unsubscribe-auth/AAEz6xPex0fo2y_MqVHbNP5YNkJ0CBJrks5rviW-gaJpZM4ANcqc
.

Ini adalah perbedaan waktu lebih dari 100x dan menggambarkan pentingnya menjaga efisiensi pengurangan.

Terima kasih untuk itu - Saya kira saya meremehkan biaya overhead yang terkait dengan tahap pertama dari panggilan reduce (yang hanya terjadi sekali untuk reduceat ).

Bukan argumen yang menentang fungsi bebas, tapi tentu saja argumen yang menentang penerapannya dalam python murni

tetapi bukan sebagai perbaikan untuk perilaku yang tidak konsisten reduceat saat ini.

Masalahnya adalah, sulit untuk mengubah perilaku kode yang sudah ada sejak lama.


Ekstensi lain yang mungkin: ketika indices[i] > indices[j] , hitung kebalikannya:

    for i, (si, ei) in enumerate(zip(start, stop)):
        if si >= ei:
            func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
        else:
            func.reduce(arr[ei:si,...], out=out[i, ...], axis=axis)
            func.inverse(func.identity, out[i, ...], out=out[i, ...])

Dimana np.add.inverse = np.subtract , np.multiply.inverse = np.true_divide . Ini menghasilkan properti bagus itu

func.reduce(func.reduceat(x, inds_from_0)) == func.reduce(x))

Sebagai contoh

a = [1, 2, 3, 4]
inds = [0, 3, 1]
result = np.add.reduceat(a, inds) # [6, -5, 9] == [(1 + 2 + 3), -(3 + 2), (2 + 3 + 4)]

Masalahnya adalah, sulit untuk mengubah perilaku kode yang sudah ada sejak lama.

Inilah sebagian mengapa di utas email saya menyarankan untuk memberi arti khusus pada array indeks 2-D di mana dimensi tambahannya adalah 2 atau 3: kemudian (secara efektif) diinterpretasikan sebagai tumpukan irisan. Tetapi saya menyadari ini juga agak berantakan dan tentu saja seseorang mungkin juga memiliki metode reduce_by_slice , slicereduce , atau reduceslice .

ps Saya pikir apa pun yang bekerja pada banyak ufunc harus menjadi metode, sehingga dapat melewati __array_ufunc__ dan diganti.

Sebenarnya, saran lain yang menurut saya jauh lebih baik: daripada menyelamatkan reduceat , mengapa tidak menambahkan argumen slice (atau start , stop , step ) menjadi ufunc.reduce !? Seperti yang dicatat oleh @ eric-wieser, penerapan seperti itu berarti kita dapat menghentikan reduceat sama sekali, seperti yang akan terjadi

add.reduce(array, slice=slice(indices[:-1], indices[1:])

(di mana sekarang kita bebas untuk membuat perilaku sesuai dengan apa yang diharapkan untuk potongan kosong)

Di sini, seseorang akan menyiarkan irisan jika 0-d, dan bahkan mungkin mempertimbangkan untuk mengirimkan tupel irisan jika tupel sumbu digunakan.

EDIT: membuat slice(indices[:-1], indices[1:]) untuk memungkinkan ekstensi ke tupel irisan ( slice dapat menyimpan data sewenang-wenang, jadi ini akan berfungsi dengan baik).

Saya masih akan menemukan perbaikan untuk reduceat , untuk membuatnya menjadi versi vektor 100% yang tepat dari reduce , solusi desain yang paling logis. Sebagai alternatif, untuk menghindari kerusakan kode (tapi lihat di bawah), metode yang setara dengan nama seperti reducebins dapat dibuat, yang merupakan versi koreksi dari reduceat . Faktanya, saya setuju dengan @ eric-wieser bahwa penamaan reduceat memberikan lebih banyak koneksi ke fungsi at daripada yang ada.

Saya memahami kebutuhan untuk tidak memecahkan kode. Tetapi saya harus mengatakan bahwa saya merasa sulit untuk membayangkan bahwa banyak kode bergantung pada perilaku lama, mengingat bahwa itu sama sekali tidak masuk akal, dan saya hanya akan menyebutnya bug lama. Saya berharap kode yang menggunakan reduceat hanya memastikan indices tidak diduplikasi, untuk menghindari hasil yang tidak masuk akal dari reduceat , atau memperbaiki keluaran seperti yang saya lakukan menggunakan out[:-1] *= np.diff(indices) > 0 . Tentu saja saya akan tertarik dengan kasus pengguna di mana perilaku / bug lama digunakan sebagaimana mestinya.

Saya tidak sepenuhnya yakin tentang solusi @mhvk slice karena ini memperkenalkan penggunaan non-standar untuk konstruksi slice . Selain itu, ini akan menjadi tidak konsisten dengan ide desain saat ini reduce , yaitu untuk _ "mengurangi dimensi a satu per satu, dengan menerapkan ufunc di sepanjang satu sumbu." _

Saya juga tidak melihat kasus pengguna yang menarik untuk indeks start dan end . Sebenarnya, saya melihat logika desain yang bagus dari metode reduceat ini secara konseptual mirip dengan np.histogram , di mana bins , yang _ "mendefinisikan tepi bin," _ diganti dengan indices , yang juga mewakili tepi tempat sampah, tetapi dalam ruang indeks daripada nilai. Dan reduceat menerapkan fungsi ke elemen yang terdapat di dalam setiap pasang tepi tempat sampah. Histogram adalah konstruksi yang sangat populer, tetapi tidak perlu, dan di Numpy tidak menyertakan, opsi untuk melewatkan dua vektor tepi kiri dan kanan. Untuk alasan yang sama saya ragu ada kebutuhan yang kuat untuk kedua sisi di reduceat atau penggantinya.

Motivasi utama untuk reduceat adalah untuk menghindari loop over reduce untuk mendapatkan kecepatan maksimum. Jadi saya tidak sepenuhnya yakin pembungkus for loop over reduce akan menjadi tambahan yang sangat berguna untuk Numpy. Itu akan bertentangan dengan tujuan utama.

Saya setuju dengan @divenex di sini. Fakta bahwa reduceat membutuhkan indeks untuk diurutkan dan tumpang tindih adalah kendala yang masuk akal untuk memastikan bahwa loop dapat dihitung dengan cara yang efisien dalam cache dengan satu kali pass di atas data. Jika Anda ingin bin yang tumpang tindih, hampir pasti ada cara yang lebih baik untuk menghitung operasi yang diinginkan (misalnya, agregasi jendela bergulir).

Saya juga setuju bahwa solusi terbersih adalah dengan mendefinisikan metode baru seperti reducebins dengan API tetap (dan menghentikan reduceat ), dan tidak mencoba memerasnya menjadi reduce yang sudah melakukan sesuatu yang berbeda.

Halo semuanya,

Saya ingin menghentikan diskusi bahwa ini adalah bug. Ini adalah perilaku yang didokumentasikan dari docstring :

For i in ``range(len(indices))``, `reduceat` computes
``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th
generalized "row" parallel to `axis` in the final result (i.e., in a
2-D array, for example, if `axis = 0`, it becomes the i-th row, but if
`axis = 1`, it becomes the i-th column).  There are three exceptions to this:

* when ``i = len(indices) - 1`` (so for the last index),
  ``indices[i+1] = a.shape[axis]``.
* if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is
  simply ``a[indices[i]]``.
* if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised.

Karena itu, saya menentang segala upaya untuk mengubah perilaku reduceat .

Pencarian github cepat menunjukkan banyak sekali kegunaan dari fungsi tersebut. Apakah semua orang di sini yakin bahwa mereka semua hanya menggunakan indeks yang meningkat secara ketat?

Mengenai perilaku fungsi baru , saya berpendapat bahwa tanpa larik start / stop yang terpisah, fungsinya sangat terhambat. Ada banyak situasi di mana seseorang ingin mengukur nilai di jendela yang tumpang tindih yang tidak tersusun secara teratur (sehingga jendela bergulir tidak akan berfungsi). Misalnya, wilayah yang diminati ditentukan dengan beberapa metode independen. Dan @divenex telah menunjukkan bahwa perbedaan kinerja pada iterasi Python bisa sangat besar.

Ada banyak situasi di mana seseorang ingin mengukur nilai di jendela yang tumpang tindih yang tidak tersusun secara teratur (sehingga jendela bergulir tidak akan berfungsi).

Ya, tetapi Anda tidak ingin menggunakan perulangan naif seperti yang diterapkan oleh reduceat . Anda ingin menerapkan perhitungan jendela bergulir Anda sendiri yang menyimpan hasil antara dalam beberapa cara sehingga dapat dilakukan dalam satu jalur linier atas data. Tapi sekarang kita berbicara tentang algoritma yang jauh lebih rumit dari reduceat .

@ shoyer Saya dapat membayangkan kasus di mana hanya beberapa ROI yang tumpang tindih. Dalam kasus seperti itu, menulis algoritme khusus akan sangat berlebihan. Jangan lupa bahwa basis pengguna utama kita adalah para ilmuwan, yang biasanya kekurangan waktu dan membutuhkan solusi yang "cukup baik", bukan optimal absolut. Faktor konstanta rendah yang terkait dengan kompleksitas np.reduceat berarti akan sulit atau tidak mungkin untuk mendapatkan solusi yang lebih baik dengan kode Python murni - paling sering satu-satunya kode yang ingin ditulis pengguna.

@jni Tentu, mengurangi menjadi grup dengan awal dan akhir yang sewenang-wenang bisa berguna. Tapi ini terasa seperti peningkatan cakupan yang signifikan bagi saya, dan sesuatu yang lebih cocok untuk metode lain daripada pengganti reduceat (yang tentunya ingin kami hilangkan, meskipun kami tidak pernah menghapusnya).

mengurangi menjadi grup dengan awal dan penghentian yang sewenang-wenang bisa bermanfaat. Tapi bagi saya ini terasa seperti peningkatan yang signifikan

Ini sepertinya sangat sepele bagi saya. Saat ini, kami memiliki kode yang pada dasarnya ind1 = indices[i], ind2 = indices[i + 1] . Mengubahnya untuk menggunakan dua larik yang berbeda, bukan yang sama, akan membutuhkan sedikit usaha.

Dan perilaku single-pass ketika melewati rentang yang berdekatan harus hampir sama cepatnya seperti sekarang - satu-satunya overhead adalah satu argumen lagi ke nditer.

Ini sepertinya sangat sepele bagi saya.

Persis. Selain itu, ini adalah fungsionalitas yang dimiliki pengguna dengan reduceat (dengan menggunakan setiap indeks lainnya), tetapi akan kalah dengan fungsi baru yang tidak mendukung tumpang tindih.

Selain itu, bentuk dua indeks dapat meniru perilaku lama (aneh):

def reduceat(func, arr, inds):
    deprecation_warning()
    start = inds
    stops = zeros(inds.shape)
    stops[:-1] = start[1:]
    stops[-1] = len(arr)
    np.add(stops, 1, where=ends == starts, out=stops)  # reintroduce the "bug" that we would have to keep
    return reducebins(func, arr, starts, stops)

Artinya kita tidak perlu mempertahankan dua implementasi yang sangat mirip

Saya tidak terlalu menentang indeks starts dan stops untuk indeks reducebins , meskipun saya masih tidak dapat melihat contoh yang jelas di mana keduanya dibutuhkan. Rasanya seperti menggeneralisasi np.histogram dengan menambahkan awal dan akhir bins edge ...

Pada akhirnya, ini baik-baik saja selama penggunaan utama tidak terpengaruh dan masih dapat memanggil reducebins(arr, indices) dengan satu larik indeks dan tanpa penalti kecepatan.

Tentu saja ada banyak situasi di mana seseorang perlu beroperasi pada nampan yang tidak tumpang tindih, tetapi dalam kasus ini saya biasanya mengharapkan nampan tidak ditentukan oleh pasangan tepi saja. Fungsi yang tersedia untuk skenario semacam ini adalah ndimage.labeled_comprehension Scipy , dan fungsi terkait seperti ndimage.sum dan seterusnya.

Tetapi ini tampaknya sangat berbeda dari ruang lingkup reducebins .

Jadi, bagaimana kasus penggunaan alami untuk starts dan stops dalam reducebins ?

Jadi, apa kasus penggunaan alami untuk start dan stop di reducebins?

Dapat dicapai dengan cara lain, tetapi rata-rata pergerakan dengan panjang k akan menjadi reducebins(np,add, arr, arange(n-k), k + arange(n-k)) . Saya menduga bahwa mengabaikan biaya pengalokasian indeks, kinerja akan sebanding dengan pendekatan as_strided .

Uniknya, reducebins akan memungkinkan rata-rata bergerak dengan durasi yang bervariasi, yang tidak mungkin dilakukan dengan as_strided

Kasus penggunaan lain - membedakan antara memasukkan akhir atau awal dalam bentuk satu argumen.

Misalnya:

a = np.arange(10)
reducebins(np.add, start=[2, 4, 6]) == [2 + 3, 4 + 5, 6 + 7 + 8 + 9]  # what `reduceat` does
reducebins(np.add, stop=[2, 4, 6])  == [0 + 1, 2 + 3, 4 + 5]          # also useful

Kasus penggunaan lain - membedakan antara memasukkan akhir atau awal dalam bentuk satu argumen.

Saya tidak begitu mengerti yang ini. Bisakah Anda memasukkan tensor input di sini? Juga: apa yang akan menjadi nilai default untuk start / stop ?

Bagaimanapun, saya tidak menentang keras argumen terpisah, tapi itu tidak sebersih penggantinya. Saya ingin sekali mengatakan "Jangan gunakan reduceat, gunakan reducebins sebagai gantinya" tetapi itu (sedikit) lebih sulit ketika antarmuka terlihat berbeda.

Sebenarnya, saya baru menyadari bahwa bahkan opsi start / stop tidak mencakup kasus penggunaan irisan kosong, yang pernah berguna bagi saya di masa lalu: ketika properti / label saya sesuai dengan baris dalam matriks jarang CSR, dan saya menggunakan nilai indptr untuk melakukan pengurangan. Dengan reduceat , saya bisa mengabaikan baris kosong. Penggantian apa pun akan membutuhkan pembukuan tambahan. Jadi, apa pun pengganti yang Anda buat, silakan tinggalkan reduceat sekitar.

In [2]: A = np.random.random((4000, 4000))
In [3]: B = sparse.csr_matrix((A > 0.8) * A)
In [9]: %timeit np.add.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 1)
1000 loops, best of 3: 1.81 ms per loop
In [12]: %timeit B.sum(axis=1).A
100 loops, best of 3: 1.95 ms per loop
In [16]: %timeit np.maximum.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 0)
1000 loops, best of 3: 1.8 ms per loop
In [20]: %timeit B.max(axis=1).A
100 loops, best of 3: 2.12 ms per loop

Secara kebetulan, teka-teki urutan kosong dapat diselesaikan dengan cara yang sama seperti Python melakukannya : dengan memberikan nilai awal. Ini bisa berupa skalar atau array dengan bentuk yang sama seperti indices .

ya, saya setuju bahwa fokus pertama harus pada menyelesaikan irisan kosong
kasus. Dalam kasus start = end, kita dapat memiliki cara untuk mengatur output
elemen identitas, atau untuk tidak mengubah elemen keluaran dengan a
menentukan larik. Masalah dengan arus adalah bahwa itu ditimpa
dengan data yang tidak relevan

Saya sepenuhnya dengan @shoyer tentang komentar terakhirnya.

Mari kita definisikan out=ufunc.reducebins(a, inds) sebagai out[i]=ufunc.reduce(a[inds[i]:inds[i+1]]) untuk semua i tapi yang terakhir, dan hentikan reduceat .

Kasus penggunaan saat ini untuk starts dan ends indeks tampak lebih alami dan mungkin lebih efisien diimplementasikan dengan fungsi alternatif seperti as_strided atau konvolusi.

@septianjoko_

Saya tidak begitu mengerti yang ini. Bisakah Anda memasukkan tensor input di sini? Juga: apa yang akan menjadi nilai default untuk start / stop?

Diperbarui dengan masukan. Lihat implementasi reduce_bins di komentar yang memulai ini untuk nilai default. Saya telah menambahkan docstring di sana juga. Implementasi itu lengkap fitur tetapi lambat (karena menjadi python).

tetapi itu (sedikit) lebih sulit ketika antarmuka terlihat berbeda.

Ketika hanya satu argumen start yang dilewatkan, antarmukanya identik (mengabaikan kasus identitas yang kami tetapkan untuk diperbaiki di tempat pertama). Ketiga baris ini memiliki arti yang sama:

np.add.reduce_at(arr, inds)
reduce_bins(np.add, arr, inds)
reduce_bins(np.add, arr, start=inds)

(perbedaan metode / fungsi bukanlah sesuatu yang terlalu saya pedulikan, dan saya tidak dapat mendefinisikan metode ufunc baru sebagai prototipe dengan python!)


@jni :

Sebenarnya, saya baru menyadari bahwa bahkan opsi start / stop tidak mencakup kasus penggunaan dari irisan kosong, yang pernah berguna bagi saya di masa lalu.

Anda salah, itu benar - dengan cara yang sama persis seperti yang sudah dilakukan ufunc.reduceat . Ini juga mungkin hanya dengan mengirimkan start[i] == end[i] .

teka-teki urutan kosong dapat diselesaikan ... dengan memberikan nilai awal.

Ya, kita sudah membahas ini, dan ufunc.reduce sudah melakukannya dengan mengisi ufunc.identity . Ini tidak sulit untuk ditambahkan ke ufunc.reduecat , terutama jika # 8952 digabungkan. Namun seperti yang Anda katakan sendiri, perilaku saat ini adalah _documented_, jadi sebaiknya kita tidak mengubahnya.


@tokopedia

Mari kita definisikan out = ufunc.reducebins (a, inds) sebagai out [i] = ufunc.reduce (a [inds [i]: inds [i + 1]]) untuk semua i kecuali yang terakhir

Jadi len(out) == len(inds) - 1 ? Ini berbeda dengan perilaku saat ini reduceat , jadi argumen @shoyer tentang pengalihan lebih kuat di sini


Semua: Saya telah membaca komentar sebelumnya dan menghapus balasan email yang dikutip, karena mereka membuat diskusi ini sulit untuk dibaca

@ eric-wieser poin yang bagus. Dalam kalimat saya di atas, saya bermaksud bahwa untuk indeks terakhir perilaku reducebins akan berbeda seperti pada reduceat . Namun, dalam kasus itu, saya tidak yakin berapa nilainya, karena nilai terakhir secara formal tidak masuk akal.

Mengabaikan masalah kompatibilitas, output reducebins (dalam 1D) harus berukuran inds.size-1 , untuk alasan yang sama bahwa np.diff(a) memiliki ukuran a.size-1 dan np.histogram(a, bins) memiliki ukuran bins.size-1 . Namun ini bertentangan dengan keinginan untuk memiliki pengganti drop-in untuk reduceat .

Saya rasa tidak ada argumen yang meyakinkan bahwa a.size-1 adalah jawaban yang benar - termasuk indeks 0 dan / atau indeks n sepertinya juga merupakan perilaku yang masuk akal. Semuanya tampak berguna dalam beberapa keadaan, tetapi saya pikir sangat penting untuk memiliki pengganti.

Ada juga argumen lain untuk stop / start bersembunyi di sini - ini memungkinkan Anda untuk membangun perilaku seperti diff jika Anda menginginkannya, dengan biaya yang sangat sedikit, sambil tetap mempertahankan reduceat perilaku:

a = np.arange(10)
inds = [2, 4, 6]
reduce_bins(a, start=inds[:-1], stop=inds[1:])  #  [2 + 3, 4 + 5]

# or less efficiently:
reduce_at(a, inds)[:-1}
reduce_bins(a, start=inds)[:-1]
reduce_bins(a, stop=inds)[1:]

@ eric-Wieser saya akan OK dengan diperlukan start dan stop argumen, tapi aku tidak seperti membuat salah satu dari mereka opsional. Tidak jelas bahwa hanya menyediakan start berarti out[i] = func.reduce(arr[start[i]:start[i+1]]) daripada out[i] = func.reduce(arr[start[i]:]) , yang akan saya duga.

API pilihan saya untuk reducebins adalah seperti reduceat tetapi tanpa "pengecualian" yang membingungkan yang dicatat di docstring . Yakni saja:

Untuk i in range(len(indices)) , reduceat menghitung ufunc.reduce(a[indices[i]:indices[i+1]]) , yang menjadi "baris" umum ke-i yang sejajar dengan sumbu di hasil akhir (yaitu, dalam larik 2-D, misalnya, jika sumbu = 0, itu menjadi baris ke-i, tetapi jika sumbu = 1, itu menjadi kolom ke-i).

Saya bisa menggunakan "pengecualian" ketiga yang membutuhkan indeks non-negatif ( 0 <= indices[i] <= a.shape[axis] ), yang menurut saya lebih sebagai pemeriksaan kewarasan daripada pengecualian. Tetapi mungkin orang itu juga bisa pergi - saya dapat melihat bagaimana indeks negatif mungkin berguna bagi seseorang, dan tidak sulit melakukan matematika untuk menormalkan indeks semacam itu.

Tidak secara otomatis menambahkan indeks di akhir tidak menyiratkan bahwa hasil harus memiliki panjang len(a)-1 , seperti hasil dari np.histogram .

@jni Bisakah Anda memberikan contoh tentang apa yang sebenarnya ingin Anda hitung dari array yang ditemukan dalam matriks renggang? Lebih disukai dengan contoh konkret dengan angka non-acak, dan mandiri (tanpa bergantung pada scipy.sparse).

Tidak jelas bahwa menyediakan hanya start berarti keluar [i] = func.reduce (arr [start [i]: start [i + 1]]) daripada keluar [i] = func.reduce (arr [start [i] :]), seperti yang saya duga.

Bacaan yang saya tuju adalah bahwa "Setiap bin dimulai pada posisi ini", dengan implikasi bahwa semua nampan berdekatan kecuali secara eksplisit ditentukan lain. Mungkin saya harus mencoba dan membuat draf docstring yang lebih lengkap. Saya rasa saya dapat melihat argumen yang kuat untuk melarang melewatkan tidak satu pun argumen, jadi saya akan menghapusnya dari fungsi proposal saya.

yang membutuhkan indeks non-negatif (0 <= indeks [i] <a.shape [axis])

Perhatikan bahwa ada juga bug di sini (# 835) - batas atas harus inklusif, karena ini adalah irisan.

Perhatikan bahwa ada juga bug di sini - batas atas harus inklusif, karena ini adalah irisan.

Diperbaiki, terima kasih.

Tidak dalam fungsi reduceat itu sendiri, Anda belum;)

Ternyata :\doc\neps\groupby_additions.rst berisi proposal (IMO inferior) untuk fungsi reduceby .

Apakah halaman ini membantu?
0 / 5 - 0 peringkat