最强总结!十大数据插值法 !!

文摘   2024-11-07 11:26   北京  

哈喽,我是小白~

今儿和大家聊一个非常重要的话题:数据插值法

在机器学习实验中,数据插值方法可以有效处理缺失数据,帮助模型更好地利用现有信息。合理的插值方法可以保持数据分布的完整性,避免引入偏差或误差。数据插值还能提高模型的泛化能力,使其在面对不完整数据时表现更稳定。

涉及到最重要的十种方法有:

  • 线性插值
  • 多项式插值
  • 样条插值
  • 拉格朗日插值
  • 牛顿插值
  • 最近邻插值
  • 分段常数插值
  • 高斯过程回归插值
  • 克里金插值
  • 双线性/三线性插值

如果需要 本文PDF版本 的同学,文末获取~

另外,文末有总结性的干货~

一起来看下具体细化内容~

1. 线性插值

线性插值是最基本的一种插值方法,它假设两个已知数据点之间的关系是线性的,因此可以通过直线来估算未知点的值。在线性插值中,插值函数就是连接两个点的直线。

核心公式

假设我们有两个数据点  和 ,并且我们需要在这两个点之间找到  位置的值 

这个公式表示,在  和  之间, 按照线性方式变化。

  1. 设  是通过两点的线性函数:
  1. 将已知点  和  代入:
  1. 联立方程解得  和 
  1. 最终插值公式为:

这就是线性插值的推导过程。

Python实现

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d

# 生成带有缺失值的虚拟数据集
np.random.seed(0)
x = np.linspace(01020)
y = np.sin(x) + 0.5 * np.random.normal(size=len(x))

# 随机选择一部分数据置为空以模拟缺失
mask = np.random.choice([TrueFalse], size=y.shape, p=[0.30.7])
y_missing = y.copy()
y_missing[mask] = np.nan  # 将随机部分置为NaN模拟缺失值

# 使用线性插值填补缺失值
x_valid = x[~mask]  # 有效的x值(非NaN)
y_valid = y[~mask]  # 有效的y值(非NaN)

# 生成插值函数
linear_interp_func = interp1d(x_valid, y_valid, kind='linear', fill_value="extrapolate")
y_interpolated = linear_interp_func(x)  # 生成插值数据

# 计算插值误差(仅对原始非缺失位置)
error = np.abs(y - y_interpolated)

# 绘制图形
plt.figure(figsize=(128))

# 图1:原始数据和插值数据对比
plt.subplot(311)
plt.plot(x, y, 'o-', color="royalblue", label='Original Data', alpha=0.6)
plt.plot(x, y_missing, 'o', color="tomato", label='Data with Missing Values', alpha=0.5)
plt.plot(x, y_interpolated, 'o--', color="forestgreen", label='Interpolated Data', alpha=0.7)
plt.title('Original Data, Missing Data, and Interpolated Data')
plt.legend()

# 图2:插值误差图
plt.subplot(312)
plt.plot(x, error, 's-', color="purple", markerfacecolor="yellow", label='Interpolation Error')
plt.title('Interpolation Error')
plt.xlabel('X')
plt.ylabel('Error')
plt.legend()

# 图3:插值数据的趋势
plt.subplot(313)
plt.plot(x, y_interpolated, 'o-', color="darkorange", label='Interpolated Data')
plt.title('Trend of Interpolated Data')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()

plt.tight_layout()
plt.show()

  1. 原始数据与插值数据对比图: 这张图通过三种颜色区分出原始数据、包含缺失值的数据以及插值后的数据,让我们一眼就能看到插值效果以及原始数据的变化情况。
  2. 插值误差图:插值的误差图帮助我们了解插值后数据与原始数据的偏差,尤其是缺失值较多时,观察插值是否引入了较大的误差。
  3. 插值数据的趋势图:趋势图展示了插值后数据的整体形态。可以观察到插值后数据的趋势与原数据是否一致,判断插值是否合理。

2. 多项式插值

多项式插值通过一个多项式来拟合给定的  个数据点。多项式的次数是 ,因此插值函数是一个  次多项式,能够通过所有的已知点。

核心公式

给定  个数据点 ,我们需要找到一个  多项式,使得:

多项式的形式为:

其中  是需要通过已知数据点来求解的系数。

  1. 设插值多项式为:
  1. 将每个已知点代入方程:

以此类推,构成一个包含  个方程的线性方程组:

  1. 解线性方程组得到系数 ,从而得到多项式 

Python实现

import numpy as np
import matplotlib.pyplot as plt
from numpy.polynomial import Polynomial

# 生成虚拟数据集
np.random.seed(42)
x = np.linspace(-5510)  # 原始数据的x坐标
y = np.sin(x) + np.random.normal(00.1, len(x))  # 带噪声的y数据

