Ganti semua elemen Python NumPy Array yang lebih besar dari beberapa nilai


190

Saya memiliki array NumPy 2D dan ingin mengganti semua nilai di dalamnya lebih besar atau sama dengan ambang T dengan 255.0. Sepengetahuan saya, cara yang paling mendasar adalah:

shape = arr.shape
result = np.zeros(shape)
for x in range(0, shape[0]):
    for y in range(0, shape[1]):
        if arr[x, y] >= T:
            result[x, y] = 255
  1. Apa cara paling ringkas dan pythonic untuk melakukan ini?

  2. Apakah ada cara yang lebih cepat (mungkin kurang ringkas dan / atau kurang pythonic) untuk melakukan ini?

Ini akan menjadi bagian dari subroutine penyesuaian level / jendela untuk pemindaian MRI kepala manusia. Array numpy 2D adalah data piksel gambar.


Untuk informasi lebih lanjut, lihat pengantar pengindeksan ini .
askewchan

Jawaban:


333

Saya pikir cara tercepat dan paling ringkas untuk melakukan ini adalah dengan menggunakan pengindeksan NumPy bawaan. Jika Anda memiliki ndarraynama arr, Anda dapat mengganti semua elemen >255dengan nilai xsebagai berikut:

arr[arr > 255] = x

Saya menjalankan ini pada mesin saya dengan matriks acak 500 x 500, mengganti semua nilai> 0,5 dengan 5, dan butuh rata-rata 7,59 ms.

In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)
In [3]: timeit A[A > 0.5] = 5
100 loops, best of 3: 7.59 ms per loop

3
Perhatikan bahwa ini memodifikasi array yang ada arr, alih-alih membuat resultarray seperti pada OP.
askewchan

1
Apakah ada cara untuk melakukan ini dengan tidak memodifikasi Atetapi membuat array baru?
sodiumnitrate

Apa yang akan kita lakukan, jika kita ingin mengubah nilai pada indeks yang merupakan kelipatan dari n yang diberikan, seperti [2], a [4], a [6], a [8] ..... untuk n = 2?
lavee_singh

100 loop, terbaik 3: 2.22 ms per loop
dreab

5
CATATAN: ini tidak berfungsi jika data ada dalam daftar python, itu HARUS berada dalam array numpy ( np.array([1,2,3])
mjp

46

Karena Anda sebenarnya menginginkan array yang berbeda di arrmana arr < 255, dan 255sebaliknya, ini bisa dilakukan dengan mudah:

result = np.minimum(arr, 255)

Secara umum, untuk batas bawah dan / atau atas:

result = np.clip(arr, 0, 255)

Jika Anda hanya ingin mengakses nilai lebih dari 255, atau sesuatu yang lebih rumit, jawaban @ mtitan8 lebih umum, tetapi np.clipdan np.minimum(atau np.maximum) lebih baik dan lebih cepat untuk kasus Anda:

In [292]: timeit np.minimum(a, 255)
100000 loops, best of 3: 19.6 µs per loop

In [293]: %%timeit
   .....: c = np.copy(a)
   .....: c[a>255] = 255
   .....: 
10000 loops, best of 3: 86.6 µs per loop

Jika Anda ingin melakukannya di tempat (yaitu, memodifikasi arralih-alih membuat result) Anda dapat menggunakan outparameter np.minimum:

np.minimum(arr, 255, out=arr)

atau

np.clip(arr, 0, 255, arr)

( out=nama bersifat opsional karena argumen dalam urutan yang sama dengan definisi fungsi.)

Untuk modifikasi di tempat, pengindeksan boolean mempercepat banyak (tanpa harus membuat dan kemudian memodifikasi salinan secara terpisah), tetapi masih tidak secepat minimum:

In [328]: %%timeit
   .....: a = np.random.randint(0, 300, (100,100))
   .....: np.minimum(a, 255, a)
   .....: 
100000 loops, best of 3: 303 µs per loop

In [329]: %%timeit
   .....: a = np.random.randint(0, 300, (100,100))
   .....: a[a>255] = 255
   .....: 
100000 loops, best of 3: 356 µs per loop

Sebagai perbandingan, jika Anda ingin membatasi nilai Anda dengan minimum dan maksimum, tanpa clipAnda harus melakukan ini dua kali, dengan sesuatu seperti

np.minimum(a, 255, a)
np.maximum(a, 0, a)

atau,

a[a>255] = 255
a[a<0] = 0

1
Terima kasih banyak atas komentar lengkap Anda, namun np.clip dan np.minimum tampaknya bukan yang saya butuhkan dalam kasus ini, dalam OP Anda melihat bahwa ambang T dan nilai penggantian (255) tidak harus sama jumlah. Namun saya masih memberi Anda suara untuk ketelitian. Terima kasih lagi.
NLi10Me

Apa yang akan kita lakukan, jika kita ingin mengubah nilai pada indeks yang merupakan kelipatan dari n yang diberikan, seperti [2], a [4], a [6], a [8] ..... untuk n = 2?
lavee_singh

@ lavee_singh, untuk melakukan itu, Anda dapat menggunakan bagian ketiga dari slice, yang biasanya diabaikan: a[start:stop:step]memberi Anda elemen-elemen array dari startke stop, tetapi alih-alih setiap elemen, dibutuhkan hanya setiap step(jika diabaikan, itu secara 1default) ). Jadi untuk mengatur semua GENAP menjadi nol, Anda bisa melakukannyaa[::2] = 0
askewchan

