如何为DataFrame逐行计算值?DAX转Python遇结果一致问题
解决DAX转Python时行级滚动聚合结果重复的问题
原DAX代码的核心逻辑是:对每一行数据,按Item_Number分组,计算该行Casting_Date往前推365天到该行日期前一天的Inclusion总和与Closed总和的比值。
你的Python代码出现每行结果相同的原因是:_item_number和_start_date都是Series类型,直接用df2['Item_Number'] == _item_number会触发广播机制,最终筛选出的是整个数据集里所有满足对应行条件的记录,求和后得到一个标量值并赋值给整列,导致所有行结果一致。
以下是三种可行的解决方向:
方法1:使用apply逐行计算(直观易理解)
对DataFrame的每一行单独执行计算逻辑,适合数据量不大的场景:
import pandas as pd def calculate_scrap_rate(row, df): # 获取当前行的Item和日期范围 item = row['Item_Number'] end_date = row['Casting_Date'] start_date = end_date - pd.DateOffset(days=365) # 筛选同Item且在日期范围内的记录 mask = (df['Item_Number'] == item) & \ (df['Casting_Date'] >= start_date) & \ (df['Casting_Date'] < end_date) # 计算总和 total_inclusions = df.loc[mask, 'Inclusion'].sum() total_closed = df.loc[mask, 'Closed'].sum() # 计算比值,处理除零情况 return total_inclusions / total_closed if total_closed != 0 else None # 应用到每一行 df2['expected_inclusion_scrap_rate'] = df2.apply(calculate_scrap_rate, axis=1, df=df2)
方法2:使用分组滚动窗口(高效,适合大数据集)
利用pandas的groupby结合rolling窗口,先按Item_Number分组,再基于时间窗口计算滚动聚合:
# 先确保Casting_Date是datetime类型 df2['Casting_Date'] = pd.to_datetime(df2['Casting_Date']) # 按Item_Number分组,设置时间滚动窗口为365天,窗口不包含当前行日期 grouped = df2.groupby('Item_Number').rolling( window='365D', on='Casting_Date', closed='left' # 对应DAX中<Casting_Date的逻辑 ) # 计算滚动总和 df2['inclusions_group'] = grouped['Inclusion'].sum().reset_index(level=0, drop=True) df2['closed_group'] = grouped['Closed'].sum().reset_index(level=0, drop=True) # 计算报废率,处理除零 df2['expected_inclusion_scrap_rate'] = df2['inclusions_group'].div(df2['closed_group']).fillna(None)
方法3:使用merge_asof(高效的时间匹配方式)
先按Item分组排序,再用merge_asof匹配时间范围内的记录,最后聚合:
# 排序(merge_asof要求左右表都按匹配键排序) df_sorted = df2.sort_values(['Item_Number', 'Casting_Date']) # 生成每个行的时间范围 df_sorted['start_date'] = df_sorted['Casting_Date'] - pd.DateOffset(days=365) # 自连接,匹配同Item且日期在[start_date, Casting_Date)范围内的记录 merged = pd.merge_asof( df_sorted, df_sorted[['Item_Number', 'Casting_Date', 'Inclusion', 'Closed']], by='Item_Number', left_on='start_date', right_on='Casting_Date', direction='forward' ) # 按原行的索引分组,计算总和 aggregated = merged.groupby(merged.index).agg( total_inclusions=('Inclusion_y', 'sum'), total_closed=('Closed_y', 'sum') ) # 合并回原表 df2 = df2.join(aggregated) df2['expected_inclusion_scrap_rate'] = df2['total_inclusions'].div(df2['total_closed']).fillna(None)
内容的提问来源于stack exchange,提问作者Jordan Bennett
相关产品推荐
相关产品推荐