# 生成高精度的插值点,用于绘制插值曲线
x_interp = np.linspace(-55500)  # 插值后高分辨率的x坐标

# 多项式插值
degree = 9  # 多项式的阶数
coefficients = np.polyfit(x, y, degree)  # 拟合多项式的系数
poly = np.poly1d(coefficients)  # 创建多项式函数
y_interp = poly(x_interp)  # 插值后的y数据

# 计算误差:真实函数和插值函数之间的差异
true_function = np.sin(x_interp)
error = true_function - y_interp

# 创建图像
plt.figure(figsize=(126))

# 图1:数据点和插值曲线
plt.subplot(121)
plt.scatter(x, y, color='red', label='Data points')  # 原始数据点
plt.plot(x_interp, y_interp, color='blue', label=f'Polynomial (degree {degree})')  # 插值曲线
plt.plot(x_interp, true_function, color='green', linestyle='--', label='True function')  # 真实函数
plt.title('Polynomial Interpolation')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True)

# 图2:插值误差曲线
plt.subplot(122)
plt.plot(x_interp, error, color='purple', label='Error')
plt.axhline(0, color='black', linestyle='--')  # y=0的参考线
plt.title('Interpolation Error')
plt.xlabel('x')
plt.ylabel('Error')
plt.legend()
plt.grid(True)

# 显示图像
plt.tight_layout()
plt.show()
  1. 虚拟数据集生成:通过 np.linspace 生成原始的 x 数据,使用 np.sin 函数作为目标函数,并加入一定的噪声,模拟真实数据。
  2. 多项式插值:通过 np.polyfit 进行多项式插值,生成一个阶数为 9 的多项式,并通过 np.poly1d创建插值函数。
  3. 高精度插值点:使用 x_interp 提高插值点的密度,以得到更平滑的插值曲线。
  4. 误差分析:插值后的结果与真实函数值作对比,计算误差并绘制误差曲线。
  5. 可视化图形:创建了两个子图,分别展示了插值曲线和误差曲线,颜色使用鲜艳的红、蓝、绿和紫来区分数据点、插值结果、真实函数和误差。

  • 图1(Polynomial Interpolation):展示了多项式拟合的效果,红色表示原始数据点,蓝色曲线是拟合的多项式,绿色虚线是真实的 sin 函数。通过对比可以看到拟合曲线在数据点附近的表现。
  • 图2(Interpolation Error):展示了多项式插值的误差。误差曲线帮助我们理解在不同区域中插值函数的准确性。

3. 样条插值

样条插值是一种通过低阶多项式(通常是三次多项式)构造的插值方法。每一段小区间内使用一个三次多项式,而在各个区间的连接处保证函数值和导数的连续性,从而实现平滑插值。

核心公式

假设给定  个数据点 ,三次样条函数在每个区间  上表示为:

在每个区间内,样条函数是三次的,具有四个未知系数 

  1. 对每个区间  采用三次多项式拟合。
  • 在节点  和  处,要求  和 
  1. 保证各区间的连续性:
  • 一阶导数连续:
  • 二阶导数连续:
  1. 边界条件:常见的边界条件有:
  • 自然边界条件: 和 
  • 第一类边界条件:给定边界点的导数值。
  1. 将这些条件代入,形成一个关于  的线性方程组,解得系数。

Python实现

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import CubicSpline

# 生成不规则的温度数据
np.random.seed(42)  # 固定随机种子
time_points = np.sort(np.random.choice(np.linspace(02450), 12, replace=False))  # 随机12个时间点,范围0到24小时
temperature_points = np.sin(time_points * np.pi / 12) * 10 + np.random.normal(01, len(time_points))  # 温度数据模拟

# 使用样条插值进行数据补全
cs = CubicSpline(time_points, temperature_points)  # 创建样条插值对象

# 生成插值点
time_fine = np.linspace(024200)
temperature_fine = cs(time_fine)
temperature_derivative2 = cs(time_fine, 2)  # 计算二阶导数

# 设置画布和子图布局
fig, (ax1, ax2) = plt.subplots(21, figsize=(1010), sharex=True)

# 图形1:原始数据点与样条插值曲线
ax1.plot(time_fine, temperature_fine, label="Cubic Spline Interpolation", color="blue", linewidth=2)
ax1.scatter(time_points, temperature_points, color="red", label="Original Data Points", s=50)
ax1.set_title("Temperature over Time with Cubic Spline Interpolation")
ax1.set_ylabel("Temperature (°C)")
ax1.legend()
ax1.grid(True)

