Python中带条件的GroupBy与违规分类问题求助
解决思路:分组统计格式优化与违规分类实现
嗨,我来帮你一步步搞定这两个问题,先从分组统计的格式调整说起,再处理违规分类的逻辑~
一、分组统计:实现目标显示格式
你用df.groupby(['name', 'id']).size().reset_index(name='counts')其实已经得到了正确的统计结果,只是显示格式不符合预期。要注意的是:DataFrame本身的存储结构里每一行的name列都有值,你想要的“同一个name只显示第一行”是可视化/输出层面的格式,我们可以通过两种方式实现:
方式1:仅用于显示的样式设置(适合Notebook展示)
如果只是在Jupyter Notebook里想让表格更易读,可以用pandas的Styler隐藏重复的name值:
import pandas as pd # 先完成基础分组统计 df_counts = df.groupby(['name', 'id'], as_index=False).size().rename(columns={'size': 'count'}) # 设置样式,把重复的name单元格隐藏 styled_table = df_counts.style.hide(axis='index').set_table_styles([ {'selector': 'tr:not(:first-child) td:first-child', 'props': [('visibility', 'hidden')]} ]) # 展示样式化后的表格 display(styled_table)
方式2:生成带空值的DataFrame(适合导出Excel等)
如果需要把重复name的位置设为空字符串(比如导出到Excel时保持格式),可以先排序,再用shift判断是否和前一行name相同:
df_counts = df.groupby(['name', 'id'], as_index=False).size().rename(columns={'size': 'count'}) # 按name排序,确保同一name的行连续排列 df_counts = df_counts.sort_values('name') # 把和上一行重复的name替换为空字符串 df_counts['name'] = df_counts['name'].where(df_counts['name'] != df_counts['name'].shift(), '') # 输出结果 print(df_counts)
这样得到的DataFrame就完全符合你期望的格式了。
二、违规分类:按规则判断
你的思路方向是对的,但需要先拿到每个name对应的90和91的计数,再按规则逐步判断。这里用pivot_table把计数转成列会更方便处理:
完整实现代码
import pandas as pd # 第一步:获取每个name对应的90、91计数 pivot_df = df.pivot_table( index='name', columns='id', values='id', aggfunc='count', fill_value=0 # 把不存在的id计数设为0 ).rename(columns={90: 'count_90', 91: 'count_91'}) # 第二步:编写违规判断逻辑 def check_violation(row): # 情况1:仅对应一个id(90或91),不属于违规 if row['count_90'] == 0 or row['count_91'] == 0: return 0 # 情况2:同时对应两个id else: # 子情况i:90计数<5且91计数>0,不属于违规 if row['count_90'] < 5 and row['count_91'] > 0: return 0 # 其他情况都属于违规 else: return 1 # 应用判断函数到每一行 pivot_df['violation'] = pivot_df.apply(check_violation, axis=1) # 提取最终需要的列 result_df = pivot_df[['violation']].reset_index() # 输出结果 print(result_df)
简化版(用numpy链式判断)
如果想让代码更简洁,可以用np.where替代自定义函数:
import pandas as pd import numpy as np pivot_df = df.pivot_table( index='name', columns='id', values='id', aggfunc='count', fill_value=0 ).rename(columns={90: 'c90', 91: 'c91'}) # 用np.where嵌套实现多条件判断 pivot_df['violation'] = np.where( # 仅一个id的情况,返回0 (pivot_df['c90'] == 0) | (pivot_df['c91'] == 0), 0, # 两个id的情况,再判断是否符合非违规条件 np.where( (pivot_df['c90'] < 5) & (pivot_df['c91'] > 0), 0, 1 ) ) result_df = pivot_df[['violation']].reset_index()
这样就能完美得到你想要的违规分类结果啦!
内容的提问来源于stack exchange,提问作者user13419531
相关产品推荐
相关产品推荐

