如何简化Pandas统计月日频率的多if函数并消除SettingWithCopyWarning
问题解决方案
1 冗余判断函数简化
你写的两个统计频次的函数完全可以删除,value_counts()返回的本身就是索引为待统计值、值为对应频次的Series,直接用map()方法映射到原列即可,一行代码就能替代几十行if判断:
# 直接映射频次,替代原来的两个自定义函数 df['dow_total'] = df['dow'].map(df['dow'].value_counts()) df['dom_total'] = df['dom'].map(df['dom'].value_counts())
额外还可以优化日期相关字段的提取逻辑,不用写lambda表达式,用pandas内置的dt属性更高效:
# 日期转换直接用pd.to_datetime,不用自定义lambda df['new_date'] = pd.to_datetime(df['date'], format='%d/%m/%Y') # 直接用dt属性提取字段 df['month'] = df['new_date'].dt.month df['dom'] = df['new_date'].dt.day df['dow'] = df['new_date'].dt.day_name() # 直接返回星期几的英文名称,和你原来strftime效果一致
2 SettingWithCopyWarning警告消除
这个警告出现在你给dfa加days_diff列的步骤,原因是dfa是从原df筛选出来的切片,pandas不确定你要修改的是原df还是切片副本。你不介意拷贝的话,只需要在筛选dfa的时候显式调用.copy()方法声明要生成独立副本即可:
# 末尾加.copy()显式生成副本 dfa = df.loc[df['dom'] != top_day_of_month].copy()
如果想全局关闭这类警告,可以在导入pandas后加一行配置:
pd.set_option('mode.chained_assignment', None)
更推荐显式加.copy()的方案,代码可读性更高,也不会屏蔽其他可能的链式赋值错误。顺带修复原代码中计算days_diff时误用原df索引导致的值错位问题。
完整优化后代码
import pandas as pd # 可选:全局关闭SettingWithCopyWarning,不需要的话可以删掉 # pd.set_option('mode.chained_assignment', None) df = pd.read_csv(r"C:\Users\mattl\OneDrive\Desktop\netflix - only.csv") # 转换日期格式+提取衍生字段 df['new_date'] = pd.to_datetime(df['date'], format='%d/%m/%Y') df['month'] = df['new_date'].dt.month df['dom'] = df['new_date'].dt.day df['dow'] = df['new_date'].dt.day_name() # 直接映射频次,完全删除原来的两个自定义函数 df['dow_total'] = df['dow'].map(df['dow'].value_counts()) df['dom_total'] = df['dom'].map(df['dom'].value_counts()) print(df) if df["dom_total"].max() >= df["dow_total"].max(): # 筛选月中日频次最高的记录 top_dom_tot = df.loc[df['dom_total'] == df['dom_total'].max()] # 用iloc[0]避免索引异常 top_day_of_month = top_dom_tot['dom'].iloc[0] print('Top day of month is:') print(top_day_of_month) # 筛选非最高频记录时显式copy,消除警告 dfa = df.loc[df['dom'] != top_day_of_month].copy() dfa['days_diff'] = dfa['dom'] - top_day_of_month print('Payments that are not related to the top day per month') print(dfa)
内容的提问来源于stack exchange,提问作者Matt Lightbourn
相关产品推荐
相关产品推荐