# 图形2:样条插值后温度的二阶导数(加速度)
ax2.plot(time_fine, temperature_derivative2, label="Second Derivative (Acceleration)", color="green", linewidth=2)
ax2.set_title("Second Derivative of Temperature (Acceleration)")
ax2.set_xlabel("Time (hours)")
ax2.set_ylabel("Acceleration of Temperature Change")
ax2.legend()
ax2.grid(True)

plt.tight_layout()
plt.show()
  1. 生成数据:我们随机生成12个时间点的温度数据,使用时间点与正弦函数相乘并加上小的随机噪声,模拟不规则的温度数据。
  2. 样条插值:我们使用CubicSpline来生成样条插值对象,并在0到24小时的范围内对时间点进行插值。

插值图和二阶导数图:分别绘制了插值曲线和二阶导数曲线,这些图形帮助分析温度的平滑趋势和波动加速度。

4. 拉格朗日插值

拉格朗日插值使用拉格朗日基函数来构建插值多项式。每个基函数在其对应的节点处为 1,而在其他节点处为 0。通过加权这些基函数来构建插值多项式。

核心公式

插值多项式  表示为:

其中, 是第  个拉格朗日基函数:

该函数在  处为 1,其他点为 0。

  1. 通过构造基函数 ,确保每个基函数  在  处为 1,而在其他点  处为 0。
  2. 然后插值多项式  是基函数的加权和:

这保证了多项式在每个已知点处准确地插值。

Python实现

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange

# 生成虚拟数据集
# 原始数据点
x = np.array([0123456])
y = np.array([1205342])

# 创建拉格朗日插值多项式
poly = lagrange(x, y)

# 生成更密集的x,用于绘制平滑的插值曲线
x_dense = np.linspace(min(x), max(x), 500)
y_dense = poly(x_dense)

# 生成测试数据,模拟"真实值" (在这个案例中, 假设我们知道真实值)
y_true = np.sin(x_dense) + 2  # 真实值可以人为定义为一个复杂的函数

# 计算插值误差
error = y_true - y_dense

# 绘制插值曲线和误差图

fig, (ax1, ax2) = plt.subplots(21, figsize=(1010), dpi=100)

# 1. 原始数据点与插值曲线图
ax1.plot(x_dense, y_dense, label='Lagrange Interpolation', color='blue', linewidth=2)
ax1.scatter(x, y, color='red', label='Original Data Points', zorder=5)
ax1.plot(x_dense, y_true, label='True Values (sin(x) + 2)', color='green', linestyle='--', linewidth=2)

ax1.set_title('Lagrange Interpolation and Original Data')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.legend()
ax1.grid(True)

# 2. 插值误差图
ax2.plot(x_dense, error, label='Interpolation Error', color='purple', linewidth=2)
ax2.axhline(0, color='black', linestyle='--', linewidth=1)
ax2.set_title('Interpolation Error (True Value - Interpolated Value)')
ax2.set_xlabel('x')
ax2.set_ylabel('Error')
ax2.legend()
ax2.grid(True)

plt.tight_layout()
plt.show()
  1. 数据生成x 和 y 为原始数据点。x_dense 是在 [0, 6] 的区间中生成的更多点,用于绘制平滑的插值曲线。y_true 为模拟的真实数据值(在这个例子中,我们使用了 sin(x) + 2,作为复杂函数的真实值)。
  2. 拉格朗日插值:使用 scipy.interpolate.lagrange 函数生成拉格朗日插值的多项式,并在 x_dense 上计算出插值后的值 y_dense
  3. 误差计算:计算插值值与真实值之间的误差 error = y_true - y_dense,用于绘制误差图。

  1. 拉格朗日插值的曲线与原始数据点:蓝色的平滑曲线是插值结果,红色的点为原始数据点,绿色的虚线是真实值曲线。可以清晰地看到拉格朗日插值的曲线是如何拟合原始数据的。这对于插值方法的效果有直观的展示意义。
  2. 插值误差图:紫色曲线显示了插值误差,误差为插值结果与真实值之间的差值。可以帮助评估拉格朗日插值的准确性及在不同点上的表现。

5. 牛顿插值

牛顿插值利用差商来递归构造插值多项式,插值多项式的构造过程能够动态地增加点,并且无需重新计算整个多项式。牛顿插值的优点是能够在已有多项式的基础上添加新数据点。

核心公式

牛顿插值的多项式可以表示为:

其中, 是  和  之间的差商。

  1. 计算差商:
  • 0阶差商:
  • 1阶差商:
  • 2阶差商: 以此类推,计算更高阶差商。
  1. 构造牛顿插值多项式。

Python实现

import numpy as np
import matplotlib.pyplot as plt

