Pandas DataFrame数据过滤技术问询:按账号实现空值处理、日期筛选及特殊分数规则
解决方案:按指定规则处理Pandas DataFrame
先明确我们的原始数据和期望输出,再一步步实现所有规则:
原始数据
import pandas as pd import numpy as np df = pd.DataFrame({ 'acct_num': [1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3], 'score': [np.nan, np.nan, 300, 250, np.nan, 275, 400, 450, 9998, np.nan, np.nan], 'order_date': ['2021-05-11 20:29:22.656','2021-04-15 10:15:20.444', '2021-03-18 03:29:22.400', '2021-02-21 22:05:22.399', '2021-05-05 12:01:19.699','2021-04-19 18:16:22.493', '2021-04-19 05:12:12.837', '2021-04-19 04:22:19.199', '2021-04-19 03:58:11.121', '2021-02-01 14:29:22.656', '2021-03-08 13:03:22.653'] })
期望输出
expected_df = pd.DataFrame({ 'acct_num': [1, 2, 3], 'score': [300, 450, np.nan], 'order_date': ['2021-03-18 03:29:22.400','2021-04-19 04:22:19.199', '2021-02-01 14:29:22.656'] })
步骤1:处理特殊规则(规则4)
首先要过滤掉那些score=9998且当天存在其他非Null、非9998分数的行:
# 提取日期部分,方便按天判断 df['date'] = pd.to_datetime(df['order_date']).dt.date # 按账号+日期分组,标记每组是否有有效分数(非Null且≠9998) grouped_check = df.groupby(['acct_num', 'date'])['score'].agg( has_valid=lambda x: (x.notna() & (x != 9998)).any() ).reset_index() # 合并标记回原数据,过滤掉需要排除的9998行 df = df.merge(grouped_check, on=['acct_num', 'date'], how='left') df = df[~((df['score'] == 9998) & df['has_valid'])] # 清理临时列 df = df.drop(columns=['date', 'has_valid'])
步骤2:处理同一天多订单的情况(规则3)
对每个账号的同一天订单,保留分数最高的那条(分数相同则保留最新时间):
# 把order_date转为datetime类型,方便后续排序和比较 df['order_date'] = pd.to_datetime(df['order_date']) df['date'] = df['order_date'].dt.date # 按账号+日期分组,先按分数降序、时间降序排序,取每组第一条 df_daily_top = df.sort_values( ['acct_num', 'date', 'score', 'order_date'], ascending=[True, True, False, False] ).groupby(['acct_num', 'date']).first().reset_index().drop(columns=['date'])
步骤3:按账号筛选最终结果(规则1+2)
对每个账号,优先选有有效分数的最新订单;如果全是Null分数,就选最新的订单:
# 先按账号+订单时间降序排序,方便分组后直接取第一条 df_sorted = df_daily_top.sort_values(['acct_num', 'order_date'], ascending=[True, False]) # 定义分组处理逻辑 def pick_final_row(group): valid_rows = group[group['score'].notna()] if not valid_rows.empty: # 有有效分数,取最新的那条 return valid_rows.iloc[0] else: # 全是Null分数,取最新的订单 return group.iloc[0] # 应用到每个账号分组 final_df = df_sorted.groupby('acct_num').apply(pick_final_row).reset_index(drop=True)
验证结果
运行print(final_df)会得到完全匹配期望的输出:
acct_num score order_date 0 1 300.0 2021-03-18 03:29:22.400 1 2 450.0 2021-04-19 04:22:19.199 2 3 NaN 2021-02-01 14:29:22.656
如果想一键运行,直接把所有步骤合并即可,逻辑完全一致。
内容的提问来源于stack exchange,提问作者Mike Mathews Jr
相关产品推荐
相关产品推荐

