code

numpy 배열에서 NaN 값 보간

codestyles 2021. 1. 10. 17:17
반응형

numpy 배열에서 NaN 값 보간


numpy 배열의 모든 NaN 값을 선형 보간 값으로 바꾸는 빠른 방법이 있습니까?

예를 들면

[1 1 1 nan nan 2 2 nan 0]

로 변환됩니다

[1 1 1 1.3 1.6 2 2  1  0]

NaN의 인덱스와 논리 인덱스를보다 쉽게 ​​처리 할 수 ​​있도록 먼저 간단한 도우미 함수를 정의 해 보겠습니다 .

import numpy as np

def nan_helper(y):
    """Helper to handle indices and logical indices of NaNs.

    Input:
        - y, 1d numpy array with possible NaNs
    Output:
        - nans, logical indices of NaNs
        - index, a function, with signature indices= index(logical_indices),
          to convert logical indices of NaNs to 'equivalent' indices
    Example:
        >>> # linear interpolation of NaNs
        >>> nans, x= nan_helper(y)
        >>> y[nans]= np.interp(x(nans), x(~nans), y[~nans])
    """

    return np.isnan(y), lambda z: z.nonzero()[0]

이제 다음 nan_helper(.)과 같이 사용할 수 있습니다.

>>> y= array([1, 1, 1, NaN, NaN, 2, 2, NaN, 0])
>>>
>>> nans, x= nan_helper(y)
>>> y[nans]= np.interp(x(nans), x(~nans), y[~nans])
>>>
>>> print y.round(2)
[ 1.    1.    1.    1.33  1.67  2.    2.    1.    0.  ]

---
처음에는 다음과 같은 작업을 수행하기 위해 별도의 함수를 지정하는 것이 약간 과잉 인 것처럼 보일 수 있습니다.

>>> nans, x= np.isnan(y), lambda z: z.nonzero()[0]

결국 배당금을 지급합니다.

따라서 NaN 관련 데이터로 작업 할 때마다 필요한 모든 (새 NaN 관련) 기능을 특정 도우미 함수 아래에 캡슐화하십시오. 코드베이스는 쉽게 이해할 수있는 관용구를 따르기 때문에 더 일관되고 가독성이 좋습니다.

실제로 보간은 NaN 처리가 수행되는 방법을 볼 수있는 좋은 컨텍스트이지만 다른 다양한 컨텍스트에서도 유사한 기술이 활용됩니다.


이 코드를 생각해 냈습니다.

import numpy as np
nan = np.nan

A = np.array([1, nan, nan, 2, 2, nan, 0])

ok = -np.isnan(A)
xp = ok.ravel().nonzero()[0]
fp = A[-np.isnan(A)]
x  = np.isnan(A).ravel().nonzero()[0]

A[np.isnan(A)] = np.interp(x, xp, fp)

print A

그것은 인쇄

 [ 1.          1.33333333  1.66666667  2.          2.          1.          0.        ]

numpy logical과 where 문을 사용하여 1D 보간을 적용하십시오.

import numpy as np
from scipy import interpolate

def fill_nan(A):
    '''
    interpolate to fill nan values
    '''
    inds = np.arange(A.shape[0])
    good = np.where(np.isfinite(A))
    f = interpolate.interp1d(inds[good], A[good],bounds_error=False)
    B = np.where(np.isfinite(A),A,f(inds))
    return B

처음에 데이터가 생성되는 방식을 변경하는 것이 더 쉬울 수 있지만 그렇지 않은 경우 :

bad_indexes = np.isnan(data)

nan이 어디에 있는지 나타내는 부울 배열을 만듭니다.

good_indexes = np.logical_not(bad_indexes)

좋은 값 영역을 나타내는 부울 배열 만들기

good_data = data[good_indexes]

nans를 제외한 원본 데이터의 제한된 버전

interpolated = np.interp(bad_indexes.nonzero(), good_indexes.nonzero(), good_data)

보간을 통해 모든 잘못된 인덱스 실행

data[bad_indexes] = interpolated

원래 데이터를 보간 된 값으로 바꿉니다.


또는 Winston의 답변을 기반으로

def pad(data):
    bad_indexes = np.isnan(data)
    good_indexes = np.logical_not(bad_indexes)
    good_data = data[good_indexes]
    interpolated = np.interp(bad_indexes.nonzero()[0], good_indexes.nonzero()[0], good_data)
    data[bad_indexes] = interpolated
    return data

A = np.array([[1, 20, 300],
              [nan, nan, nan],
              [3, 40, 500]])

A = np.apply_along_axis(pad, 0, A)
print A

결과