# 生成虚拟数据
np.random.seed(0)
x = np.linspace(01010)  # 原始数据x点
y = np.sin(x) + np.random.normal(00.1, len(x))  # 添加一些噪声的原始y值

# 牛顿插值算法实现
def newton_interpolation(x, y, x_new):
    n = len(x)
    divided_diff = np.zeros((n, n))
    divided_diff[:, 0] = y

    for j in range(1, n):
        for i in range(n - j):
            divided_diff[i, j] = (divided_diff[i + 1, j - 1] - divided_diff[i, j - 1]) / (x[i + j] - x[i])

    # 计算插值值
    y_new = np.zeros_like(x_new)
    for i in range(len(x_new)):
        term = 1
        y_new[i] = divided_diff[00]
        for j in range(1, n):
            term *= (x_new[i] - x[j - 1])
            y_new[i] += divided_diff[0, j] * term

    return y_new

# 插值计算
x_new = np.linspace(010100)  # 新的x点
y_interp = newton_interpolation(x, y, x_new)  # 牛顿插值获得的y值

# 真实的sin(x)曲线
y_true = np.sin(x_new)

# 误差计算
error = y_true - y_interp

# 绘图
plt.figure(figsize=(128))

# 图1:原始数据点与插值曲线
plt.subplot(211)
plt.scatter(x, y, color='red', label='Original Data Points')
plt.plot(x_new, y_interp, color='blue', label='Newton Interpolation Curve')
plt.plot(x_new, y_true, color='green', linestyle='--', label='True sin(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Newton Interpolation vs. True Function')
plt.legend()
plt.grid()

# 图2:误差分析图
plt.subplot(212)
plt.plot(x_new, error, color='purple', label='Interpolation Error')
plt.xlabel('x')
plt.ylabel('Error')
plt.title('Interpolation Error Analysis')
plt.legend()
plt.grid()

# 展示图形
plt.tight_layout()
plt.show()

  1. 原始数据点与插值曲线:在第一个子图中,我们绘制了红色的原始数据点散点图、蓝色的插值曲线以及绿色的真实曲线。这有助于观察牛顿插值的平滑度和数据拟合情况。

  2. 误差分析图:第二个子图展示了插值误差(即插值值与真实值的差异)。通过误差分析,我们可以识别插值的精确度,特别是在数据点较少的情况下,插值的误差可能较大。

6. 最近邻插值

最近邻插值是一种最简单的插值方法,它为每个插值点选择离其最近的已知点作为插值值。这种方法的优点是计算非常简单,但由于其对邻近数据的忽略,插值结果通常会有较大的误差,表现为不平滑。

核心公式

给定一个插值点  和已知的离散数据点 ,最近邻插值选择距离  最近的点  并赋予它  的值。公式为:

其中, 是距离插值点  最近的已知点, 是对应的函数值。

  1. 对于给定的插值点 ,计算其与所有已知点  的距离。
  2. 选择使得距离  最小的点 ,即:
  1. 然后返回对应的值 ,即:

这个方法没有插值公式的推导过程,因为它只是简单地选择最邻近的数据点。

Python实现

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata

# 生成二维数据集
np.random.seed(0)
x = np.random.rand(100) * 10
y = np.random.rand(100) * 10
z = np.sin(x/2) * np.cos(y/2)

# 添加一些NaN值模拟缺失数据
mask = np.random.choice([TrueFalse], size=z.shape, p=[0.10.9])
z[mask] = np.nan

# 定义插值网格
xi = np.linspace(010100)
yi = np.linspace(010100)
xi, yi = np.meshgrid(xi, yi)

# 使用最近邻插值填补缺失值
zi_nn = griddata((x[~mask], y[~mask]), z[~mask], (xi, yi), method='nearest')

# 创建图形窗口
fig, axs = plt.subplots(13, figsize=(186))

# 1. 原始数据分布图(包含NaN)
sc1 = axs[0].scatter(x, y, c=z, cmap='jet', s=50)
axs[0].set_title('Original Data with NaN')
axs[0].set_xlabel('X-axis')
axs[0].set_ylabel('Y-axis')
fig.colorbar(sc1, ax=axs[0])

# 2. 最近邻插值后数据分布图
sc2 = axs[1].contourf(xi, yi, zi_nn, cmap='rainbow', levels=100)
axs[1].set_title('Data after Nearest Neighbor Interpolation')
axs[1].set_xlabel('X-axis')
axs[1].set_ylabel('Y-axis')
fig.colorbar(sc2, ax=axs[1])

# 3. 插值前后差异图
zi_original = griddata((x, y), z, (xi, yi), method='nearest', fill_value=np.nan)
diff = np.abs(zi_nn - zi_original)
sc3 = axs[2].contourf(xi, yi, diff, cmap='plasma', levels=100)
axs[2].set_title('Difference between Original and Interpolated Data')
axs[2].set_xlabel('X-axis')
axs[2].set_ylabel('Y-axis')
fig.colorbar(sc3, ax=axs[2])

