Thay thế tất cả các phần tử của Python NumPy Array lớn hơn một số giá trị


187

Tôi có một mảng NumPy 2D và muốn thay thế tất cả các giá trị trong nó lớn hơn hoặc bằng ngưỡng T bằng 255.0. Theo hiểu biết của tôi, cách cơ bản nhất sẽ là:

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. Cách ngắn gọn và pythonic nhất để làm điều này là gì?

  2. Có cách nào nhanh hơn (có thể ít súc tích hơn và / hoặc ít pythonic hơn) để làm điều này không?

Đây sẽ là một phần của chương trình con điều chỉnh cửa sổ / cấp độ để quét MRI đầu người. Mảng 2D numpy là dữ liệu pixel hình ảnh.


Để biết thêm thông tin, hãy xem phần giới thiệu này để lập chỉ mục .
askewchan

Câu trả lời:


329

Tôi nghĩ cả hai cách nhanh nhất và ngắn gọn nhất để làm điều này là sử dụng lập chỉ mục Fancy tích hợp của NumPy. Nếu bạn có ndarraytên arr, bạn có thể thay thế tất cả các thành phần >255bằng một giá trị xnhư sau:

arr[arr > 255] = x

Tôi đã chạy nó trên máy của mình với ma trận ngẫu nhiên 500 x 500, thay thế tất cả các giá trị> 0,5 bằng 5 và mất trung bình 7,59ms.

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
Lưu ý rằng điều này sửa đổi mảng hiện có arr, thay vì tạo một resultmảng như trong OP.
askewchan

1
Có cách nào để làm điều này bằng cách không sửa đổi Anhưng tạo ra một mảng mới?
natrinitrate

Chúng ta sẽ làm gì, nếu chúng ta muốn thay đổi giá trị tại các chỉ số là bội số của n đã cho, như [2], a [4], a [6], a [8] ..... cho n = 2?
lavee_singh

100 vòng, tốt nhất là 3: 2,22 ms mỗi vòng
dreab

5
LƯU Ý: điều này không hoạt động nếu dữ liệu nằm trong danh sách python, nó đã ở trong một mảng numpy ( np.array([1,2,3])
mjp

46

Vì bạn thực sự muốn một mảng khác nhau arrở đâu arr < 255, và 255nếu không, điều này có thể được thực hiện đơn giản:

result = np.minimum(arr, 255)

Tổng quát hơn, cho giới hạn dưới và / hoặc trên:

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

Nếu bạn chỉ muốn truy cập các giá trị trên 255 hoặc một cái gì đó phức tạp hơn, câu trả lời của @ mtitan8 thì chung chung hơn, np.clipnp.minimum(hoặc np.maximum) đẹp hơn và nhanh hơn cho trường hợp của bạn:

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

Nếu bạn muốn thực hiện tại chỗ (nghĩa là sửa đổi arrthay vì tạo result), bạn có thể sử dụng outtham số của np.minimum:

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

hoặc là

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

( out=tên là tùy chọn vì các đối số theo cùng thứ tự với định nghĩa của hàm.)

Để sửa đổi tại chỗ, lập chỉ mục boolean tăng tốc rất nhiều (không cần phải thực hiện và sau đó sửa đổi bản sao riêng biệt), nhưng vẫn không nhanh như 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

Để so sánh, nếu bạn muốn hạn chế các giá trị của mình ở mức tối thiểu cũng như tối đa, nếu không clipbạn sẽ phải làm điều này hai lần, với một cái gì đó như

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

hoặc là,

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

1
Cảm ơn bạn rất nhiều vì bình luận đầy đủ của bạn, tuy nhiên np.clip và np.minimum dường như không phải là điều tôi cần trong trường hợp này, trong OP bạn thấy rằng ngưỡng T và giá trị thay thế (255) không nhất thiết phải giống nhau con số. Tuy nhiên tôi vẫn cho bạn một phiếu bầu cho sự kỹ lưỡng. Cảm ơn một lần nữa.
NLi10Me

Chúng ta sẽ làm gì, nếu chúng ta muốn thay đổi giá trị tại các chỉ số là bội số của n đã cho, như [2], a [4], a [6], a [8] ..... cho n = 2?
lavee_singh

@lavee_singh, để làm điều đó, bạn có thể sử dụng phần thứ ba của slice, mà thường bị bỏ qua: a[start:stop:step]mang đến cho bạn các phần tử của mảng từ startđến stop, nhưng thay vì mỗi phần tử, nó chỉ mất mỗi step(nếu bị bỏ quên, đó là 1theo mặc định ). Vì vậy, để đặt tất cả các giá trị về 0, bạn có thể làma[::2] = 0
askewchan

Cảm ơn tôi cần một cái gì đó, như thế này, mặc dù tôi biết nó cho các danh sách đơn giản, nhưng tôi không biết liệu nó hoạt động như thế nào cho numpy.array.
lavee_singh

14

Tôi nghĩ bạn có thể đạt được điều này nhanh nhất bằng cách sử dụng wherechức năng:

Ví dụ: tìm kiếm các mục lớn hơn 0,2 trong một mảng gọn gàng và thay thế các mục bằng 0:

import numpy as np

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

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

10

Bạn có thể xem xét sử dụng numpy.putmask :

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

Dưới đây là so sánh hiệu suất với lập chỉ mục dựng sẵn của 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

Một cách khác là sử dụng np.placethay thế tại chỗ và hoạt động với các mảng đa chiều:

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)

Đây là giải pháp tôi đã sử dụng vì đây là lần đầu tiên tôi đi qua. Tôi tự hỏi nếu có một sự khác biệt lớn giữa điều này và câu trả lời được chọn ở trên. Bạn nghĩ sao?
jonathanking

Trong các thử nghiệm rất hạn chế của tôi, mã trên của tôi với np.place đang chạy chậm hơn 2 lần so với phương pháp lập chỉ mục trực tiếp của câu trả lời được chấp nhận. Thật đáng ngạc nhiên vì tôi đã nghĩ np.place sẽ được tối ưu hóa hơn nhưng tôi đoán có lẽ họ đã đặt nhiều công việc hơn vào việc lập chỉ mục trực tiếp.
Shital Shah

Trong trường hợp của tôi np.placecũng chậm hơn so với phương pháp tích hợp sẵn, mặc dù điều ngược lại được khẳng định trong nhận xét này .
riyansh. Cho vay

3

Bạn cũng có thể sử dụng &, |(và / hoặc) để linh hoạt hơn:

giá trị từ 5 đến 10: A[(A>5)&(A<10)]

các giá trị lớn hơn 10 hoặc nhỏ hơn 5: A[(A<5)|(A>10)]

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.