แทนที่องค์ประกอบทั้งหมดของ Python NumPy Array ที่มากกว่าค่าบางส่วน


190

ฉันมีอาร์เรย์ 2D NumPy และต้องการแทนที่ค่าทั้งหมดในนั้นมากกว่าหรือเท่ากับ threshold T ด้วย 255.0 สำหรับความรู้ของฉันวิธีพื้นฐานที่สุดคือ:

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. อะไรคือวิธีที่กระชับและไพเราะที่สุดในการทำสิ่งนี้?

  2. มีวิธีที่เร็วขึ้น (อาจกระชับและ / หรือ pythonic น้อยกว่า) ในการทำเช่นนี้?

นี่จะเป็นส่วนหนึ่งของรูทีนย่อยการปรับหน้าต่าง / ระดับสำหรับการสแกน MRI ของหัวมนุษย์ อาร์เรย์ 2 มิติคือข้อมูลพิกเซลของภาพ


สำหรับข้อมูลเพิ่มเติมโปรดดูที่บทนำนี้เพื่อการจัดทำดัชนี
askewchan

คำตอบ:


332

ฉันคิดว่าทั้งวิธีที่เร็วและรัดกุมที่สุดในการทำเช่นนี้คือการใช้การจัดทำดัชนีแบบแฟนซีของ NumPy หากคุณมีndarrayชื่อarrคุณสามารถแทนที่องค์ประกอบทั้งหมด>255ด้วยค่าxดังนี้:

arr[arr > 255] = x

ฉันวิ่งบนเครื่องของฉันด้วยเมทริกซ์แบบสุ่ม 500 x 500 แทนที่ค่าทั้งหมด> 0.5 ด้วย 5 และใช้เวลาเฉลี่ย 7.59 มิลลิวินาที

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
โปรดทราบว่าสิ่งนี้จะแก้ไขอาเรย์ที่มีอยู่arrแทนการสร้างresultอาเรย์ใน OP
askewchan

1
มีวิธีการทำเช่นนี้โดยไม่แก้ไขAแต่สร้างอาร์เรย์ใหม่หรือไม่
sodiumnitrate

เราจะทำอย่างไรถ้าเราต้องการเปลี่ยนค่าที่ดัชนีซึ่งมีหลายค่าของ n ที่กำหนดเช่น [2], [4], [6], [8] ..... สำหรับ n = 2?
lavee_singh

100 ลูป, ดีที่สุดคือ 3: 2.22 ms ต่อลูป
dreab

