NumPy Array konumunda en yakın sıfır bulun


12

Diyelim ki bir NumPy dizim var:

x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])

Her dizinde en yakın sıfır değerine olan mesafeyi bulmak istiyorum. Konum sıfır ise, sıfır olarak mesafe olarak geri dönün. Daha sonra, yalnızca mevcut konumun sağındaki en yakın sıfıra olan mesafelerle ilgileniyoruz. Süper saf yaklaşım şöyle olacaktır:

out = np.full(x.shape[0], x.shape[0]-1)
for i in range(x.shape[0]):
    j = 0
    while i + j < x.shape[0]:
        if x[i+j] == 0:
            break
        j += 1
    out[i] = j

Ve çıktı:

array([0, 2, 1, 0, 4, 3, 2, 1, 0, 0])

Sıfırlar arasındaki çıkışta bir geri sayım / azalma deseni görüyorum. Yani, sıfırların konumlarını kullanabilirim (yani zero_indices = np.argwhere(x == 0).flatten())

Doğrusal zamanda istenen çıktıyı almanın en hızlı yolu nedir?


Sağda 0 yoksa ne olur?
Divakar

Büyük soru, o zaman son dizinin varsayılan gerektiğini (yani x.shape[0] - 1)
Lahana Salatası

Yanıtlar:


8

Yaklaşım # 1: Searchsorted Doğrusal zaman için vektörize edilmiş bir şekilde kurtarmaya (numba adamları gelmeden önce)!

mask_z = x==0
idx_z = np.flatnonzero(mask_z)
idx_nz = np.flatnonzero(~mask_z)

# Cover for the case when there's no 0 left to the right
# (for same results as with posted loop-based solution)
if x[-1]!=0:
    idx_z = np.r_[idx_z,len(x)]

out = np.zeros(len(x), dtype=int)
idx = np.searchsorted(idx_z, idx_nz)
out[~mask_z] = idx_z[idx] - idx_nz

Yaklaşım # 2: Bazıları ile bir diğeri cumsum-

mask_z = x==0
idx_z = np.flatnonzero(mask_z)

# Cover for the case when there's no 0 left to the right
if x[-1]!=0:
    idx_z = np.r_[idx_z,len(x)]

out = idx_z[np.r_[False,mask_z[:-1]].cumsum()] - np.arange(len(x))

Alternatif olarak, son adımının cumsumyerini repeatişlevsellik alabilir -

r = np.r_[idx_z[0]+1,np.diff(idx_z)]
out = np.repeat(idx_z,r)[:len(x)] - np.arange(len(x))

Yaklaşım # 3: Çoğunlukla cumsum-

mask_z = x==0
idx_z = np.flatnonzero(mask_z)

pp = np.full(len(x), -1)
pp[idx_z[:-1]] = np.diff(idx_z) - 1
if idx_z[0]==0:
    pp[0] = idx_z[1]
else:
    pp[0] = idx_z[0]
out = pp.cumsum()

# Handle boundary case and assigns 0s at original 0s places
out[idx_z[-1]:] = np.arange(len(x)-idx_z[-1],0,-1)
out[mask_z] = 0

4

Diğer taraftan çalışabilirsin. Kaç tane sıfır olmayan basamak geçtiğini bir sayaç tutun ve bunu dizideki öğeye atayın. 0 görürseniz sayacı 0'a sıfırlayın

Düzenleme: sağda sıfır yoksa, başka bir kontrole ihtiyacınız var

x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
out = x 
count = 0 
hasZero = False 
for i in range(x.shape[0]-1,-1,-1):
    if out[i] != 0:
        if not hasZero: 
            out[i] = x.shape[0]-1
        else:
            count += 1
            out[i] = count
    else:
        hasZero = True
        count = 0
print(out)

2

Önceki sıfıra olan mesafeyi belirlemek için, her bir konumun indeksleri ile sıfır konumlarının kümülatif maks. Konumu arasındaki farkı kullanabilirsiniz. Bu ileri ve geri yapılabilir. Önceki (veya sonraki) sıfıra ileri ve geri mesafe arasındaki minimum mesafe en yakın olacaktır:

import numpy as np

indices  = np.arange(x.size)
zeroes   = x==0
forward  = indices - np.maximum.accumulate(indices*zeroes)  # forward distance
forward[np.cumsum(zeroes)==0] = x.size-1                    # handle absence of zero from edge
forward  = forward * (x!=0)                                 # set zero positions to zero                

