Numpy: BUG: np.vectorize () berfungsi dua kali pada elemen pertama (seperti yang terlihat ketika fungsi memodifikasi objek yang bisa berubah)

Dibuat pada 8 Mar 2017  ·  7Komentar  ·  Sumber: numpy/numpy

Saya punya daftar kamus. Saya mencoba menggunakan np.vectorize untuk menerapkan fungsi yang memodifikasi elemen kamus untuk setiap kamus dalam daftar. Hasilnya tampaknya menunjukkan bahwa vectorize bekerja dua kali pada elemen pertama. Apakah ini bug yang bisa diperbaiki? (Mungkin terkait dengan fakta bahwa vectorize check type pada elemen pertama?) Di bawah ini adalah beberapa contoh kasus dan keluaran:

Kasus uji sederhana tanpa modifikasi kamus:

def fcn1(x):
    return x['b']
a = [{'b': 1} for _ in range(3) ]
print(a)
print(np.vectorize(fcn1)(a))
print(a, '\n\n')

keluaran:

[{'b': 1}, {'b': 1}, {'b': 1}]
[1 1 1]
[{'b': 1}, {'b': 1}, {'b': 1}]

Sekarang ubah kamus dan lihat bahwa fungsi tersebut diterapkan dua kali ke elemen pertama:

def fcn2(x):
    x['b'] += 1
    return x['b']
a = [{'b': 1} for _ in range(3) ]
print(a)
print(np.vectorize(fcn2)(a))
print(a, '\n\n')

keluaran:

[{'b': 1}, {'b': 1}, {'b': 1}]
[3 2 2]
[{'b': 3}, {'b': 2}, {'b': 2}]  

Coba modifikasi lain untuk memeriksa konsistensi bug:

def fcn3(x):
    x['b'] *= 2
    return x['b']
a = [{'b': 1} for _ in range(3) ]
print(a)
print(np.vectorize(fcn3)(a))
print(a, '\n\n')

keluaran:

[{'b': 1}, {'b': 1}, {'b': 1}]
[4 2 2]
[{'b': 4}, {'b': 2}, {'b': 2}]    

Anda dapat melakukan hal yang sama tanpa benar-benar memberikan nilai pengembalian (begitulah cara saya mencoba menggunakannya dalam kasus penggunaan saya):

def fcn4(x):
    x['b'] += 1
a = [{'b': 1} for _ in range(3) ]
print(a)
np.vectorize(fcn4)(a)
print(a, '\n\n')

keluaran:

[{'b': 1}, {'b': 1}, {'b': 1}]
[{'b': 3}, {'b': 2}, {'b': 2}]

Dan omong-omong, tidak ada yang istimewa tentang daftar panjang 3, Anda dapat mengubahnya dan melihat perilaku yang sama hanya elemen pertama yang dimodifikasi ganda.

Saya mengonfirmasi perilaku menggunakan Numpy versi 1.11.3 dan 1.12.0

EDIT:
Saya menemukan solusi yang juga menegaskan masalah "menguji tipe pada elemen pertama". Jika Anda menentukan argumen otypes , elemen pertama tidak akan dipukul dua kali:

def fcn(x):
    x['b'] += 1
    return x['b']
a = [{'b': 1} for _ in range(3)]
print a
print np.vectorize(fcn, otypes=[dict])(a)
print a, '\n\n'

keluaran:

[{'b': 1}, {'b': 1}, {'b': 1}]
[2 2 2]
[{'b': 2}, {'b': 2}, {'b': 2}]
00 - Bug

Komentar yang paling membantu

"Jika otypes tidak ditentukan, maka panggilan ke fungsi dengan argumen pertama akan digunakan untuk menentukan jumlah keluaran. Hasil dari panggilan ini akan disimpan dalam cache jika cache adalah True untuk mencegah pemanggilan fungsi dua kali. Namun, untuk mengimplementasikan cache, fungsi asli harus digabungkan yang akan memperlambat panggilan berikutnya, jadi lakukan ini hanya jika fungsi Anda mahal. "
https://docs.scipy.org/doc/numpy/reference/generated/numpy.vectorize.html