# 调整图像布局
plt.tight_layout()
plt.show()
  1. 数据生成:使用np.random生成随机的二维坐标xy,并用z存储对应的函数值。我们使用np.sinnp.cos生成一些有规律的波动数据。
  2. 缺失数据模拟:通过一个掩码mask随机将10%的数据标记为缺失(NaN),模拟现实中不完整的数据集。
  3. 插值处理:使用scipy.interpolate.griddata函数进行最近邻插值,将缺失数据进行填补。插值的结果保存在zi_nn中。

  • 第一幅图展示了原始数据,包含随机的NaN值。让我们看到原始数据中缺失值的位置,并可观察数据分布的特点。
  • 第二幅图展示了经过最近邻插值后的数据,清晰展示了插值后数据的平滑效果及如何填补空缺。
  • 第三幅图直观地显示了插值前后的差异,突出插值后的变化区域,帮助我们理解插值对数据的影响。

7. 分段常数插值

分段常数插值也称为阶梯插值,它将每个区间内的值都定为区间的某个已知点的值。通常,这个已知点可以是左端点、右端点,或者区间内的某个点。插值结果会在各区间之间出现不连续性。

核心公式

如果已知一组数据点 ,对于插值点  位于区间 ,其插值值为:

这种方法直接在每个区间内使用常数值  来表示该区间的插值结果。

  1. 对于给定的插值点 ,找出所在的区间 ,即:
  1. 然后直接返回对应的值 

这就是分段常数插值的方法,不需要复杂的推导过程。

Python实现

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d

# 生成虚拟数据
np.random.seed(42)
time_stamps = np.sort(np.random.choice(range(0100), size=15, replace=False))  # 时间点
temperature_group_1 = np.random.uniform(2030, size=len(time_stamps))  # 区域1温度
temperature_group_2 = np.random.uniform(2535, size=len(time_stamps))  # 区域2温度

# 创建DataFrame
df = pd.DataFrame({
    'Time': time_stamps,
    'Temperature_Group_1': temperature_group_1,
    'Temperature_Group_2': temperature_group_2
})

# 分段常数插值
time_full = np.arange(0100)  # 定义插值后的完整时间范围
f_const_1 = interp1d(df['Time'], df['Temperature_Group_1'], kind='previous', fill_value="extrapolate")
f_const_2 = interp1d(df['Time'], df['Temperature_Group_2'], kind='previous', fill_value="extrapolate")

temperature_interpolated_1 = f_const_1(time_full)
temperature_interpolated_2 = f_const_2(time_full)

# 计算区域平均温度
mean_temp_1 = np.mean(temperature_interpolated_1)
mean_temp_2 = np.mean(temperature_interpolated_2)

# 绘图
plt.figure(figsize=(128))

# 图1:原始数据点
plt.subplot(311)
plt.plot(df['Time'], df['Temperature_Group_1'], 'o-', label="Group 1 - Original", color='blue')
plt.plot(df['Time'], df['Temperature_Group_2'], 'o-', label="Group 2 - Original", color='green')
plt.xlabel("Time")
plt.ylabel("Temperature (°C)")
plt.title("Original Temperature Data Points")
plt.legend()
plt.grid(True)

# 图2:分段常数插值
plt.subplot(312)
plt.step(time_full, temperature_interpolated_1, where='post', label="Group 1 - Step Interpolated", color='blue')
plt.step(time_full, temperature_interpolated_2, where='post', label="Group 2 - Step Interpolated", color='green')
plt.xlabel("Time")
plt.ylabel("Temperature (°C)")
plt.title("Temperature Data with Stepwise Constant Interpolation")
plt.legend()
plt.grid(True)

# 图3:区域平均温度对比
plt.subplot(313)
plt.bar(['Group 1''Group 2'], [mean_temp_1, mean_temp_2], color=['blue''green'])
plt.xlabel("Group")
plt.ylabel("Average Temperature (°C)")
plt.title("Average Temperature Comparison After Interpolation")
plt.grid(True)

# 调整布局和显示
plt.tight_layout()
plt.show()

  1. 原始数据图:展示了区域1和区域2在原始记录时的温度数据。可以看到数据记录时刻是离散的,并不是连续的。
  2. 分段常数插值图:利用分段常数插值填补了温度数据的空缺部分,使得温度随时间变化呈阶梯状,符合分段常数插值的特点。这有助于在不连续的记录间补充数据,适用于需要保持数据原始特征的分析。
  3. 区域平均温度对比图:展示了插值后两个区域的平均温度对比,帮助我们分析两个区域的温度水平是否存在差异。

