Python NumPy Array öğesinin bir değerden büyük tüm öğelerini değiştir


190

Bir 2D NumPy dizisi var ve tüm değerleri 255.0 ile bir eşik T veya daha büyük değiştirmek istiyorum. Bildiğim kadarıyla en temel yol:

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. Bunu yapmanın en özlü ve pitonik yolu nedir?

  2. Bunu yapmanın daha hızlı (muhtemelen daha az özlü ve / veya daha az pitonik) bir yolu var mı?

Bu, insan kafasının MRI taramaları için bir pencere / seviye ayarlama altyordamının bir parçası olacaktır. 2B numpy dizisi görüntü piksel verileridir.


Daha fazla bilgi için bu dizine ekleme girişine bakın .
askewchan

Yanıtlar:


332

Bunu yapmanın hem en hızlı hem de en kısa yolunun NumPy'nin yerleşik Fancy dizinini kullanmak olduğunu düşünüyorum. Bir varsa ndarrayadlandırılmış arr, tüm unsurları yerine >255bir değerle xaşağıdaki gibi:

arr[arr > 255] = x

Bunu makinemde 500 x 500 rasgele bir matrisle çalıştırdım,> 0,5'in üzerindeki tüm değerleri 5 ile değiştirdim ve ortalama 7,59 ms sürdü.

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
arrBunun resultOP'de olduğu gibi bir dizi oluşturmak yerine mevcut diziyi değiştirdiğini unutmayın .
askewchan

1
Bunu değiştirerek Adeğil, yeni bir dizi oluşturarak yapmanın bir yolu var mı ?
sodiumnitrate

Eğer n = 2 için verilen bir n'nin katları olan [2], a [4], a [6], a [8] gibi endekslerdeki değerleri değiştirmek istersek ne yapardık?
lavee_singh

100 döngü, en iyisi 3: döngü başına 2.22 ms
dreab

5
NOT: veriler bir python listesindeyse bu çalışmaz, bir numpy dizisinde ( np.array([1,2,3]) olmalıdır
mjp

46

Aslında arrnerede olduğu farklı bir dizi istediğiniz arr < 255ve 255aksi takdirde, bu basitçe yapılabilir:

result = np.minimum(arr, 255)

Daha genel olarak, bir alt ve / veya üst sınır için:

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

Sadece 255'in üzerindeki değerlere veya daha karmaşık bir şeye erişmek istiyorsanız, @ mtitan8'in cevabı daha geneldir, ancak np.clipve np.minimum(veya np.maximum) davanız için daha güzel ve çok daha hızlıdır:

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

Yerinde yapmak (yani arroluşturmak yerine değiştirmek result) istiyorsanız, şu outparametreyi kullanabilirsiniz np.minimum:

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

veya

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

( out=işlev isteğe bağlı olarak aynı sırada yer alan bağımsız değişkenler olduğundan ad isteğe bağlıdır.)

Yerinde değişiklik için, boole endekslemesi çok hızlanır (kopyayı ayrı ayrı yapmak ve sonra değiştirmek zorunda kalmadan), ancak yine de şu kadar hızlı değildir 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

Karşılaştırma için, değerlerinizi minimum ve maksimum değerlerle sınırlamak isterseniz, clipbunu iki kez yapmak zorunda kalmazsınız,

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

veya,

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

1
Tam yorumunuz için çok teşekkür ederim, ancak np.clip ve np.minimum bu durumda ihtiyacım olan şey gibi görünmüyor, OP'de T eşiği ve değiştirme değerinin (255) mutlaka aynı olmadığını görüyorsunuz numara. Ancak yine de size titizlik için oy verdim. Tekrar teşekkürler.
NLi10Me

Eğer n = 2 için verilen bir n'nin katları olan [2], a [4], a [6], a [8] gibi endekslerdeki değerleri değiştirmek istersek ne yapardık?
lavee_singh

@lavee_singh, bunu yapmak için, genellikle ihmal edilen dilimin üçüncü bölümünü kullanabilirsiniz: a[start:stop:step]dizinin öğelerini ile arasında startverir stop, ancak her öğe yerine yalnızca her şeyi alır step(ihmal edilirse, 1varsayılan olarak ). Yani tüm a[::2] = 0
kötülükleri

Teşekkürler Basit listeler için bilsem de böyle bir şeye ihtiyacım vardı, ancak numpy.array için çalışıp çalışmadığını veya nasıl çalıştığını bilmiyordum.
lavee_singh

14

Bence bu whereişlevi kullanarak bunu en hızlı şekilde başarabilirsiniz :

Örneğin, sayısal bir dizide 0,2'den büyük öğeleri aramak ve bunları 0 ile değiştirmek:

import numpy as np

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

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

10

Numpy.putmask kullanmayı düşünebilirsiniz :

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

İşte Numpy'nin yerleşik indeksleme ile bir performans karşılaştırması:

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

Başka bir yol, np.placeyerinde değiştirme yapan ve çok boyutlu dizilerle çalışan kullanmaktır:

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)

Kullandığım çözüm bu, çünkü ilk karşılaştığım çözüm buydu. Bu ve yukarıdaki seçilen cevap arasında büyük bir fark olup olmadığını merak ediyorum. Ne düşünüyorsun?
jonathanking

Çok sınırlı testlerimde, yukarıdaki np.place kodum, kabul edilen yanıtın doğrudan dizin oluşturma yönteminden 2 kat daha yavaş çalışıyor. Şaşırtıcı çünkü np.place'in daha optimize olacağını düşünürdüm ama sanırım muhtemelen doğrudan endeksleme üzerinde daha fazla çalışma yapmışlar.
Shital Shah

Benim durumumda np.place, bu yorumda tersi iddia edilmesine rağmen, yerleşik yönteme kıyasla da daha yavaştı .
riyansh.legend

3

Daha fazla esneklik için &, |(ve / veya) kullanabilirsiniz :

5 ile 10 arasındaki değerler: A[(A>5)&(A<10)]

10'dan büyük veya 5'ten küçük değerler: A[(A<5)|(A>10)]

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.