Jadi ini adalah perilaku yang terdokumentasi dengan baik, tetapi tampaknya berlawanan dengan intuisi. Jadi mungkin ini lebih merupakan peningkatan daripada perbaikan bug.

Semua 7 komentar

Saya baru saja mengedit posting saya dengan tes baru yang tampaknya mengkonfirmasi memeriksa jenis item pertama IS apa yang menyebabkan masalah

Testcase yang lebih sederhana:

a = np.array([1, 2, 3])
def f(x):
    print('got', x)
    return x
fv = np.vectorize(f)
y = fv(a)

Memberikan:

got 1
got 1
got 2
got 3

"Jika otypes tidak ditentukan, maka panggilan ke fungsi dengan argumen pertama akan digunakan untuk menentukan jumlah keluaran. Hasil dari panggilan ini akan disimpan dalam cache jika cache adalah True untuk mencegah pemanggilan fungsi dua kali. Namun, untuk mengimplementasikan cache, fungsi asli harus digabungkan yang akan memperlambat panggilan berikutnya, jadi lakukan ini hanya jika fungsi Anda mahal. "
https://docs.scipy.org/doc/numpy/reference/generated/numpy.vectorize.html

Jadi ini adalah perilaku yang terdokumentasi dengan baik, tetapi tampaknya berlawanan dengan intuisi. Jadi mungkin ini lebih merupakan peningkatan daripada perbaikan bug.

ah, jelas saya tidak membaca semua dokumen dengan cukup baik. Itu memperbaiki masalah panggilan ganda tetapi dengan biaya eksekusi yang lebih lambat. Jadi saya kira tidak ada cara untuk mencegah panggilan ganda ini sambil tetap mempertahankan kinerja?

misalnya, dalam numpy/lib/function_base.py dalam fungsi vectorize class _get_ufunc_and_otypes() , saya akan secara naif berpikir Anda dapat memodifikasi baris-baris ini:

inputs = [arg.flat[0] for arg in args]
outputs = func(*inputs)

untuk:

#earlier
import copy

...

inputs = copy.deepcopy([arg.flat[0] for arg in args])
outputs = func(*inputs)

Dan kemudian Anda tidak perlu menggunakan cache atau menentukan otypes tetapi saya pikir Anda menghindari memukul elemen yang sebenarnya bisa berubah dua kali. Tapi saya tidak tahu seberapa banyak kinerja yang dihasilkan dibandingkan dengan cache.

Saya baru saja memahami bahwa perilaku caching dirancang dengan mempertimbangkan waktu eksekusi fungsi yang mahal, tidak memikirkan kasus fungsi yang memodifikasi objek yang bisa berubah. Saya akan berpikir itu berpotensi untuk mengakomodasi modifikasi objek yang bisa berubah tanpa kinerja hit caching yang memiliki waktu eksekusi fungsi yang lama.

Saya pikir untuk operasi vektorisasi pada array objek yang sangat besar, sebenarnya mungkin lebih intensif secara komputasi untuk membuat salinan mendalam dari elemen pertama daripada biaya untuk menerapkan fungsi. Jadi mungkin ide yang bagus untuk memiliki fungsionalitas itu jika pengguna tidak menentukan otype , tetapi kemudian mengatakan dalam dokumentasi bahwa kinerja mungkin akan terpukul kecuali otype ditentukan untuk array besar benda. @ eric-wieser bagaimana menurut Anda?

copy.deepcopy() tidak aman untuk banyak input, jadi sayangnya itu bukan opsi yang layak.

Satu-satunya cara untuk memperbaikinya adalah dengan menulis ulang inti vectorize , yang saat ini menggunakan numpy.frompyfunc untuk membuat ufunc yang sebenarnya numpy. Perulangan dalam alternatif perlu dibuat, mirip dengan yang kita gunakan untuk np.apply_along_axis . Sayangnya, untuk menghindari penurunan kinerja, saya pikir kita perlu melakukan loop di C (seperti frompyfunc lakukan di ufunc yang dibuatnya).

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

toddrjen picture toddrjen  ·  4Komentar

Kreol64 picture Kreol64  ·  3Komentar

inducer picture inducer  ·  3Komentar

perezpaya picture perezpaya  ·  4Komentar

dmvianna picture dmvianna  ·  4Komentar