Terima kasih saya butuh sesuatu, seperti ini, walaupun saya tahu itu untuk daftar sederhana, tetapi saya tidak tahu apakah atau bagaimana cara kerjanya untuk numpy.array.
lavee_singh

14

Saya pikir Anda dapat mencapai ini dengan tercepat menggunakan wherefungsi:

Misalnya mencari item yang lebih besar dari 0,2 dalam array numpy dan menggantikannya dengan 0:

import numpy as np

nums = np.random.rand(4,3)

print np.where(nums > 0.2, 0, nums)

10

Anda dapat mempertimbangkan menggunakan numpy.putmask :

np.putmask(arr, arr>=T, 255.0)

Berikut adalah perbandingan kinerja dengan pengindeksan bawaan Numpy:

In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)

In [3]: timeit np.putmask(A, A>0.5, 5)
1000 loops, best of 3: 1.34 ms per loop

In [4]: timeit A[A > 0.5] = 5
1000 loops, best of 3: 1.82 ms per loop

8

Cara lain adalah dengan menggunakan np.placeyang melakukan penggantian di tempat dan bekerja dengan array multidimensi:

import numpy as np

# create 2x3 array with numbers 0..5
arr = np.arange(6).reshape(2, 3)

# replace 0 with -10
np.place(arr, arr == 0, -10)

Ini adalah solusi yang saya gunakan karena ini adalah yang pertama kali saya temui. Saya ingin tahu apakah ada perbedaan besar antara ini dan jawaban yang dipilih di atas. Bagaimana menurut anda?
jonathanking

Dalam tes saya yang sangat terbatas, kode saya di atas dengan np.place menjalankan 2X lebih lambat daripada metode pengindeksan jawaban langsung yang diterima. Ini mengejutkan karena saya akan berpikir np.place akan lebih dioptimalkan tetapi saya kira mereka mungkin lebih banyak bekerja pada pengindeksan langsung.
Shital Shah

Dalam kasus saya np.placejuga lebih lambat dibandingkan dengan metode built-in, meskipun sebaliknya diklaim dalam komentar ini .
riyansh.legend

3

Anda juga dapat menggunakan &, |(dan / atau) untuk lebih banyak fleksibilitas:

nilai antara 5 dan 10: A[(A>5)&(A<10)]

nilai lebih besar dari 10 atau lebih kecil dari 5: A[(A<5)|(A>10)]

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.