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

Python中满足月度均值约束的月度转日度三次样条插值

带月度均值约束的三次样条插值方案

针对你的需求——生成满足月度均值约束的日度三次样条插值数据,有两种实用方案,以下是具体实现细节:

方案一:插值后均值调整(简单高效)

常规三次样条插值仅保证节点值匹配,无法直接满足区间均值约束。最快捷的解决方式是先完成插值,再对每个月的结果做整体偏移调整,既保留样条的平滑趋势,又严格满足月度均值要求。

代码实现

import pandas as pd
import numpy as np
from scipy.interpolate import CubicSpline

# 1. 准备原始月度数据
monthly_data = pd.DataFrame({
    'Month': ['01/10/2024', '01/11/2024', '01/12/2024'],
    'Value': [100, 150, 400]
})
monthly_data['Month'] = pd.to_datetime(monthly_data['Month'], format='%d/%m/%Y')
monthly_data['Month_Period'] = monthly_data['Month'].dt.to_period('M')
monthly_mean_map = monthly_data.set_index('Month_Period')['Value'].to_dict()

# 2. 生成完整日度日期序列
date_range = pd.date_range(start='2024-10-01', end='2024-12-31', freq='D')

# 3. 执行常规三次样条插值
# 将月度日期转换为起始日的偏移天数作为插值x轴
x_nodes = (monthly_data['Month'] - date_range[0]).dt.days.values
y_nodes = monthly_data['Value'].values
cs = CubicSpline(x_nodes, y_nodes)
daily_interpolated = cs((date_range - date_range[0]).dt.days.values)

# 4. 构建初始日度DataFrame并按月份调整均值
daily_df = pd.DataFrame({
    'Date': date_range,
    'Interpolated_Value': daily_interpolated,
    'Month': date_range.to_period('M')
})

# 分组调整:给每个月的插值数据加偏移量,使均值匹配原始月度值
adjusted_daily_df = daily_df.groupby('Month').apply(
    lambda group: group.assign(
        Adjusted_Value=group['Interpolated_Value'] + (monthly_mean_map[group.name] - group['Interpolated_Value'].mean())
    )
).reset_index(drop=True)

# 验证月度均值(可选)
print(adjusted_daily_df.groupby('Month')['Adjusted_Value'].mean())

原理说明

该方法通过整体偏移每个月的插值结果来满足均值约束,完全保留了三次样条的平滑曲线形状,仅调整整体高度,实现成本极低,适合绝大多数业务场景。


方案二:带均值约束的优化型三次样条(严谨可控)

若要求插值曲线本身就满足均值约束(而非事后调整),可以通过scipy.optimize优化样条的边界导数,使每个月的曲线积分等于「月度均值×当月天数」,同时保证曲线平滑性。

代码实现

import pandas as pd
import numpy as np
from scipy.interpolate import CubicSpline
from scipy.optimize import minimize

# 1. 准备原始数据与日期序列
monthly_data = pd.DataFrame({
    'Month': ['01/10/2024', '01/11/2024', '01/12/2024'],
    'Value': [100, 150, 400]
})
monthly_data['Month'] = pd.to_datetime(monthly_data['Month'], format='%d/%m/%Y')
monthly_data['days_in_month'] = monthly_data['Month'].dt.days_in_month
date_range = pd.date_range(start='2024-10-01', end='2024-12-31', freq='D')

# 2. 定义插值节点与目标积分值
x_nodes = (monthly_data['Month'] - date_range[0]).dt.days.values
y_nodes = monthly_data['Value'].values
target_integrals = (monthly_data['Value'] * monthly_data['days_in_month']).values

# 3. 定义优化目标函数:最小化积分误差+曲线曲率(保证平滑)
def objective(derivatives):
    # 用指定的边界导数构建三次样条
    cs = CubicSpline(x_nodes, y_nodes, bc_type=((1, derivatives[0]), (1, derivatives[1])))
    # 计算每个月的积分值
    integrals = [
        cs.integrate(0, 30),    # 10月:第0天(10/1)到第30天(10/31)
        cs.integrate(31, 60),   # 11月:第31天(11/1)到第60天(11/30)
        cs.integrate(61, 91)    # 12月:第61天(12/1)到第91天(12/31)
    ]
    # 积分误差平方和 + 曲率惩罚项(控制平滑度)
    error = np.sum((np.array(integrals) - target_integrals)**2)
    curvature = np.sum(cs(x_nodes, 2)**2)
    return error + 0.01 * curvature

# 4. 优化边界导数
initial_guess = [0, 0]  # 初始导数猜测值
result = minimize(objective, initial_guess, method='L-BFGS-B')

# 5. 生成满足约束的日度插值数据
optimal_cs = CubicSpline(x_nodes, y_nodes, bc_type=((1, result.x[0]), (1, result.x[1])))
optimal_daily_values = optimal_cs((date_range - date_range[0]).dt.days.values)
optimal_daily_df = pd.DataFrame({
    'Date': date_range,
    'Optimal_Value': optimal_daily_values
})

# 验证月度均值(可选)
print(optimal_daily_df.groupby(optimal_daily_df['Date'].dt.to_period('M'))['Optimal_Value'].mean())

原理说明

该方法通过优化样条的边界导数,让每个月的曲线积分严格匹配目标值,同时加入曲率惩罚项保证曲线平滑。实现相对复杂,但能得到原生满足约束的插值曲线,适合对曲线形态有严格要求的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:38:12