Python实现带约束的归一化权重计算:单权重不超过1/√(N)
实现方案
核心逻辑说明
我们先完成行归一化,再逐行处理权重约束,逐行处理逻辑如下:
- 先过滤当前行的NaN值,仅对有效值做权重调整
- 阈值为
1/np.sqrt(N),首先裁剪所有超限权重到阈值,裁剪溢出的部分计入待分配权重池 - 循环分配剩余权重:将待分配权重按未超限权重的相对占比分配,若分配后有新的权重超限,再次裁剪溢出部分回权重池,直到权重池剩余值小于浮点误差阈值或所有权重均达到阈值
- 最终将处理后的值回填到原行结构,保留原NaN位置
完整代码
import pandas as pd import numpy as np # 构造示例数据 df = pd.DataFrame({ 'a': [53, 54.06, 53.654, 55.2], 'b': [np.nan, 54.1121, 53.98, 55.12], 'c': [np.nan, 2, 53.322, 54.99], 'd': [np.nan, 53.1, 53.212, 55.002], 'e': [np.nan, 53, 53.2, 55.021], 'f': [np.nan, 53.11, 53.120, 55.3] }) # 1. 行归一化得到初始权重 df2 = df.div(df.sum(axis=1), axis=0) # 2. 定义权重约束处理函数 N = 5 max_weight = 1 / np.sqrt(N) # 约0.4472 eps = 1e-9 # 浮点误差容忍值 def adjust_weights(row): # 过滤NaN,保留索引 valid = row.dropna() if len(valid) == 0: return row # 第一步:裁剪超限值,统计待分配权重 clipped = valid.clip(upper=max_weight) remaining = valid.sum() - clipped.sum() current_weights = clipped.copy() # 循环分配剩余权重 while remaining > eps: # 筛选未达上限的权重 uncap_mask = current_weights < max_weight - eps uncap_weights = current_weights[uncap_mask] if len(uncap_weights) == 0: # 所有都达上限,剩余权重平均分配(极端边界情况,比如单行仅1个有效值) current_weights += remaining / len(current_weights) break # 按相对占比分配剩余权重 sum_uncap = uncap_weights.sum() add_ratio = remaining / sum_uncap current_weights[uncap_mask] = uncap_weights * (1 + add_ratio) # 再次裁剪新超限的部分,更新剩余权重 new_clipped = current_weights.clip(upper=max_weight) remaining = current_weights.sum() - new_clipped.sum() current_weights = new_clipped # 回填到原行,保留NaN row.loc[current_weights.index] = current_weights return row # 3. 逐行应用处理逻辑 df_adjusted = df2.apply(adjust_weights, axis=1)
结果验证
输出df_adjusted即可查看处理后的权重,每行权重和始终为1,所有有效值均不超过max_weight,完全符合需求的分配规则。
内容的提问来源于stack exchange,提问作者MathMan 99
相关产品推荐
相关产品推荐