zeroes   = zeroes[::-1]
backward = indices - np.maximum.accumulate(indices*zeroes) # backward distance
backward[np.cumsum(zeroes)==0] = x.size-1                  # handle absence of zero from edge
backward = backward[::-1] * (x!=0)                         # set zero positions to zero

distZero = np.minimum(forward,backward) # closest distance (minimum)

Sonuçlar:

distZero
# [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]

forward
# [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]

backward
# [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]

Dış kenarlarda sıfır bulunmayan özel durum:

x = np.array([3, 1, 2, 0, 4, 5, 6, 0,8,8])

forward:  [9 9 9 0 1 2 3 0 1 2]
backward: [3 2 1 0 3 2 1 0 9 9]
distZero: [3 2 1 0 1 2 1 0 1 2]

sıfır olmadan da çalışır

[EDIT]  numpy olmayan çözümler ...

numpy gerektirmeyen bir O (N) çözümü arıyorsanız, itertools'un biriktirme işlevini kullanarak bu stratejiyi uygulayabilirsiniz:

x = [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]

from itertools import accumulate

maxDist  = len(x) - 1
zeroes   = [maxDist*(v!=0) for v in x]
forward  = [*accumulate(zeroes,lambda d,v:min(maxDist,(d+1)*(v!=0)))]
backward = accumulate(zeroes[::-1],lambda d,v:min(maxDist,(d+1)*(v!=0)))
backward = [*backward][::-1]
distZero = [min(f,b) for f,b in zip(forward,backward)]                      

print("x",x)
print("f",forward)
print("b",backward)
print("d",distZero)

çıktı:

x [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
f [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
b [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
d [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]

Herhangi bir kütüphane kullanmak istemiyorsanız, mesafeleri manuel olarak bir döngü içinde toplayabilirsiniz:

x = [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
forward,backward = [],[]
fDist = bDist = maxDist = len(x)-1
for f,b in zip(x,reversed(x)):
    fDist = min(maxDist,(fDist+1)*(f!=0))
    forward.append(fDist)
    bDist = min(maxDist,(bDist+1)*(b!=0))
    backward.append(bDist)
backward = backward[::-1]
distZero = [min(f,b) for f,b in zip(forward,backward)]

print("x",x)
print("f",forward)
print("b",backward)
print("d",distZero)

çıktı:

x [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
f [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
b [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
d [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]

0

İlk sezgim dilimleme kullanmak olurdu. X, numpy dizisi yerine normal bir liste olabiliyorsa,

 out = [x[i:].index(0) for i,_ in enumerate(x)]

numpy gerekliyse kullanabilirsiniz

 out = [np.where(x[i:]==0)[0][0] for i,_ in enumerate(x)]

ancak bu daha az verimlidir, çünkü değerin sağındaki tüm sıfır konumları bulursunuz ve sonra sadece ilkini çıkarırsınız. Neredeyse kesinlikle bunu numpy ile yapmanın daha iyi bir yolu.


0

Edit: Üzgünüm, yanlış anladım. Bu size en yakın sıfırlara olan mesafeyi verecektir - solda veya sağda olsun. Ancak d_rightara sonuç olarak kullanabilirsiniz . Bu, sağda sıfır bulunmaması gibi kenar durumunu kapsamaz.

import numpy as np

x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])

# Get the distance to the closest zero from the left:
zeros = x == 0
zero_locations = np.argwhere(x == 0).flatten()
zero_distances = np.diff(np.insert(zero_locations, 0, 0))

temp = x.copy()
temp[~zeros] = 1
temp[zeros] = -(zero_distances-1)
d_left = np.cumsum(temp) - 1

# Get the distance to the closest zero from the right:
zeros = x[::-1] == 0
zero_locations = np.argwhere(x[::-1] == 0).flatten()
zero_distances = np.diff(np.insert(zero_locations, 0, 0))

temp = x.copy()
temp[~zeros] = 1
temp[zeros] = -(zero_distances-1)
d_right = np.cumsum(temp) - 1
d_right = d_right[::-1]

# Get the smallest distance from both sides:
smallest_distances = np.min(np.stack([d_left, d_right]), axis=0)
# np.array([0, 1, 1, 0, 1, 2, 2, 1, 0, 0])
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.