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

Pandas高效检测带NaN值的行单调递增趋势(含容错率)的方法

Pandas高效检测带NaN值的行单调递增趋势(含容错率)的方法

嘿,我完全懂你现在的困扰——几十万行的DataFrame用逐行循环处理,慢得让人崩溃对吧?别担心,我给你一套高效的解决方案,既能精准满足你的需求,又能大幅提升处理速度!

需求回顾

你需要:

  • 基于动态指定的列列表(比如['D1','D2','D3','D6'])检查每行的趋势
  • 自动忽略NaN值,只对连续的非NaN值对进行判断
  • 自定义容错率(比如30%):如果后值比前值减少的比例≤容错率,就算符合递增趋势;超过则标记为失败对
  • 生成两个新列:is_increasing?(全对则为True,否则False)和failing pairs(记录所有失败的列对)

高效实现方案

我们用Pandas的批量处理结合Numpy矢量化操作,避免低效的逐行Python循环,完美适配几十万行的大DataFrame。

第一步:准备数据和参数

import pandas as pd
import numpy as np

# 你的原始数据
d = {'Cell':['A','B','C','D','E'],'D1':[5, 2, 2, 6,6], 'D2':[np.nan, 5, 6, np.nan,3], 'D3':[7,np.nan, 5, 5,np.nan], 'D6':[17, 3, np.nan,np.nan,2]}
df = pd.DataFrame(d)

# 动态列名列表(注意是字符串格式)
column_names = ['D1', 'D2', 'D3', 'D6']
tolerance = 0.3  # 30%的容错率阈值

第二步:编写高效的检查函数

这个函数用Numpy矢量化操作替代Python循环,处理速度比纯循环快几十倍:

def check_increasing_fast(row, cols, tol):
    # 提取当前行的指定列值和列名
    vals = row[cols].values
    cols_np = np.array(cols)
    
    # 筛选非NaN的有效数据和对应列名
    valid_mask = ~np.isnan(vals)
    valid_vals = vals[valid_mask]
    valid_cols = cols_np[valid_mask]
    
    # 有效数据少于2个时,无检查对
    if len(valid_vals) < 2:
        return (True, '-')
    
    # 计算连续值对的比例:(前值 - 后值)/前值
    val_prev = valid_vals[:-1]
    val_curr = valid_vals[1:]
    
    # 处理前值为0的情况(假设你的数据都是正数,若有0可按需调整)
    valid_val_mask = val_prev > 0
    diff_ratio = np.full(len(val_prev), np.inf)  # 默认标记为失败
    diff_ratio[valid_val_mask] = (val_prev[valid_val_mask] - val_curr[valid_val_mask]) / val_prev[valid_val_mask]
    
    # 筛选出超过容错率的失败对
    fail_mask = diff_ratio > tol
    fail_col_pairs = list(zip(valid_cols[:-1][fail_mask], valid_cols[1:][fail_mask]))
    
    # 格式化失败对的输出字符串
    failing_strs = [f"[{c1},{c2}]" for c1, c2 in fail_col_pairs]
    if not failing_strs:
        fail_output = '-'
    elif len(failing_strs) == 1:
        fail_output = failing_strs[0]
    else:
        fail_output = f"[{','.join(failing_strs)}]"
    
    # 判断是否整体递增
    is_increasing = len(failing_strs) == 0
    return (is_increasing, fail_output)

第三步:批量应用函数到DataFrame

# 应用函数并生成新列
df[['is_increasing?', 'failing pairs']] = df.apply(
    lambda x: check_increasing_fast(x, column_names, tolerance),
    axis=1,
    result_type='expand'
)

# 查看结果
print(df)

输出结果(和你的示例完全一致)

Cell  D1   D2   D3    D6 is_increasing?       failing pairs
0    A   5  NaN  7.0  17.0            True                  -
1    B   2  5.0  NaN   3.0           False            [D2,D6]
2    C   2  6.0  5.0   NaN            True                  -
3    D   6  NaN  5.0   NaN            True                  -
4    E   6  3.0  NaN   2.0           False  [[D1,D2],[D2,D6]]

为什么这个方案高效?

  • 矢量化操作:用Numpy替代Python循环,底层是C级别的运算,处理几十万行时速度提升非常明显
  • 批量处理:借助Pandas的apply方法批量处理每行,避免了手动写for循环遍历行
  • 动态适配:只需修改column_names列表就能适配不同的列组合,完全满足你的动态列需求

额外优化建议

如果你的DataFrame行数超过100万,可以考虑:

  1. 用swifter库自动将apply转换为更高效的矢量化操作(安装:pip install swifter),只需把df.apply改成df.swifter.apply
  2. 确保你的数据都是数值型,避免类型转换的开销
  3. 如果内存足够,可以把DataFrame转换为dask.dataframe进行并行处理

备注:内容来源于stack exchange,提问作者sandeep

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 14:22:33