8. 高斯过程回归插值

高斯过程回归插值使用高斯过程(Gaussian Process, GP)模型进行插值,它假设数据之间存在某种形式的分布,通过协方差函数来描述数据点之间的相似性。该方法将数据点建模为多变量高斯分布,从而在未知点处预测数据的分布。

核心公式

高斯过程回归的预测公式为:

其中 $ \mu(x_ x_  \sigma^2(x_*) $ 是预测点的方差。

预测均值和方差由以下公式给出:

其中:

  •  是已知数据点之间的协方差矩阵。
  •  是预测点与已知数据点之间的协方差向量。
  •  是已知数据点的观测值。

1. 定义高斯过程:假设数据是一个由高斯分布生成的过程,可以用均值和协方差函数来描述。给定输入 ,输出  满足高斯分布:

2. 推导预测分布:根据已知数据点和协方差函数,使用条件概率公式可以得到预测点  的条件分布:

3. 计算预测均值和方差

  • 预测均值 
  • 预测方差 

Python实现

import numpy as np
import matplotlib.pyplot as plt
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C

# 1. 生成虚拟数据
np.random.seed(42)
X = np.sort(5 * np.random.rand(301), axis=0)  # 输入数据
y = np.sin(X).ravel() + np.random.normal(00.2, X.shape[0])  # 真实值 + 噪声

# 测试点
X_test = np.linspace(051000).reshape(-11)

# 2. 配置高斯过程回归模型
# 使用常数核和RBF核的乘积
kernel = C(1.0, (1e-41e1)) * RBF(1.0, (1e-41e1))
gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=10, alpha=0.1)

# 3. 拟合数据
gp.fit(X, y)

# 4. 预测
y_pred, sigma = gp.predict(X_test, return_std=True)

# 5. 计算残差
y_train_pred, _ = gp.predict(X, return_std=True)
residuals = y - y_train_pred

# 6. 绘图
plt.figure(figsize=(128))

# 第一张图:原始数据与预测插值曲线
plt.subplot(211)
plt.plot(X_test, y_pred, 'b-', label='Prediction (mean)', lw=2)  # 预测均值曲线
plt.fill_between(X_test.ravel(), y_pred - 2*sigma, y_pred + 2*sigma, color='lightgray', alpha=0.5, label='Confidence interval (±2σ)')  # 置信区间
plt.scatter(X, y, c='r', s=50, zorder=10, label='Observed data')  # 真实数据点
plt.title('Gaussian Process Regression with Confidence Interval')
plt.xlabel('Input')
plt.ylabel('Output')
plt.legend(loc='upper left')

# 第二张图:残差分析
plt.subplot(212)
plt.scatter(X, residuals, c='purple', s=50, zorder=10)
plt.axhline(y=0, color='k', linestyle='--', lw=2)
plt.title('Residuals Analysis')
plt.xlabel('Input')
plt.ylabel('Residuals (True - Predicted)')

plt.tight_layout()
plt.show()
  1. 数据生成:我们使用 np.random.rand() 生成了一些随机的 X 数据,并且用 np.sin(X) 创建了非线性数据,同时加入了一些噪声。
  2. 高斯过程模型的定义:我们定义了一个常数核 C 和 RBF 核的乘积来作为高斯过程的核函数,RBF 核可以很好地处理非线性数据。通过 GaussianProcessRegressor 进行拟合和预测。
  3. 预测结果:模型输出预测的均值 y_pred 和标准差 sigma,用来绘制插值曲线和置信区间。
  4. 残差计算:通过计算模型在训练集上的预测值和真实值的差异,得到残差,用于残差分析。

  1. 原始数据与预测插值曲线:该图展示了模型的拟合效果,蓝色的预测曲线代表模型的插值结果,红点为原始数据。阴影部分表示模型的不确定性,显示置信区间,帮助理解模型预测的置信度。
  2. 残差分析:通过绘制残差图,可以看到模型在不同输入点的拟合误差情况。如果残差均匀分布,说明模型的拟合较好;如果残差呈现某种系统性的模式,则说明模型可能存在某些偏差或欠拟合问题。

9. 克里金插值

克里金插值是一种基于变异函数(或协方差函数)的方法,广泛应用于地质学、环境科学等领域。克里金插值假设数据是空间相关的,通过最大化加权平均的线性估计来推断未知点的值。

核心公式

克里金插值的预测公式为:

其中, 是克里金权重, 是已知点的观测值。

1. 构建变异函数:克里金插值假设数据之间的相关性可以通过变异函数(或协方差函数)描述,常用的协方差函数如:

