2017-05-04 3 views
2

Numpy를 사용하여 몇 줄로 쉽게 풀 수 있다고 생각되는 것에 집착합니다. 의 일부 누락 된 값을 포함하는 예를 배열을 정의 할 수 있습니다 : 내가 무엇을 찾고Numpy를 사용하여 행에서 가장 가까운 이전의 유한 수까지의 거리를 구하는 방법

import numpy as np 
input_data = np.array([[1,3,5,8,6],[3,np.nan,np.nan,5,6],[np.nan,6,7,np.nan,2]]) 

Out[530]: [[1, 3, 5, 8, 6], [3, nan, nan, 5, 6], [nan, 6, 7, nan, 2]] 

은 각 요소에 대해 각 행의 이전 유효한 값까지의 거리를 나에게주는 배열을 얻을 수 있습니다. 위의 예에서이 될 것 같은 것을 : (이 정의하는 더 나은 방법이 있는지 확실하지 않습니다)에는 이전 값이 없기 때문에 각 행의 첫 번째 요소는 항상 NaN이 될 것

delta_valid = [[nan, 1, 1, 1, 1], [nan, 1, 2, 3, 1], [nan, nan, 1, 1, 2]] 

.

누가 Numpy에서이 결과를 얻을 수 있도록 도와 줄 수 있습니까? 고마워요!

답변

1

기본적으로 (1,2,3,...)의 범위를 다음 non-NaN까지 만듭니다. 아래 그림과 같이 이러한 경우를 해결하기 위해, 우리는 각 행에 대한 몇 가지 diff + cumsum 마법을 사용할 수 있습니다 -

def closest_distance_per_row(a): 
    m0 = np.ones(a.shape,dtype=int) 
    mask = ~np.isnan(a) 
    for i,item in enumerate(a): 
     idx = np.flatnonzero(mask[i]) 
     if len(idx)>0: 
      m0[i,:idx[0]] = 0 
      m0[i,idx[1:]] = idx[:-1] - idx[1:] +1 

    out = np.full(a.shape,np.nan,dtype=float) 
    out[:,1:] = m0[:,:-1].cumsum(1) 
    out[out==0] = np.nan 
    out[~mask.any(1)] = np.nan 
    return out 

샘플 실행 -

In [353]: a 
Out[353]: 
array([[ 1., 3., 5., 8., 6.], 
     [ 3., nan, nan, 5., 6.], 
     [ nan, 6., 7., nan, 2.]]) 

In [354]: closest_distance_per_row(a) 
Out[354]: 
array([[ nan, 1., 1., 1., 1.], 
     [ nan, 1., 2., 3., 1.], 
     [ nan, nan, 1., 1., 2.]]) 

In [343]: a 
Out[343]: 
array([[ nan, nan, nan, nan, nan, nan, 4., nan, 3., 1.], 
     [ nan, nan, 6., nan, nan, nan, nan, nan, nan, nan], 
     [ 0., nan, 2., nan, 1., nan, 0., nan, nan, nan], 
     [ 3., nan, 2., nan, 8., 6., nan, 4., 2., nan], 
     [ nan, 0., nan, nan, nan, nan, nan, nan, nan, nan], 
     [ nan, nan, 2., nan, 0., nan, nan, 1., nan, nan]]) 

In [344]: closest_distance_per_row(a) 
Out[344]: 
array([[ nan, nan, nan, nan, nan, nan, nan, 1., 2., 1.], 
     [ nan, nan, nan, 1., 2., 3., 4., 5., 6., 7.], 
     [ nan, 1., 2., 1., 2., 1., 2., 1., 2., 3.], 
     [ nan, 1., 2., 1., 2., 1., 1., 2., 1., 1.], 
     [ nan, nan, 1., 2., 3., 4., 5., 6., 7., 8.], 
     [ nan, nan, nan, 1., 2., 1., 2., 3., 1., 2.]]) 

런타임 테스트 -

In [4]: a = np.random.randint(0,9,(5000,5000)).astype(float) 

In [5]: a.ravel()[np.random.choice(a.size, int(a.size*0.5), replace=0)] = np.nan 

In [6]: %timeit two_loops(a) 
1 loops, best of 3: 16.7 s per loop 

In [7]: %timeit closest_distance_per_row(a) 
1 loops, best of 3: 339 ms per loop 

In [8]: 16700/339.0 # Speedup with one loop (proposed in this post) over two loops 
Out[8]: 49.26253687315634 
+0

당신이 더 빠르지는 않겠지 만, 목록을 작성한 다음 변환하는 대신에'distance' 함수에 배열을 미리 할당하여 꽤 많이 내 솔루션의 런타임을 향상시킬 수 있습니다 배열로. – JohanL

+0

@JohanL 코드를 업데이트하려는 경우 타이밍을 업데이트 해 주어서 기쁩니다. 너 한테 알려줘. – Divakar

+0

그레이트 솔루션! 두 가지 해결책이 모두 포함되어 있으므로 최선의 답변으로 표시하겠습니다. @ JohanL, 만약 당신의 업데이 트를 얻을 것이라고, 나는이 대답뿐만 아니라 편집 할 수있을 것 같아요. 둘 다 감사합니다! – Kristof

1

다음은 문제를 해결하는 방법입니다. 내가 즉각적인 문제 해결 적어도 더지도 및/또는 지능형리스트 공상하지만 뭔가 할 수있을로, 최적이 아닐 수 있습니다 또한에만 현재 2 차원 배열 작동

import numpy as np 
input_data = np.array([[1,3,5,8,6],[3,np.nan,np.nan,5,6],[np.nan,6,7,np.nan,2]]) 

def distance(vector): 
    dist = np.nan 
    dists = [] 
    for a in vector: 
     dists.append(dist) 
     dist = dist + 1 if np.isnan(a) else 1 
    return np.array(dists) 

dists = np.empty(input_data.shape) 
for row_num, row in enumerate(input_data): 
    dists[row_num, :] = distance(row) 

을하지만, 아마 꽤 쉽게 일반화 될 수 있습니다.

또한 위 코드는 최적화되지 않았습니다. 허용 대답에보다 공정한 비교를 위해, 여기에 별도의 함수 호출로,보다 최적화 된 버전을 제공, 또는 목록 빌드 :

def two_loops(input_data): 
    dists = np.empty(input_data.shape) 
    for row_num, row in enumerate(input_data): 
     dist = np.nan 
     for col_num, value in enumerate(row): 
      dists[row_num, col_num] = dist 
      dist = dist + 1 if np.isnan(value) else 1 
    return dists 

이것은 실행 시간이 더 비슷합니다. 측정 할 때 내 솔루션을 실행하는 데 약 두 배의 시간이 걸립니다.

+0

우수함! 이것은 실제로 작동합니다! 나는 크기가 훨씬 큰 실제 데이터로 시도 할 것이고, 속도가 너무 큰 문제는 아니기를 바랍니다. 다른 사람이 답을 찾기 위해 for 루프를 필터링 할 수 있는지 보도록하겠습니다. 그때까지, 당신의 대답을 받아 기쁘게, 고마워! – Kristof