5
หมายเหตุ: วิธีนี้ใช้ไม่ได้หากข้อมูลอยู่ในรายการหลามมันจะต้องอยู่ในอาร์เรย์ numpy ( np.array([1,2,3])
mjp

46

เนื่องจากคุณต้องการอาร์เรย์ที่แตกต่างกันซึ่งเป็นarrที่arr < 255และ255อื่น ๆ สิ่งนี้สามารถทำได้ง่ายๆ:

result = np.minimum(arr, 255)

โดยทั่วไปแล้วสำหรับขอบล่างและ / หรือบน:

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

หากคุณต้องการเข้าถึงค่าที่มากกว่า 255 หรือสิ่งที่ซับซ้อนกว่านี้คำตอบของ @ mtitan8 นั้นกว้างกว่า แต่np.clipและnp.minimum(หรือnp.maximum) นั้นดีกว่าและเร็วกว่ามากสำหรับกรณีของคุณ:

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

หากคุณต้องการที่จะทำมันในสถานที่ (เช่นแก้ไขarrแทนการสร้างresult) คุณสามารถใช้outพารามิเตอร์ของnp.minimum:

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

หรือ

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

( out=ชื่อเป็นทางเลือกเนื่องจากอาร์กิวเมนต์ในลำดับเดียวกันกับนิยามของฟังก์ชัน)

สำหรับการปรับเปลี่ยนในสถานที่การทำดัชนีบูลีนจะเพิ่มความเร็วขึ้นมาก (โดยไม่ต้องสร้างและแก้ไขสำเนาแยกต่างหาก) แต่ก็ยังไม่เร็วเท่า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

สำหรับการเปรียบเทียบหากคุณต้องการ จำกัด ค่าของคุณด้วยค่าต่ำสุดและสูงสุดโดยไม่clipต้องทำสองครั้งด้วยสิ่งที่ชอบ

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

หรือ,

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

1
ขอบคุณมากสำหรับความคิดเห็นทั้งหมดของคุณอย่างไรก็ตาม np.clip และ np.minimum ดูเหมือนจะไม่เป็นสิ่งที่ฉันต้องการในกรณีนี้ใน OP คุณจะเห็นว่าเกณฑ์ T และค่าการแทนที่ (255) ไม่จำเป็นต้องเหมือนกัน จำนวน. อย่างไรก็ตามฉันยังคงให้คะแนนคุณอย่างละเอียด ขอบคุณอีกครั้ง.
NLi10Me

เราจะทำอย่างไรถ้าเราต้องการเปลี่ยนค่าที่ดัชนีซึ่งมีหลายค่าของ n ที่กำหนดเช่น [2], [4], [6], [8] ..... สำหรับ n = 2?
lavee_singh

@ lavee_singh เพื่อทำเช่นนั้นคุณสามารถใช้ส่วนที่สามของส่วนแบ่งซึ่งมักจะถูกละเลย: a[start:stop:step]ให้องค์ประกอบของอาร์เรย์จากstartไปยังstopแต่แทนที่จะเป็นทุกองค์ประกอบจะใช้เวลาเพียงทุกองค์ประกอบstep(ถ้าถูกทอดทิ้งก็เป็น1ค่าเริ่มต้น ) ดังนั้นในการตั้งค่า evens ทั้งหมดให้เป็นศูนย์คุณสามารถทำได้a[::2] = 0
askewchan

ขอบคุณฉันต้องการบางสิ่งบางอย่างเช่นนี้แม้ว่าฉันจะรู้ว่ามันเป็นรายการง่าย ๆ แต่ฉันไม่รู้ว่ามันทำงานได้ดีหรือไม่สำหรับ numpy.array
lavee_singh

14

ฉันคิดว่าคุณสามารถทำสิ่งนี้ได้อย่างรวดเร็วที่สุดโดยใช้whereฟังก์ชั่น:

ตัวอย่างเช่นการค้นหาไอเท็มที่มากกว่า 0.2 ในอาเรย์ numpy และแทนที่ไอเท็มด้วย 0:

import numpy as np

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

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

10

คุณสามารถพิจารณาใช้numpy.putmask :

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

นี่คือการเปรียบเทียบประสิทธิภาพกับการจัดทำดัชนีของ 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

อีกวิธีหนึ่งคือการใช้np.placeซึ่งทำหน้าที่แทนในสถานที่และทำงานร่วมกับอาร์เรย์แบบหลายชั้น:

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)

นี่คือทางออกที่ฉันใช้เพราะเป็นครั้งแรกที่ฉันเจอ ฉันสงสัยว่ามีความแตกต่างใหญ่ระหว่างนี้และคำตอบที่เลือกข้างต้น คุณคิดอย่างไร?
jonathanking

ในการทดสอบที่ จำกัด มากโค้ดด้านบนของฉันกับ np.place กำลังรัน 2X ช้ากว่าวิธีการทำดัชนีโดยตรงของคำตอบที่ยอมรับ มันน่าแปลกใจเพราะฉันคิดว่า np.place จะได้รับการปรับให้เหมาะสมที่สุด แต่ฉันคิดว่าพวกเขาอาจใช้การจัดทำดัชนีโดยตรงมากขึ้น
Shital Shah

ในกรณีของฉัน np.placeยังช้าลงเมื่อเทียบกับในตัววิธีแม้จะตรงข้ามจะอ้างว่าในนี้แสดงความคิดเห็น
riyansh.legend

3

นอกจากนี้คุณยังสามารถใช้&, |(และ / หรือ) ความยืดหยุ่นมากขึ้น:

ค่าระหว่าง 5 และ 10: A[(A>5)&(A<10)]

ค่าที่มากกว่า 10 หรือน้อยกว่า 5: A[(A<5)|(A>10)]

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.