其中, 是两个点之间的距离。 2. 设定加权平均:在已知点  上,构建一个加权和:

3. 求解权重:通过最小化误差的平方和,或者通过协方差函数最大化线性估计,来解得克里金权重 

Python实现

import numpy as np
import matplotlib.pyplot as plt
from pykrige.ok import OrdinaryKriging

# 生成虚拟数据集
np.random.seed(42)
num_samples = 30  # 原始观测点的数量
x_observed = np.random.uniform(0100, num_samples)
y_observed = np.random.uniform(0100, num_samples)
z_observed = np.sin(x_observed / 10) * np.cos(y_observed / 10) + np.random.normal(00.1, num_samples)

# 定义插值网格
grid_x = np.linspace(0100200)
grid_y = np.linspace(0100200)
grid_x, grid_y = np.meshgrid(grid_x, grid_y)

# 使用普通克里金插值 (Ordinary Kriging)
OK = OrdinaryKriging(
    x_observed, y_observed, z_observed, 
    variogram_model="spherical"
    verbose=False, enable_plotting=False
)
z_kriging, z_variance = OK.execute("grid", grid_x[0], grid_y[:, 0])

# 创建图形
fig, ax = plt.subplots(13, figsize=(186))

# 原始数据分布图
scatter = ax[0].scatter(x_observed, y_observed, c=z_observed, cmap="coolwarm", s=50, edgecolor='k')
ax[0].set_title("Observed Data Distribution")
ax[0].set_xlabel("X coordinate")
ax[0].set_ylabel("Y coordinate")
fig.colorbar(scatter, ax=ax[0], label="Observed Value")

# 克里金插值结果图
im = ax[1].imshow(z_kriging, extent=(01000100), origin="lower", cmap="coolwarm")
ax[1].set_title("Kriging Interpolation Result")
ax[1].set_xlabel("X coordinate")
ax[1].set_ylabel("Y coordinate")
fig.colorbar(im, ax=ax[1], label="Interpolated Value")

# 插值误差(方差)分布图
im_var = ax[2].imshow(z_variance, extent=(01000100), origin="lower", cmap="YlOrBr")
ax[2].set_title("Interpolation Variance")
ax[2].set_xlabel("X coordinate")
ax[2].set_ylabel("Y coordinate")
fig.colorbar(im_var, ax=ax[2], label="Variance")

# 显示所有图形
plt.tight_layout()
plt.show()

  1. 原始数据分布图:展示采样点的位置和浓度的数值大小。我们使用散点图,设置颜色映射来显示不同的浓度值,以便观察数据的原始分布。该图可以帮助我们直观地理解采样点的空间分布,了解哪些区域浓度较高或较低。

  2. 克里金插值结果图:展示插值后的浓度分布,使用平滑的插值值填充整个区域。该图可以帮助我们观察插值之后整个区域的浓度变化和趋势,展示了克里金插值对于未知区域浓度的预测。

  3. 插值误差(方差)分布图:展示每个插值点的预测方差(误差),颜色越深的区域表示预测的误差越大。该图可以帮助我们识别插值的不确定性,在采样较少的区域误差通常更高。这对于评估插值结果的可靠性非常有用。

10. 双线性/三线性插值

双线性插值和三线性插值是针对二维和三维数据进行插值的方法。双线性插值适用于二维格点数据,它在一个矩形区域内插值;三线性插值则适用于三维数据,用于插值一个立方体内的点。

核心公式

1. 双线性插值公式:对于二维数据,插值点  在矩形区域  内时,双线性插值的公式为:

其中, 和  是标准化的比例因子。

2. 三线性插值公式:对于三维数据,三线性插值公式类似于双线性插值,适用于一个立方体区域内的插值。公式为:

这里  是对应于  坐标的标准化比例因子。

Python实现

案例1:双线性插值

创建一个二维的网格数据(例如温度分布数据),通过在稀疏的网格点上生成数据,采用双线性插值填充中间的未知数据。通过分析数据插值前后的误差和插值效果,来说明其有效性。

案例2:三线性插值

接下来在三维网格(例如体积密度数据)中,生成稀疏的点并使用三线性插值填充三维空间中的数据。这种插值方式可用于体积数据处理,如流体模拟、CT扫描数据重建等。

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata

# 创建虚拟数据
def generate_2d_data():
    x = np.linspace(045)
    y = np.linspace(045)
    X, Y = np.meshgrid(x, y)
    Z = np.sin(X) * np.cos(Y)
    return X, Y, Z

def generate_3d_data():
    x = np.linspace(045)
    y = np.linspace(045)
    z = np.linspace(045)
    X, Y, Z = np.meshgrid(x, y, z)
    V = np.sin(X) * np.cos(Y) * np.sin(Z)
    return X, Y, Z, V

