You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何拟合多条收益率曲线并按曲线拆分pandas DataFrame

多收益率曲线拟合与数据集拆分方案

单轮RANSAC只能提取1条曲线,要提取散点中隐含的3-4条独立收益率曲线,直接用序贯RANSAC逻辑即可,不需要换复杂算法,在你现有代码基础上做迭代就能实现。


1. 多曲线独立拟合实现

核心逻辑是:拟合完1条曲线后,把该曲线对应的内点从样本池中移除,在剩余未分配的点里重复拟合过程,直到提取够目标数量的曲线,或者剩余样本量不足无法拟合为止。
几个关键调参注意点:

  • 你原来设置的residual_threshold=2 * np.std(y_ax)过大,会把其他曲线的点误判为当前曲线的内点,建议初始设为0.2-0.5(对应0.2-0.5个百分点的收益率差,匹配不同评级信用债的常规利差区间),再根据拟合效果微调
  • 多项式阶数保持3阶即可,收益率曲线本身符合三次多项式形态,阶数过高会出现过拟合的波浪形异常
  • 初始目标曲线数设为4,若某轮拟合出的曲线明显由噪声点构成,再把曲线数下调到3

完整拟合代码如下,可直接复用你已写的多项式回归类:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import RANSACRegressor
from sklearn.metrics import mean_squared_error

# 原有多项式回归类无需修改
class PolynomialRegression(object):
    def __init__(self, degree=3, coeffs=None):
        self.degree = degree
        self.coeffs = coeffs

    def fit(self, X, y):
        self.coeffs = np.polyfit(X.ravel(), y, self.degree)

    def get_params(self, deep=False):
        return {'coeffs': self.coeffs}

    def set_params(self, coeffs=None, random_state=None):
        self.coeffs = coeffs

    def predict(self, X):
        poly_eqn = np.poly1d(self.coeffs)
        y_hat = poly_eqn(X.ravel())
        return y_hat

    def score(self, X, y):
        return mean_squared_error(y, self.predict(X))

# 多曲线拟合参数配置
poly_degree = 3
target_curve_num = 4
residual_threshold = 0.3  # 单曲线内点残差阈值,可根据实际效果调整
min_sample_per_curve = 50 # 单条曲线最少样本数,低于该值停止拟合

fitted_models = []
unassigned_mask = np.ones(len(df_clean), dtype=bool) # 初始所有点标记为未分配

for i in range(target_curve_num):
    # 提取当前未分配的样本
    x_unassign = df_clean.loc[unassigned_mask, 'YTM'].values
    y_unassign = df_clean.loc[unassigned_mask, 'YTW'].values
    
    # 剩余样本不足则终止拟合
    if len(x_unassign) < min_sample_per_curve:
        break
    
    # 训练单条曲线的RANSAC模型
    ransac = RANSACRegressor(
        PolynomialRegression(degree=poly_degree),
        residual_threshold=residual_threshold,
        random_state=0
    )
    ransac.fit(np.expand_dims(x_unassign, axis=1), y_unassign)
    
    # 将当前模型识别的内点映射回全量数据索引
    inlier_local_mask = ransac.inlier_mask_
    unassign_global_idx = np.where(unassigned_mask)[0]
    inlier_global_idx = unassign_global_idx[inlier_local_mask]
    
    # 保存模型,标记内点为已分配
    fitted_models.append({
        'model': ransac,
        'inlier_idx': inlier_global_idx
    })
    unassigned_mask[inlier_global_idx] = False

2. 原始DataFrame拆分逻辑

不要直接用RANSAC输出的内点掩码拆分子集——RANSAC拟合时的内点判断只针对当前轮的剩余样本,容易漏判边界点。更稳妥的方式是:对每个样本点,计算它到所有拟合曲线的垂直残差(实际YTW与曲线预测YTW的差值绝对值),将点分配给残差最小的曲线;若最小残差超过阈值,可归为离群点单独存储。
对应代码如下:

# 生成用于曲线预测的X轴序列
x_pred = np.linspace(df_clean['YTM'].min(), df_clean['YTM'].max(), 1000)
curve_predictions = [mod['model'].predict(np.expand_dims(x_pred, axis=1)) for mod in fitted_models]

# 逐点计算到所有曲线的残差,完成样本分配
sample_assign = np.zeros(len(df_clean), dtype=int)
outlier_threshold = 1.0 # 残差超过1个百分点判定为离群点
for idx, row in df_clean.iterrows():
    x_val, y_real = row['YTM'], row['YTW']
    res_list = [abs(y_real - mod['model'].predict(np.array([[x_val]]))[0]) for mod in fitted_models]
    min_res = min(res_list)
    if min_res > outlier_threshold:
        sample_assign[idx] = -1 # 离群点标记为-1
    else:
        sample_assign[idx] = np.argmin(res_list)

# 拆分得到各曲线对应的数据子集
df_subsets = []
for curve_id in range(len(fitted_models)):
    subset = df_clean[sample_assign == curve_id].copy()
    subset['curve_label'] = curve_id
    df_subsets.append(subset)
# 单独存储离群点子集
df_outliers = df_clean[sample_assign == -1].copy()

# 可视化验证拟合与拆分效果
colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
plt.figure(figsize=(12, 6))
for cid in range(len(fitted_models)):
    plt.scatter(df_subsets[cid]['YTM'], df_subsets[cid]['YTW'], s=10, color=colors[cid], label=f'曲线{cid+1}样本')
    plt.plot(x_pred, curve_predictions[cid], color=colors[cid], linewidth=3, label=f'拟合曲线{cid+1}')
plt.scatter(df_outliers['YTM'], df_outliers['YTW'], s=10, c='gray', label='离群点')
plt.xlabel('剩余到期期限(YTM)')
plt.ylabel('最差收益率(YTW)')
plt.legend()
plt.show()

调参参考

  • 若拟合时出现多条曲线重叠、单条曲线跨度过大,就把residual_threshold调小
  • 若某条明显存在的曲线没被拟合出来,要么把residual_threshold适当调大,要么增加target_curve_num
  • 若拟合出的曲线出现不符合收益率常识的剧烈波动,就把多项式阶数下调到2阶

内容的提问来源于stack exchange,提问作者Egemen Candir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 10:09:47