Python实现归一化DataFrame带上限权重重分配的无循环方案
权重上限裁剪与再分配pandas向量化实现
你需要的无显式循环、纯向量化的Python实现方案如下,所有核心运算均基于numpy/pandas原生接口,性能远高于逐行遍历的循环实现。
核心逻辑
算法通过迭代向量化操作完成权重调整,直到收敛:
- 先将所有超过上限的权重截断到阈值,计算每行截断后多出的冗余总权重
- 将冗余权重平均分配给所有未达到上限的有效权重
- 重复上述步骤,直到所有行权重总和收敛到1,且所有权重不超过设定上限
- 全程保留原始NaN的位置,可根据需求配置是否让NaN对应的位置参与分配
完整实现代码
import pandas as pd import numpy as np # 生成示例数据 df = pd.DataFrame({ 'a': [5003, 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] }) N = 5 cap = 1 / np.sqrt(N) # 权重上限:0.4472135954999579 # 初始行归一化 df = df.div(df.sum(axis=1), axis=0) def adjust_weights(df, cap, na_exclude=True): """ :param df: 已行归一化的权重DataFrame :param cap: 单个权重的上限阈值 :param na_exclude: 是否排除NaN列,不参与权重分配 :return: 调整后的权重DataFrame """ # 记录原始NaN位置 na_mask = df.isna() w = df.fillna(0).values n_rows = w.shape[0] while True: # 截断超过上限的权重 clipped = np.clip(w, 0, cap) # 计算每行需要再分配的冗余总权重 surplus = 1 - clipped.sum(axis=1, keepdims=True) # 所有行权重总和收敛到1时退出迭代 if np.allclose(surplus, 0): break # 筛选可分配的位置 eligible_mask = (w < cap) if na_exclude: eligible_mask = eligible_mask & (~na_mask.values) eligible_cnt = eligible_mask.sum(axis=1, keepdims=True) # 避免除以0,无有效可分配位置时直接跳过 eligible_cnt[eligible_cnt == 0] = 1 # 冗余平均分配给所有有效位置 w = clipped + surplus / eligible_cnt # 还原原始NaN位置 res = pd.DataFrame(w, index=df.index, columns=df.columns) res = res.mask(na_mask) return res
测试验证
你的示例场景测试
# 测试你给出的小例子 test_df = pd.DataFrame([[0.1, 0.3, 0.5, 0.1]]) print(adjust_weights(test_df, 0.4)) # 输出结果符合预期:[[0.1, 0.4, 0.4, 0.1]] # 测试你的示例DataFrame adjusted_df = adjust_weights(df, cap) print(adjusted_df)
内容的提问来源于stack exchange,提问作者MathMan 99
相关产品推荐
相关产品推荐

