使用高斯函数对 2D 数组中的 NaN 值进行内插

46 阅读2分钟

我们有一个包含一些 NaN 值的 2D 数组。我们希望使用具有数据的那些位置来对这些 NaN 值进行插值(内插)。数组如下所示:

array([[        nan,         nan,         nan,         nan,         nan,                nan,         nan,         nan,         nan,         nan],
       [        nan,         nan,         nan,         nan,         nan,                nan,         nan,         nan,         nan,         nan],
       [        nan,         nan,         nan,         nan,         nan,                nan,         nan,         nan,         nan,         nan],
       [        nan,         nan,         nan,         nan,         nan,                nan,         nan,         nan,         nan,         nan],
       [ 1.        ,  0.        ,  1.        ,  0.        ,  0.25      ,                nan,  0.        ,         nan,         nan,         nan],
       [        nan,  0.        ,         nan,  0.25      ,  0.66666667,         0.25      ,  0.66666667,  0.        ,  1.        ,         nan],
       [ 0.        ,  0.5       ,  0.66666667,  0.8       ,  0.66666667,         0.8       ,  0.5       ,  0.83333333,         nan,         nan],
       [ 0.625     ,  0.5625    ,  0.9       ,  0.8       ,  0.8       ,         0.83333333,  0.57142857,  0.66666667,  0.5       ,         nan],
       [        nan,  1.        ,  0.71428571,  0.85714286,  1.        ,         1.        ,  1.        ,         nan,         nan,         nan],
       [        nan,         nan,         nan,         nan,  1.        ,         1.        ,         nan,         nan,         nan,         nan]])

我们希望通过插值,使远离非 NaN 值的位置逐渐接近 0。

2. 解决方案

有多种可能的方法可以根据想要使用的插值技术来实现。事实上,由于数据周围都是 NaN 值,我们更倾向于将其视为函数平滑而不是插值。如果希望远离 2D 地图上的非 NaN 值时更接近 0,我们建议使用如下方法:

将每个非 NaN 数据点 X[i,j] 视为以 [i,j] 为中心的正态分布,方差=1,缩放比例使其 pdf( [i,j] ) = X[i,j],即 f_ij( [a,b] ) = X[i,j] * exp( -|| [a,b] - [i,j] ||^2/2 )。 对于每个 NaN 数据点 X[a,b],将 X[a,b] 设置为 sum( f_ij( [a,b] ) ),其中求和在所有非 NaN 数据点的 [i,j] 索引上进行。

这样,我们可以得到一个类似“密度估计”的结果,并且可以通过改变方差(我们建议使用 =1)来调整值的“消失速度”。 因此,代码只循环遍历所有 NaN 值,然后在每个 NaN 值上循环遍历所有非 NaN 值并对高斯值求和。 代码如下:

import numpy as np

def inpaint_nans_gaussian(X):
    """
    Inpaint NaN values in a 2D array using a Gaussian function.

    Args:
        X: The 2D array with NaN values.

    Returns:
        The 2D array with NaN values inpainted.
    """

    # Get the indices of the NaN values
    nans = np.array(np.where(np.isnan(X))).T

    # Get the indices of the non-NaN values
    notnans = np.array(np.where(~np.isnan(X))).T

    # Loop over the NaN values
    for p in nans:
        # Sum the Gaussian values for all non-NaN values
        X[p[0], p[1]] = sum(
            X[q[0], q[1]] * np.exp(-(sum((p - q) ** 2)) / 2) for q in notnans
        )

    return X

# Example usage
X = np.array([[np.nan, np.nan, np.nan, np.nan, np.nan],
              [np.nan, np.nan, np.nan, np.nan, np.nan],
              [np.nan, np.nan, np.nan, np.nan, np.nan],
              [np.nan, np.nan, np.nan, np.nan, np.nan],
              [1., 0., 1., 0., 0.25],
              [np.nan, 0., np.nan, 0.25, 0.66666667],
              [0., 0.5, 0.66666667, 0.8, 0.66666667],
              [0.625, 0.5625, 0.9, 0.8, 0.8],
              [np.nan, 1., 0.71428571, 0.85714286, 1.],
              [np.nan, np.nan, np.nan, np.nan, 1.],
              [np.nan, np.nan, np.nan, np.nan, 1.]])

# Inpaint the NaN values
X_inpainted = inpaint_nans_gaussian(X)

# Print the inpainted array
print(X_inpainted)