[[   1.   20.  300.]
 [   2.   30.  400.]
 [   3.   40.  500.]]

2 차원 데이터의 경우 SciPy가 griddata저에게 상당히 잘 작동합니다.

>>> import numpy as np
>>> from scipy.interpolate import griddata
>>>
>>> # SETUP
>>> a = np.arange(25).reshape((5, 5)).astype(float)
>>> a
array([[  0.,   1.,   2.,   3.,   4.],
       [  5.,   6.,   7.,   8.,   9.],
       [ 10.,  11.,  12.,  13.,  14.],
       [ 15.,  16.,  17.,  18.,  19.],
       [ 20.,  21.,  22.,  23.,  24.]])
>>> a[np.random.randint(2, size=(5, 5)).astype(bool)] = np.NaN
>>> a
array([[ nan,  nan,  nan,   3.,   4.],
       [ nan,   6.,   7.,  nan,  nan],
       [ 10.,  nan,  nan,  13.,  nan],
       [ 15.,  16.,  17.,  nan,  19.],
       [ nan,  nan,  22.,  23.,  nan]])
>>>
>>> # THE INTERPOLATION
>>> x, y = np.indices(a.shape)
>>> interp = np.array(a)
>>> interp[np.isnan(interp)] = griddata(
...     (x[~np.isnan(a)], y[~np.isnan(a)]), # points we know
...     a[~np.isnan(a)],                    # values we know
...     (x[np.isnan(a)], y[np.isnan(a)]))   # points to interpolate
>>> interp
array([[ nan,  nan,  nan,   3.,   4.],
       [ nan,   6.,   7.,   8.,   9.],
       [ 10.,  11.,  12.,  13.,  14.],
       [ 15.,  16.,  17.,  18.,  19.],
       [ nan,  nan,  22.,  23.,  nan]])

I am using it on 3D images, operating on 2D slices (4000 slices of 350x350). The whole operation still takes about an hour :/


I needed an approach that would also fill in NaN's at the start of end of the data, which the main answer does not appear to do.

The function I came up with uses a linear regression to fill in the NaN's. This overcomes my problem:

import numpy as np

def linearly_interpolate_nans(y):
    # Fit a linear regression to the non-nan y values

    # Create X matrix for linreg with an intercept and an index
    X = np.vstack((np.ones(len(y)), np.arange(len(y))))

    # Get the non-NaN values of X and y
    X_fit = X[:, ~np.isnan(y)]
    y_fit = y[~np.isnan(y)].reshape(-1, 1)

    # Estimate the coefficients of the linear regression
    beta = np.linalg.lstsq(X_fit.T, y_fit)[0]

    # Fill in all the nan values using the predicted coefficients
    y.flat[np.isnan(y)] = np.dot(X[:, np.isnan(y)].T, beta)
    return y

Here's an example usage case:

# Make an array according to some linear function
y = np.arange(12) * 1.5 + 10.

# First and last value are NaN
y[0] = np.nan
y[-1] = np.nan

# 30% of other values are NaN
for i in range(len(y)):
    if np.random.rand() > 0.7:
        y[i] = np.nan

# NaN's are filled in!
print (y)
print (linearly_interpolate_nans(y))

Building on the answer by Bryan Woods, I modified his code to also convert lists consisting only of NaN to a list of zeros:

def fill_nan(A):
    '''
    interpolate to fill nan values
    '''
    inds = np.arange(A.shape[0])
    good = np.where(np.isfinite(A))
    if len(good[0]) == 0:
        return np.nan_to_num(A)
    f = interp1d(inds[good], A[good], bounds_error=False)
    B = np.where(np.isfinite(A), A, f(inds))
    return B

Simple addition, I hope it will be of use to someone.


Slightly optimized version based on response of BRYAN WOODS. It handles starting and ending values of source data correctly, and it is faster on 25-30% than original version. Also you may use different kinds of interpolations (see scipy.interpolate.interp1d documentations for details).

import numpy as np
from scipy.interpolate import interp1d

def fill_nans_scipy1(padata, pkind='linear'):
"""
Interpolates data to fill nan values

Parameters:
    padata : nd array 
        source data with np.NaN values

Returns:
    nd array 
        resulting data with interpolated values instead of nans
"""
aindexes = np.arange(padata.shape[0])
agood_indexes, = np.where(np.isfinite(padata))
f = interp1d(agood_indexes
           , padata[agood_indexes]
           , bounds_error=False
           , copy=False
           , fill_value="extrapolate"
           , kind=pkind)
return f(aindexes)

ReferenceURL : https://stackoverflow.com/questions/6518811/interpolate-nan-values-in-a-numpy-array

반응형