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?


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)

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 .


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]]

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)
>>> 

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.