Заполнение пробелов в массиве numpy

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

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

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


person Jose    schedule 05.04.2011    source источник
comment
Это очень актуальный пост: stackoverflow.com/questions/3104781/   -  person Jose    schedule 05.04.2011


Ответы (5)


Вы можете настроить алгоритм в стиле роста кристаллов, попеременно смещая вид по каждой оси, заменяя только те данные, которые отмечены знаком 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.

введите описание изображения здесь

person Paul    schedule 05.04.2011
comment
Хороший! Вопрос (или два): не так очевидно (по крайней мере, для меня), как обобщить это решение для арбитража измерений (1D, 2D, ...). Но в любом случае вы, кажется, можете атаковать (довольно хорошо) граничные условия изнутри, но знаете ли вы о каких-либо побочных эффектах, связанных с этим подходом? Хотите подробнее рассказать об этих проблемах? Спасибо - person eat; 06.04.2011
comment
@eat: этот является обобщенным для ND. Просто измените shape на (10,10,10,10,10) и посмотрите. Я не понимаю вашего второго вопроса. Не могли бы вы переформулировать это иначе? - person Paul; 06.04.2011
comment
Да, немного поработав, я (вроде) выяснил, как это можно обобщить на ND. В любом случае, насчет границ, я просто имел в виду, что (с вашим решением) кажется, что вам вообще не нужно указывать их явно (правильно?). Теперь, когда мы, кажется, обладаем некоторой сущностью, связанной с вопросом ОП, было бы неплохо сравнить, как они работают в некоторых выбранных случаях? Возможно, в другой ветке, чтобы иметь возможность полностью сосредоточиться на теме. Спасибо - person eat; 07.04.2011
comment
@eat: Нет, границы между заполненными значениями явно не определены. Конечно, я буду рад обсудить это дальше. Я не знаю хороших форумов, на которых можно было бы сотрудничать. SO, вероятно, не хуже любого другого места (если вы публикуете его в форме вопроса!). Я разместил код на pastie.org pastie.org/pastes/1766087 Мне было бы любопытно, какие настройки потребовались, чтобы сделать его общедоступным. Я думал, это уже было. - person Paul; 07.04.2011

Используя 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

результат: введите описание изображения здесь

person Juh_    schedule 13.02.2012
comment
Вау, я не знал distance_transform_edt. Это очень полезная функция. - person Jose; 27.02.2012

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

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

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

person opit    schedule 30.04.2015

Вы можете попробовать решить свою проблему следующим образом:

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

person eat    schedule 05.04.2011

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

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

person Simon Bergot    schedule 05.04.2011
comment
Это перебор для моих требований. Мне просто нужно заполнить некоторые пробелы в данных с помощью простого стабильного механизма интерполяции. - person Jose; 05.04.2011