# 进行双线性插值
def bilinear_interpolation(X, Y, Z):
    xi = np.linspace(04100)
    yi = np.linspace(04100)
    XI, YI = np.meshgrid(xi, yi)
    Zi = griddata((X.flatten(), Y.flatten()), Z.flatten(), (XI, YI), method='linear')
    return XI, YI, Zi

# 进行三线性插值
def trilinear_interpolation(X, Y, Z, V):
    xi = np.linspace(0420)
    yi = np.linspace(0420)
    zi = np.linspace(0420)
    XI, YI, ZI = np.meshgrid(xi, yi, zi)
    Vi = griddata((X.flatten(), Y.flatten(), Z.flatten()), V.flatten(), (XI, YI, ZI), method='linear')
    return XI, YI, ZI, Vi

# 绘制双线性插值结果
def plot_bilinear(X, Y, Z, XI, YI, Zi):
    plt.figure(figsize=(108))
    plt.subplot(221)
    plt.pcolormesh(X, Y, Z, shading='auto', cmap='jet')
    plt.title("Original Data (2D)")
    plt.colorbar()

    plt.subplot(222)
    plt.contourf(XI, YI, Zi, levels=15, cmap='jet')
    plt.title("Bilinear Interpolated Data (2D)")
    plt.colorbar()

# 绘制三线性插值结果
def plot_trilinear(X, Y, Z, V, XI, YI, ZI, Vi):
    fig = plt.figure(figsize=(108))

    # 绘制原始数据切片
    ax1 = fig.add_subplot(221, projection='3d')
    slice_z = V[:, :, 2]
    ax1.plot_surface(X[:, :, 2], Y[:, :, 2], slice_z, cmap='jet')
    ax1.set_title('Original Data (Slice z=2)')

    # 绘制插值后的切片
    ax2 = fig.add_subplot(222, projection='3d')
    slice_zi = Vi[:, :, 10]
    ax2.plot_surface(XI[:, :, 10], YI[:, :, 10], slice_zi, cmap='jet')
    ax2.set_title('Trilinear Interpolation (Slice z=10)')

    # 绘制体积可视化
    ax3 = fig.add_subplot(223, projection='3d')
    ax3.scatter(X.flatten(), Y.flatten(), Z.flatten(), c=V.flatten(), cmap='jet')
    ax3.set_title('Original 3D Data Points')

    ax4 = fig.add_subplot(224, projection='3d')
    ax4.scatter(XI.flatten(), YI.flatten(), ZI.flatten(), c=Vi.flatten(), cmap='jet')
    ax4.set_title('Trilinear Interpolated 3D Data')

# 主程序
X2, Y2, Z2 = generate_2d_data()
XI2, YI2, Zi2 = bilinear_interpolation(X2, Y2, Z2)
plot_bilinear(X2, Y2, Z2, XI2, YI2, Zi2)

X3, Y3, Z3, V3 = generate_3d_data()
XI3, YI3, ZI3, Vi3 = trilinear_interpolation(X3, Y3, Z3, V3)
plot_trilinear(X3, Y3, Z3, V3, XI3, YI3, ZI3, Vi3)

plt.tight_layout()
plt.show()

双线性插值(2D)

  1. 数据生成:生成一个二维网格 (X, Y),对应的 Z 值通过 sin(X) * cos(Y) 函数生成虚拟数据。
  2. 插值:使用 scipy.interpolate.griddata 进行双线性插值,把稀疏数据扩展到更密集的网格。

  • 图1:绘制插值前的二维原始数据分布。
  • 图2:插值后的二维数据,展示双线性插值的效果。

三线性插值(3D)

  1. 数据生成:三维网格 (X, Y, Z),其值 V 通过三维函数 sin(X) * cos(Y) * sin(Z) 生成。
  2. 插值:使用 griddata 进行三线性插值,将三维稀疏点数据扩展为更密集的三维数据。

  • 图1:显示原始三维数据在 z=2 切片下的情况。
  • 图2:展示 z=10 切片下的插值数据效果。
  • 图3:三维数据的散点图,展示原始数据点在三维空间的分布。
  • 图4:展示插值后的三维数据散点图,显示插值后填充的三维数据分布。

最后

以上就是今天所有的内容了。
获取本文PDF,点击名片回复「十大算法模型」即可~
 
另外, 我们整理了机器学习算法册子,总共16大块的内容,124个问题的总结点击领取!
如果对你来说比较有用,记得点赞、转发,收藏起来慢慢学习~
下期会有更多干货等着你!~

Python和机器学习初学者
Python和机器学习分享,只写干货,一起学习~
 最新文章