Вы можете попытаться решить вашу проблему как:
# main ideas described in very high level pseudo code
choose suitable base kernel shape and type (gaussian?)
while true
loop over your array (moving average manner)
adapt your base kernel to current sparsity pattern
set current value based on adapted kernel
break if converged
На самом деле это может быть реализовано довольно простым способом (особенно если производительность не является главной задачей).
Очевидно, что это всего лишь эвристика, и вам нужно провести несколько экспериментов с вашими фактическими данными, чтобы найти правильную схему адаптации. Рассматривая адаптацию ядра как перевес ядра, вы можете сделать это на основе того, как были распространены значения. Например, ваши веса для оригинальных опор равны 1, и они уменьшаются в зависимости от того, на какой итерации они возникли.
Кроме того, определение того, когда этот процесс фактически сходится, может быть сложным. В зависимости от приложения может оказаться целесообразным в конце концов оставить некоторые «области разрыва» «незаполненными».
Обновление : Вот очень простая реализация в том же духе *), описанная выше:
from numpy import any, asarray as asa, isnan, NaN, ones, seterr
from numpy.lib.stride_tricks import as_strided as ast
from scipy.stats import nanmean
def _a2t(a):
"""Array to tuple."""
return tuple(a.tolist())
def _view(D, shape, strides):
"""View of flattened neighbourhood of D."""
V= ast(D, shape= shape, strides= strides)
return V.reshape(V.shape[:len(D.shape)]+ (-1,))
def filler(A, n_shape, n_iter= 49):
"""Fill in NaNs from mean calculated from neighbour."""
# boundary conditions
D= NaN* ones(_a2t(asa(A.shape)+ asa(n_shape)- 1), dtype= A.dtype)
slc= tuple([slice(n/ 2, -(n/ 2)) for n in n_shape])
D[slc]= A
# neighbourhood
shape= _a2t(asa(D.shape)- asa(n_shape)+ 1)+ n_shape
strides= D.strides* 2
# iterate until no NaNs, but not more than n iterations
old= seterr(invalid= 'ignore')
for k in xrange(n_iter):
M= isnan(D[slc])
if not any(M): break
D[slc][M]= nanmean(_view(D, shape, strides), -1)[M]
seterr(**old)
A[:]= D[slc]
И простая демонстрация действия filler(.)
будет выглядеть примерно так:
In []: x= ones((3, 6, 99))
In []: x.sum(-1)
Out[]:
array([[ 99., 99., 99., 99., 99., 99.],
[ 99., 99., 99., 99., 99., 99.],
[ 99., 99., 99., 99., 99., 99.]])
In []: x= NaN* x
In []: x[1, 2, 3]= 1
In []: x.sum(-1)
Out[]:
array([[ nan, nan, nan, nan, nan, nan],
[ nan, nan, nan, nan, nan, nan],
[ nan, nan, nan, nan, nan, nan]])
In []: filler(x, (3, 3, 5))
In []: x.sum(-1)
Out[]:
array([[ 99., 99., 99., 99., 99., 99.],
[ 99., 99., 99., 99., 99., 99.],
[ 99., 99., 99., 99., 99., 99.]])
*) Итак, здесь nanmean(.)
используется только для демонстрации идеи процесса адаптации. Основываясь на этой демонстрации, должно быть довольно просто реализовать более сложную схему адаптации и разложения весов. Также обратите внимание, что фактическому исполнению не уделяется внимания, но оно все равно должно быть хорошим (с разумными формами ввода).