Заполнение пробелов в массиве NumPy - PullRequest
12 голосов
/ 05 апреля 2011

Я просто хочу, в самых простых терминах, интерполировать 3D-набор данных. Линейная интерполяция, ближайший сосед, все, что было бы достаточно (это запустить некоторый алгоритм, поэтому точной оценки не требуется).

В новых версиях scipy такие вещи, как griddata, были бы полезны, но в настоящее время у меня есть только scipy 0.8. Таким образом, у меня есть массив "куб" (data[:,:,:], (NixNjxNk)) и массив флагов (flags[:,:,:,], True или False) того же размера. Я хочу интерполировать свои данные для элементов данных, где соответствующий элемент флага равен False, используя, например, ближайший действительный пункт данных в данных или некоторую линейную комбинацию точек «рядом».

В наборе данных могут быть большие промежутки как минимум в двух измерениях. Кроме кодирования полноценного алгоритма ближайшего соседа с использованием kdtrees или аналогичного, я не могу найти общий N-мерный интерполятор ближайшего соседа.

Ответы [ 5 ]

28 голосов
/ 13 февраля 2012

Используя scipy.ndimage, ваша проблема может быть решена с помощью интерполяции ближайшего соседа в 2 строки:

from scipy import ndimage as nd

indices = nd.distance_transform_edt(invalid_cell_mask, return_distances=False, return_indices=True)
data = data[tuple(ind)]

Теперь в виде функции:

import numpy as np
from scipy import ndimage as nd

def fill(data, invalid=None):
    """
    Replace the value of invalid 'data' cells (indicated by 'invalid') 
    by the value of the nearest valid data cell

    Input:
        data:    numpy array of any dimension
        invalid: a binary array of same shape as 'data'. 
                 data value are replaced where invalid is True
                 If None (default), use: invalid  = np.isnan(data)

    Output: 
        Return a filled array. 
    """    
    if invalid is None: invalid = np.isnan(data)

    ind = nd.distance_transform_edt(invalid, 
                                    return_distances=False, 
                                    return_indices=True)
    return data[tuple(ind)]

Пример использования:

def test_fill(s,d):
     # s is size of one dimension, d is the number of dimension
    data = np.arange(s**d).reshape((s,)*d)
    seed = np.zeros(data.shape,dtype=bool)
    seed.flat[np.random.randint(0,seed.size,int(data.size/20**d))] = True

    return fill(data,-seed), seed

import matplotlib.pyplot as plt
data,seed  = test_fill(500,2)
data[nd.binary_dilation(seed,iterations=2)] = 0   # draw (dilated) seeds in black
plt.imshow(np.mod(data,42))                       # show cluster

Результат: enter image description here

14 голосов
/ 05 апреля 2011

Вы можете настроить алгоритм в стиле роста кристаллов, смещая вид поочередно вдоль каждой оси, заменяя только данные, помеченные False, но имеющие соседа True. Это дает результат, подобный «ближайшему соседу» (но не на расстоянии Евклида или Манхэттена - я думаю, что это может быть ближайший сосед, если вы подсчитываете пиксели, учитываете все соединительные пиксели с общими углами). Это должно быть достаточно эффективно с NumPy поскольку он повторяется только по осям и итерациям сходимости, а не по маленьким кусочкам данных.

Сырой, быстрый и стабильный. Я думаю, что это то, что вы были после:

import numpy as np
# -- setup --
shape = (10,10,10)
dim = len(shape)
data = np.random.random(shape)
flag = np.zeros(shape, dtype=bool)
t_ct = int(data.size/5)
flag.flat[np.random.randint(0, flag.size, t_ct)] = True
# True flags the data
# -- end setup --

slcs = [slice(None)]*dim

while np.any(~flag): # as long as there are any False's in flag
    for i in range(dim): # do each axis
        # make slices to shift view one element along the axis
        slcs1 = slcs[:]
        slcs2 = slcs[:]
        slcs1[i] = slice(0, -1)
        slcs2[i] = slice(1, None)

        # replace from the right
        repmask = np.logical_and(~flag[slcs1], flag[slcs2])
        data[slcs1][repmask] = data[slcs2][repmask]
        flag[slcs1][repmask] = True

        # replace from the left
        repmask = np.logical_and(~flag[slcs2], flag[slcs1])
        data[slcs2][repmask] = data[slcs1][repmask]
        flag[slcs2][repmask] = True

Для примера приведу визуализацию (2D) зон, заполненных данными, помеченными первоначально помеченными True.

enter image description here

2 голосов
/ 01 мая 2015

Некоторое время назад я написал этот сценарий для моей докторской степени: https://github.com/Technariumas/Inpainting

Пример: http://blog.technariumas.lt/post/117630308826/healing-holes-in-python-arrays

Медленно, но работает.Гауссово ядро ​​- лучший выбор, просто проверьте значения размера / сигмы.

1 голос
/ 05 апреля 2011

Вы можете попытаться решить вашу проблему как:

# 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(.) используется только для демонстрации идеи процесса адаптации. Основываясь на этой демонстрации, должно быть довольно просто реализовать более сложную схему адаптации и разложения весов. Также обратите внимание, что фактическому исполнению не уделяется внимания, но оно все равно должно быть хорошим (с разумными формами ввода).

0 голосов
/ 05 апреля 2011

Может быть, вам нужен алгоритм машинного обучения, например, нейронная сеть или машина опорных векторов.

Вы можете проверить эту страницу, на которой есть несколько ссылок на пакеты SVM для python: http://web.media.mit.edu/~stefie10/technical/pythonml.html

...