插值缺失的2D Python数值

18

我有一个二维数组(或矩阵,如果您喜欢),其中一些缺失值用NaN表示。 缺失的值通常沿着一个轴形成条带,例如:

1   2   3 NaN   5
2   3   4 Nan   6
3   4 Nan Nan   7
4   5 Nan Nan   8
5   6   7   8   9

我希望将NaN替换为合理的数字。

我研究了Delaunay三角剖分,但文档很少。

我尝试使用astropy中的卷积,因为它支持2d数组的使用,而且非常简单。 问题在于卷积不是插值,它会将所有值都移向平均值(可以通过使用窄核来减轻这种情况)。

这个问题应该是这篇文章的自然二维扩展。有没有一种方法可以在2d数组上插值/处理NaN/缺失值?


有很多方法可以进行插值。一个困难是你的数据不再是矩形的,而许多简单的二维插值算法需要这个条件,但仍然是可行的。你对插值有特殊要求吗? - Jeremy West
例如,这个http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.interp2d.html可能是你需要的。只需传入不是NaN的数据点,然后在构建插值后重新采样NaN。 - Jeremy West
1
此外,这个问题:https://dev59.com/J2435IYBdhLWcg3w50j4 似乎基本上是相同的。 - Jeremy West
@JeremyWest 非常感谢您提供的链接,我认为griddata接近我所寻找的内容。 - M.T
1
比下面的答案更好的方法是使用修复技术。例如,可以参考:https://docs.opencv.org/3.4/df/d3d/tutorial_py_inpainting.html - Cris Luengo
3个回答

30

你可以使用scipy.interpolate.griddata和掩码数组,你可以使用参数method选择喜欢的插值类型,通常为'cubic',会做得非常好:

import numpy as np
from scipy import interpolate


#Let's create some random  data
array = np.random.random_integers(0,10,(10,10)).astype(float)
#values grater then 7 goes to np.nan
array[array>7] = np.nan

使用plt.imshow(array, interpolation='nearest')可以得到如下类似的结果:

enter image description here

x = np.arange(0, array.shape[1])
y = np.arange(0, array.shape[0])
#mask invalid values
array = np.ma.masked_invalid(array)
xx, yy = np.meshgrid(x, y)
#get only the valid values
x1 = xx[~array.mask]
y1 = yy[~array.mask]
newarr = array[~array.mask]

GD1 = interpolate.griddata((x1, y1), newarr.ravel(),
                          (xx, yy),
                             method='cubic')

这是最终结果:

enter image description here

如果NaN值在边缘并被NaN值包围,则它们无法进行插值并将保持为nan。您可以使用fill_value参数更改它。

如果有一个3x3的NaN值区域,中间点能得到合理的数据吗?

这取决于您的数据类型,您需要进行一些测试。例如,您可以故意屏蔽一些良好的数据,尝试不同种类的插值,如立方体、线性等,并使用具有屏蔽值的数组计算插值值与先前屏蔽的原始值之间的差异,看看哪种方法返回最小差异。

您可以使用类似以下的代码:

reference = array[3:6,3:6].copy()
array[3:6,3:6] = np.nan
method = ['linear', 'nearest', 'cubic']

for i in method:
    GD1 = interpolate.griddata((x1, y1), newarr.ravel(),
                              (xx, yy),
                                 method=i)
    meandifference = np.mean(np.abs(reference - GD1[3:6,3:6]))
    print ' %s interpolation difference: %s' %(i,meandifference )

那就变成了这样:

   linear interpolation difference: 4.88888888889
   nearest interpolation difference: 4.11111111111
   cubic interpolation difference: 5.99400137377

当然,这是随机数,结果可能会有很大差异。因此,最好的方法是在数据集的“有目的掩码”部分进行测试,看看会发生什么。


1
如果存在一个 3x3 的NaN值区域,那么中心点的数据是否可靠? - M.T
@M.T 你好,我已经编辑了答案来回答这个问题。 - G M

8

为了方便起见,这里提供了一个实现G M的回答的函数。

from scipy import interpolate
import numpy as np

def interpolate_missing_pixels(
        image: np.ndarray,
        mask: np.ndarray,
        method: str = 'nearest',
        fill_value: int = 0
):
    """
    :param image: a 2D image
    :param mask: a 2D boolean image, True indicates missing values
    :param method: interpolation method, one of
        'nearest', 'linear', 'cubic'.
    :param fill_value: which value to use for filling up data outside the
        convex hull of known pixel values.
        Default is 0, Has no effect for 'nearest'.
    :return: the image with missing values interpolated
    """
    from scipy import interpolate

    h, w = image.shape[:2]
    xx, yy = np.meshgrid(np.arange(w), np.arange(h))

    known_x = xx[~mask]
    known_y = yy[~mask]
    known_v = image[~mask]
    missing_x = xx[mask]
    missing_y = yy[mask]

    interp_values = interpolate.griddata(
        (known_x, known_y), known_v, (missing_x, missing_y),
        method=method, fill_value=fill_value
    )

    interp_image = image.copy()
    interp_image[missing_y, missing_x] = interp_values

    return interp_image

-3

实际上,我会手动逐行浏览此矩阵,并在遇到NaN列表时记录NaN前后的数字及回到普通数字之前所见的NaN数量。一旦找到这些数字,就可以自己使用插值值覆盖NaN。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接