优化商户每日用户增长率计算代码:解决连续零销量问题
问题
需要为DataFrame添加「商户每日用户增长率」列,已编写相关代码,但部分商户存在连续零销量的情况(0→0无有效增长率),导致代码运行异常,请求优化建议。
当前使用的代码如下:
# 筛选日期范围和订单类型 df_wk = df.loc[(df['Booked at'] >= '2023-01-01') & (df['Booked at'] <= '2023-05-31')] # 筛选Eats类型订单 df_eats = df_wk.loc[df_wk['Job Type'] == 'Eats'] # 创建包含所有商户和日期的空DataFrame dates = pd.date_range('2023-01-01', '2023-05-31', freq='D') merchants = df_eats['Merchant Name'].unique() index = pd.MultiIndex.from_product([dates, merchants], names=['Booked at', 'Merchant Name']) all_sales = pd.DataFrame(index=index).reset_index() # 按日期和商户分组,统计每日独立用户数 eats_users = df_eats.groupby(['Booked at', 'Merchant Name']).agg({'Customer Name': lambda x: x.nunique()}) eats_users.rename(columns={'Customer Name': 'Total Unique Users'}, inplace=True) # 按日期排序 eats_users.sort_values('Booked at', inplace=True) # 合并数据,填充零销量日期的用户数为0 unique_users = pd.merge(all_sales, eats_users, on=['Booked at', 'Merchant Name'], how='left') unique_users.fillna(0, inplace=True) # 再次按日期排序 unique_users.sort_values('Booked at', inplace=True) # 计算每日用户增长率 previous_users = {} for index, row in unique_users.iterrows(): current_users = row['Total Unique Users'] merchant = index[1] # 尝试从索引元组获取商户名(此处存在错误) if pd.isna(current_users): unique_users.at[index, 'Daily Growth Rate for Unique Users'] = pd.NA else: if merchant in previous_users: previous_users_count = previous_users[merchant] growth_rate = (current_users / previous_users_count - 1) * 100 unique_users.at[index, 'Daily Growth Rate for Unique Users'] = growth_rate else: unique_users.at[index, 'Daily Growth Rate for Unique Users'] = pd.NA previous_users[merchant] = current_users
期望输出示例:
Booked at Merchant Name Total Unique Users 0 2023-01-01 Merchant A 38 1 2023-01-01 Merchant B 75 2 2023-01-01 Merchant C 74 3 2023-01-02 Merchant A 33 4 2023-01-02 Merchant B 71 .. ... ... ... 447 2023-05-30 Merchant A 75 449 2023-05-30 Merchant C 13 451 2023-05-31 Merchant B 76 450 2023-05-31 Merchant A 52 452 2023-05-31 Merchant C 2 Daily Growth Rate for Unique Users 0 NaN 1 NaN 2 NaN 3 -13.157895 4 -5.333333 .. ... 447 5.633803 449 -45.833333 451 13.432836 450 -30.666667 452 -84.615385 [453 rows x 4 columns]
优化方案
原代码核心问题
- 索引处理错误:
iterrows()返回的index是整数行号,不是创建的MultiIndex,因此merchant = index[1]会直接报错。 - 除数为零异常:当商户连续零销量时,
previous_users_count为0,执行除法会触发ZeroDivisionError。 - 循环效率低下:用
iterrows()遍历大DataFrame速度慢,不符合pandas向量化操作逻辑。
优化后代码
import pandas as pd # 筛选日期范围和订单类型 df_wk = df.loc[(df['Booked at'] >= '2023-01-01') & (df['Booked at'] <= '2023-05-31')] df_eats = df_wk.loc[df_wk['Job Type'] == 'Eats'] # 创建包含所有商户和日期的全量索引 dates = pd.date_range('2023-01-01', '2023-05-31', freq='D') merchants = df_eats['Merchant Name'].unique() index = pd.MultiIndex.from_product([dates, merchants], names=['Booked at', 'Merchant Name']) # 按日期和商户统计独立用户数,自动填充零销量日期为0 eats_users = df_eats.groupby(['Booked at', 'Merchant Name'])['Customer Name'].nunique() unique_users = eats_users.reindex(index, fill_value=0).reset_index() unique_users.rename(columns={'Customer Name': 'Total Unique Users'}, inplace=True) # 按商户分组,获取前一日的用户数 unique_users['Previous Day Users'] = unique_users.groupby('Merchant Name')['Total Unique Users'].shift(1) # 计算增长率:处理除数为0的情况 def calculate_growth(row): prev = row['Previous Day Users'] curr = row['Total Unique Users'] if pd.isna(prev) or prev == 0: # 首日或前一日用户数为0时,无有效增长率 return pd.NA return (curr / prev - 1) * 100 unique_users['Daily Growth Rate for Unique Users'] = unique_users.apply(calculate_growth, axis=1) # 按日期+商户排序(保证输出顺序一致) unique_users.sort_values(['Booked at', 'Merchant Name'], inplace=True) unique_users.reset_index(drop=True, inplace=True)
关键改进点
- 用
reindex替代合并填充:直接通过reindex生成全量日期+商户的DataFrame,自动填充零销量为0,代码更简洁。 - 分组移位获取前一日数据:用
groupby('Merchant Name').shift(1)代替循环存储previous_users,避免索引错误,效率更高。 - 除数为零逻辑处理:
- 首日数据(无前置数据):设为
pd.NA - 前一日用户数为0:无论今日是否为0,均设为
pd.NA(0→0或0→N都无有效增长率)
- 首日数据(无前置数据):设为
- 向量化操作替代循环:抛弃
iterrows()遍历,大幅提升大数据集的运行效率。
输出验证
优化后的代码会生成符合期望的输出,所有无效增长率场景(首日、0→0、0→N)都会被标记为NaN,有效场景正常计算增长率。
内容的提问来源于stack exchange,提问作者Mark Yu
相关产品推荐
相关产品推荐

