Mengganti elemen Numpy jika kondisi terpenuhi

94

Saya memiliki array numpy besar yang perlu saya manipulasi sehingga setiap elemen diubah menjadi 1 atau 0 jika suatu kondisi terpenuhi (akan digunakan sebagai topeng piksel nanti). Ada sekitar 8 juta elemen dalam array dan metode saya saat ini membutuhkan waktu terlalu lama untuk pipeline reduksi:

for (y,x), value in numpy.ndenumerate(mask_data): 

    if mask_data[y,x]<3: #Good Pixel
        mask_data[y,x]=1
    elif mask_data[y,x]>3: #Bad Pixel
        mask_data[y,x]=0

Apakah ada fungsi numpy yang akan mempercepat ini?

ChrisFro
sumber
1
Apa yang Anda inginkan terjadi jika mask_data[y,x]==3?
DSM
Poin bagus, itu masih akan menjadi piksel yang buruk. Saya akan mengubah kondisi menjadiif mask_data[y,x]>=3:
ChrisFro

Jawaban:

128
>>> import numpy as np
>>> a = np.random.randint(0, 5, size=(5, 4))
>>> a
array([[4, 2, 1, 1],
       [3, 0, 1, 2],
       [2, 0, 1, 1],
       [4, 0, 2, 3],
       [0, 0, 0, 2]])
>>> b = a < 3
>>> b
array([[False,  True,  True,  True],
       [False,  True,  True,  True],
       [ True,  True,  True,  True],
       [False,  True,  True, False],
       [ True,  True,  True,  True]], dtype=bool)
>>> 
>>> c = b.astype(int)
>>> c
array([[0, 1, 1, 1],
       [0, 1, 1, 1],
       [1, 1, 1, 1],
       [0, 1, 1, 0],
       [1, 1, 1, 1]])

Anda dapat mempersingkatnya dengan:

>>> c = (a < 3).astype(int)
Steve Barnes
sumber
2
bagaimana membuat ini terjadi dengan kolom tertentu tanpa pernah memotong beberapa kolom dan kemudian menetapkan kembali? misalnya, hanya elemen di kolom [2, 3] yang harus berubah nilai saat kondisi terpenuhi, sementara kolom lain tidak akan berubah terlepas dari kondisi terpenuhi atau tidak.
kuixiong
Benar, tetapi hanya untuk kasus nol dan satu. Lihat jawaban yang lebih umum di bawah ini (dengan biaya efisiensi)
borgr
89
>>> a = np.random.randint(0, 5, size=(5, 4))
>>> a
array([[0, 3, 3, 2],
       [4, 1, 1, 2],
       [3, 4, 2, 4],
       [2, 4, 3, 0],
       [1, 2, 3, 4]])
>>> 
>>> a[a > 3] = -101
>>> a
array([[   0,    3,    3,    2],
       [-101,    1,    1,    2],
       [   3, -101,    2, -101],
       [   2, -101,    3,    0],
       [   1,    2,    3, -101]])
>>>

Lihat, misalnya, Mengindeks dengan array boolean .

ev-br
sumber
3
barang bagus, terima kasih! Jika Anda ingin mengacu pada nilai yang Anda ubah, Anda dapat menggunakan sesuatu seperti a[a > 3] = -101+a[a > 3].
pexmar
1
@pexmar Meskipun jika Anda melakukannya a[a > 3] = -101+a[a > 3]bukan a[a > 3] += -101Anda kemungkinan besar akan kebocoran memori wajah.
Samuel Sebelumnya
1
bagaimana Anda merujuk ke nilai yang Anda ubah seperti yang diminta pexmar ??
Juan
34

Cara tercepat (dan paling fleksibel) adalah dengan menggunakan np.where , yang memilih antara dua larik sesuai dengan topeng (larik nilai benar dan salah):

import numpy as np
a = np.random.randint(0, 5, size=(5, 4))
b = np.where(a<3,0,1)
print('a:',a)
print()
print('b:',b)

yang akan menghasilkan:

a: [[1 4 0 1]
 [1 3 2 4]
 [1 0 2 1]
 [3 1 0 0]
 [1 4 0 1]]

b: [[0 1 0 0]
 [0 1 0 1]
 [0 0 0 0]
 [1 0 0 0]
 [0 1 0 0]]
Markus Dutschke
sumber
1
apa cara terbaik jika saya tidak ingin mengganti dengan apa pun jika kondisi tidak terpenuhi? yaitu hanya mengganti dengan nilai yang diberikan ketika kondisi terpenuhi, jika tidak biarkan nomor aslinya apa adanya ....
Abhishek Sengupta
1
untuk mengganti semua nilai dalam a, yang lebih kecil dari 3 dan mempertahankan sisanya seperti apa adanya, gunakana[a<3] = 0
Markus Dutschke
3

Anda dapat membuat array topeng Anda dalam satu langkah seperti ini

mask_data = input_mask_data < 3

Ini membuat array boolean yang kemudian bisa digunakan sebagai topeng piksel. Perhatikan bahwa kami belum mengubah array input (seperti dalam kode Anda) tetapi telah membuat array baru untuk menyimpan data mask - Saya akan merekomendasikan melakukannya dengan cara ini.

>>> input_mask_data = np.random.randint(0, 5, (3, 4))
>>> input_mask_data
array([[1, 3, 4, 0],
       [4, 1, 2, 2],
       [1, 2, 3, 0]])
>>> mask_data = input_mask_data < 3
>>> mask_data
array([[ True, False, False,  True],
       [False,  True,  True,  True],
       [ True,  True, False,  True]], dtype=bool)
>>> 
YXD
sumber
1
Ya. Jika OP benar-benar menginginkan 0 dan 1, dia dapat menggunakan .astype(int)atau *1, tetapi array dari Truedan Falsesama baiknya dengan itu.
DSM
-4

Saya tidak yakin saya mengerti pertanyaan Anda, tetapi jika Anda menulis:

mask_data[:3, :3] = 1
mask_data[3:, 3:] = 0

Ini akan membuat semua nilai data mask yang indeks x dan y kurang dari 3 menjadi sama dengan 1 dan sisanya sama dengan 0

mamalos
sumber