8 ответов:
Давайте сначала определим простую вспомогательную функцию, чтобы сделать ее более простой для обработки индексов и логических индексов NaNs :
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]В конечном счете это принесет дивиденды.
Поэтому всякий раз, когда вы работаете с данными, связанными с NaNs, просто инкапсулируйте все (новая связанная с нанотехнологиями) функциональность, необходимая под некоторыми специфическими вспомогательными функциями. Ваша кодовая база будет более связной и читаемой, потому что она следует легко понятным идиомам.
Интерполяция, действительно, является хорошим контекстом, чтобы увидеть, как выполняется обработка 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 и там, где оператор для применения 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
Или основываясь на ответе Уинстона
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.]]
Для двумерных данных
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]])Я использую его на 3D-изображениях, работая на 2D-срезах (4000 срезов 350x350). Вся операция по-прежнему занимает около часа : /
Основываясь на ответе Брайана Вудса , я модифицировал его код, чтобы также преобразовать списки, состоящие только из
NaN, в список нулей: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Простое дополнение, я надеюсь, что оно будет кому-то полезно.
Мне нужен был подход, который также заполнял бы NaN в начале конца данных, что, по-видимому, не делает основной ответ.
Функция, которую я придумал, использует линейную регрессию для заполнения NaN. это решает мою проблему:
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Вот пример использования:
# 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)
Comments