### 使用 Python 和 Matplotlib 进行平滑曲线拟合
为了在 Python 中使用 `matplotlib` 对数据点进行平滑曲线拟合,可以考虑以下几种方法:
#### 方法一:基于多项式插值
可以通过 NumPy 的 `polyfit` 函数来拟合一条高次多项式曲线。这种方法适用于简单的平滑需求。
```python
import numpy as np
import matplotlib.pyplot as plt
# 原始数据点
x = np.array([0, 1, 2, 3, 4])
y = np.array([0, 0.8, 0.9, 0.1, -0.8])
# 多项式拟合 (degree=3 表示三次多项式)
z = np.polyfit(x, y, deg=3)
p = np.poly1d(z)
# 绘制原始数据点和平滑曲线
xp = np.linspace(min(x), max(x), 100)
plt.plot(x, y, 'o', label="Data Points") # 数据点
plt.plot(xp, p(xp), '-', label="Polynomial Fit") # 平滑曲线
plt.legend()
plt.show()
```
此方法利用了多项式的灵活性[^1],能够较好地逼近复杂的数据分布。
---
#### 方法二:贝塞尔曲线拟合
如果希望获得更自然的过渡效果,可以尝试使用贝塞尔曲线来进行平滑处理。以下是基于二阶贝塞尔曲线的一个例子[^2]:
```python
from scipy.special import comb
import numpy as np
import matplotlib.pyplot as plt
def bernstein_poly(i, n, t):
"""计算第 i 个 Bernstein 基函数"""
return comb(n, i) * (t ** i) * ((1 - t) ** (n - i))
def bezier_curve(control_points, num_points=100):
"""生成 Bezier 曲线"""
n = len(control_points) - 1
t_values = np.linspace(0, 1, num=num_points)
curve_x = []
curve_y = []
for t in t_values:
point_x = sum([bernstein_poly(i, n, t) * control_points[i][0] for i in range(n + 1)])
point_y = sum([bernstein_poly(i, n, t) * control_points[i][1] for i in range(n + 1)])
curve_x.append(point_x)
curve_y.append(point_y)
return np.array(curve_x), np.array(curve_y)
control_points = [[0, 0], [1, 1], [2, -1]] # 控制点
curve_x, curve_y = bezier_curve(control_points)
# 绘制结果
plt.plot(*zip(*control_points), '--o', label="Control Points", color="gray")
plt.plot(curve_x, curve_y, label="Bezier Curve", color="blue")
plt.legend()
plt.show()
```
上述代码实现了二阶贝塞尔曲线的绘制功能,并允许自定义控制点的位置以调整曲线形态。
---
#### 方法三:B 样条曲线拟合
对于更加复杂的场景,推荐使用 B 样条曲线(B-Spline)。它具有局部支持特性,适合大规模数据点的平滑操作[^4]。
```python
from scipy.interpolate import splev, splrep
import numpy as np
import matplotlib.pyplot as plt
# 输入数据点
x = np.array([0, 1, 2, 3, 4])
y = np.array([0, 0.8, 0.9, 0.1, -0.8])
# 计算 B 样条参数
tck = splrep(x, y, s=0) # 参数 s 控制光滑程度
xi = np.linspace(min(x), max(x), 100)
yi = splev(xi, tck)
# 绘制图像
plt.plot(x, y, 'o', label="Original Data Points")
plt.plot(xi, yi, '-', label="B-Spline Interpolation")
plt.legend()
plt.show()
```
该方法通过 SciPy 提供的功能自动完成样条曲线的构建,并可通过调节参数 `s` 来平衡拟合精度与平滑度。
---
#### 方法四:手动实现平滑逻辑
还可以借助一些辅助工具包或者自定义算法进一步优化平滑过程。例如 CSDN 上提到的一种基于贝塞尔曲线的手动平滑方案[^3]:
```python
def smoothing_base_bezier(x, y, k=0.3, closed=False):
"""
手动实现平滑曲线
:param x: 水平坐标数组
:param y: 垂直坐标数组
:param k: 控制点权重因子,默认为 0.3
:param closed: 是否闭合曲线
:return: 新的平滑后的 x 和 y 数组
"""
points = list(zip(x, y))
smoothed_x, smoothed_y = [], []
if not closed:
for i in range(len(points)-1):
start_point = points[i]
end_point = points[i+1]
mid_point = (
(start_point[0]+end_point[0])/2,
(start_point[1]+end_point[1])/2
)
cp_start = (
start_point[0] + k*(mid_point[0]-start_point[0]),
start_point[1] + k*(mid_point[1]-start_point[1])
)
cp_end = (
end_point[0] - k*(mid_point[0]-end_point[0]),
end_point[1] - k*(mid_point[1]-end_point[1])
)
t_vals = np.linspace(0, 1, 50)
segment_x = [
(1-t)**2*start_point[0] + 2*t*(1-t)*cp_start[0] + t**2*end_point[0] for t in t_vals
]
segment_y = [
(1-t)**2*start_point[1] + 2*t*(1-t)*cp_start[1] + t**2*end_point[1] for t in t_vals
]
smoothed_x.extend(segment_x[:-1])
smoothed_y.extend(segment_y[:-1])
smoothed_x.append(end_point[0])
smoothed_y.append(end_point[1])
else:
pass # 需要额外处理闭环情况
return np.array(smoothed_x), np.array(smoothed_y)
# 测试用例
x_test = np.array([0, 1, 2, 3, 4])
y_test = np.array([0, 0.8, 0.9, 0.1, -0.8])
smoothed_x, smoothed_y = smoothing_base_bezier(x_test, y_test)
plt.plot(x_test, y_test, 'o-', label="Raw Data")
plt.plot(smoothed_x, smoothed_y, '-', label="Smoothed Curve")
plt.legend()
plt.show()
```
这种做法提供了更高的定制化能力,同时也保留了一定的通用性。
---
### 总结
以上介绍了四种不同的方式用于解决平滑曲线拟合问题,分别是多项式插值、贝塞尔曲线、B 样条以及手写平滑逻辑。每种方法都有其适用范围和特点,具体选择取决于实际应用场景的需求。