Bagaimana cara kerja python numpy.where ()?

95

Saya bermain numpydan menggali dokumentasi dan saya telah menemukan beberapa keajaiban. Yakni yang saya bicarakan numpy.where():

>>> x = np.arange(9.).reshape(3, 3)
>>> np.where( x > 5 )
(array([2, 2, 2]), array([0, 1, 2]))

Bagaimana mereka mencapai secara internal bahwa Anda dapat meneruskan sesuatu seperti x > 5ke dalam suatu metode? Saya kira itu ada hubungannya dengan __gt__tetapi saya mencari penjelasan rinci.

pajton
sumber

Jawaban:

75

Bagaimana mereka mencapai secara internal bahwa Anda dapat meneruskan sesuatu seperti x> 5 ke dalam sebuah metode?

Jawaban singkatnya adalah mereka tidak melakukannya.

Segala jenis operasi logis pada larik numpy mengembalikan larik boolean. (mis __gt__. __lt__, dll. Semua mengembalikan array boolean di mana kondisi yang diberikan benar).

Misalnya

x = np.arange(9).reshape(3,3)
print x > 5

hasil:

array([[False, False, False],
       [False, False, False],
       [ True,  True,  True]], dtype=bool)

Ini adalah alasan yang sama mengapa sesuatu seperti if x > 5:memunculkan ValueError jika xberupa array numpy. Ini adalah larik nilai Benar / Salah, bukan nilai tunggal.

Lebih lanjut, array numpy dapat diindeks oleh array boolean. Misalnya x[x>5]hasil panen [6 7 8], dalam hal ini.

Sejujurnya, sangat jarang Anda benar-benar membutuhkannya numpy.wheretetapi ini hanya mengembalikan indikasi di mana array boolean berada True. Biasanya Anda dapat melakukan apa yang Anda butuhkan dengan pengindeksan boolean sederhana.

Joe Kington
sumber
10
Hanya untuk menunjukkan bahwa numpy.wherememang memiliki 2 'mode operasional', yang pertama mengembalikan indices, di mana condition is Truedan jika parameter opsional xdan yada (bentuk yang sama seperti condition, atau disiarkan ke bentuk seperti itu!), Ia akan mengembalikan nilai dari xsaat condition is Truesebaliknya y. Jadi ini membuatnya wherelebih serbaguna dan memungkinkannya untuk digunakan lebih sering. Terima kasih
makan
1
Mungkin juga ada overhead dalam beberapa kasus menggunakan __getitem__sintaks []over baik numpy.whereatau numpy.take. Karena __getitem__harus juga mendukung pengirisan, ada beberapa overhead. Saya telah melihat perbedaan kecepatan yang nyata saat bekerja dengan struktur data Python Pandas dan secara logis mengindeks kolom yang sangat besar. Dalam kasus tersebut, jika Anda tidak perlu mengiris, kemudian takedan wheresebenarnya lebih baik.
ely
24

Jawaban Lama agak membingungkan. Ini memberi Anda LOKASI (semuanya) di mana pernyataan Anda benar.

begitu:

>>> a = np.arange(100)
>>> np.where(a > 30)
(array([31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
       48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
       65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
       82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
       99]),)
>>> np.where(a == 90)
(array([90]),)

a = a*40
>>> np.where(a > 1000)
(array([26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
       43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
       60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
       77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
       94, 95, 96, 97, 98, 99]),)
>>> a[25]
1000
>>> a[26]
1040

Saya menggunakannya sebagai alternatif untuk list.index (), tetapi memiliki banyak kegunaan lain juga. Saya tidak pernah menggunakannya dengan array 2D.

http://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html

Jawaban Baru Tampaknya orang tersebut menanyakan sesuatu yang lebih mendasar.

Pertanyaannya adalah bagaimana ANDA dapat mengimplementasikan sesuatu yang memungkinkan suatu fungsi (seperti di mana) mengetahui apa yang diminta.

Pertama, perhatikan bahwa memanggil salah satu operator pembanding melakukan hal yang menarik.

a > 1000
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True`,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)`

Ini dilakukan dengan membebani metode "__gt__". Contohnya:

>>> class demo(object):
    def __gt__(self, item):
        print item


>>> a = demo()
>>> a > 4
4

Seperti yang Anda lihat, "a> 4" adalah kode yang valid.

Anda bisa mendapatkan daftar lengkap dan dokumentasi dari semua fungsi yang kelebihan beban di sini: http://docs.python.org/reference/datamodel.html

Sesuatu yang luar biasa adalah betapa sederhananya melakukan ini. SEMUA operasi di python dilakukan sedemikian rupa. Mengatakan a> b sama dengan a. gt (b)!

Garrett Berg
sumber
3
Overloading operator perbandingan ini tampaknya tidak berfungsi dengan baik dengan ekspresi logis yang lebih kompleks - misalnya saya tidak dapat melakukannya np.where(a > 30 and a < 50)atau np.where(30 < a < 50)karena akhirnya mencoba mengevaluasi logika AND dari dua array boolean, yang sangat tidak berarti. Adakah cara untuk menulis kondisi seperti itu np.where?
davidA
@meowsqueaknp.where((a > 30) & (a < 50))
tibalt
Mengapa np.where () mengembalikan daftar dalam contoh Anda?
Andreas Yankopolus
0

np.wheremengembalikan sebuah tupel dengan panjang yang sama dengan dimensi dari ndarray numpy di mana ia dipanggil (dengan kata lain ndim) dan setiap item dari tupel adalah ndarray numpy dari indeks dari semua nilai tersebut dalam ndarray awal yang kondisinya adalah True. (Tolong jangan bingung dimensi dengan bentuk)

Sebagai contoh:

x=np.arange(9).reshape(3,3)
print(x)
array([[0, 1, 2],
      [3, 4, 5],
      [6, 7, 8]])
y = np.where(x>4)
print(y)
array([1, 2, 2, 2], dtype=int64), array([2, 0, 1, 2], dtype=int64))


y adalah tupel dengan panjang 2 karena x.ndim2. Item pertama dalam tupel berisi nomor baris semua elemen yang lebih besar dari 4 dan item ke-2 berisi nomor kolom dari semua item yang lebih besar dari 4. Seperti yang Anda lihat, [1,2,2 , 2] sesuai dengan nomor baris 5,6,7,8 dan [2,0,1,2] sesuai dengan nomor kolom 5,6,7,8 Perhatikan bahwa ndarray dilintasi sepanjang dimensi pertama (baris-bijaksana ).

Demikian pula,

x=np.arange(27).reshape(3,3,3)
np.where(x>4)


akan mengembalikan tupel dengan panjang 3 karena x memiliki 3 dimensi.

Tapi tunggu, masih ada lagi untuk np.where!

ketika dua argumen tambahan ditambahkan ke np.where; itu akan melakukan operasi penggantian untuk semua kombinasi baris-kolom berpasangan yang diperoleh oleh tupel di atas.

x=np.arange(9).reshape(3,3)
y = np.where(x>4, 1, 0)
print(y)
array([[0, 0, 0],
   [0, 0, 1],
   [1, 1, 1]])
Piyush Singh
sumber