Pandas中如何根据另一DataFrame参数为当前DataFrame列返回匹配值
实现方案
下面给两种可直接运行的实现方式,优先推荐第一种分组匹配方案,性能更优,可直接适配多商品的场景。
前置准备:数据预处理
首先导入pandas并构造样例数据,注意要先把发货日期转为datetime格式,避免字符串排序导致日期顺序错乱:
import pandas as pd # 构造订单DataFrame df_order = pd.DataFrame({ 'product_id': ['01', '01', '01'], 'product_name': ['ABC', 'ABC', 'ABC'], 'order_name': ['A1', 'A2', 'A3'], 'qty': [1, 2, 3] }) # 构造发货记录DataFrame df_ship = pd.DataFrame({ 'product_name': ['ABC']*5, 'ship_date': ['01/01/2022', '01/02/2022', '01/03/2022', '01/04/2022', '01/05/2022'], 'ship_qty': [1, 0, 2, 1, 3] }) # 转换日期格式,保证排序逻辑正确 df_ship['ship_date'] = pd.to_datetime(df_ship['ship_date'], format='%m/%d/%Y')
方法1:分组匹配(推荐,适合大数据量场景)
先把发货记录按商品分组,组内提前按发货日期升序排列,匹配时直接从对应分组里筛选符合发货量要求的第一条记录即可,比逐行循环效率高很多:
# 发货表按商品分组,组内按发货日期升序排序 ship_grouped = df_ship.sort_values(['product_name', 'ship_date']).groupby('product_name') def match_ship_date(product_name, require_qty): # 取出对应商品的所有发货记录 product_ship_list = ship_grouped.get_group(product_name) # 筛选发货量≥订单量的最早记录,返回和样例格式一致的日期字符串 first_match = product_ship_list[product_ship_list['ship_qty'] >= require_qty].iloc[0] return first_match['ship_date'].strftime('%m/%d/%Y') # 为订单表新增匹配到的发货日期列 df_order['ship_date'] = df_order.apply( lambda row: match_ship_date(row['product_name'], row['qty']), axis=1 )
运行后df_order的输出和期望结果完全一致。
方法2:for循环实现(逻辑直观,适合小数据量场景)
如果你更习惯for循环写法,逻辑也很简单,遍历每一条订单时,直接从排序后的发货表中筛选符合条件的记录即可:
# 提前把发货表按商品、发货日期升序排好 df_ship_sorted = df_ship.sort_values(['product_name', 'ship_date']).reset_index(drop=True) match_result = [] for _, row in df_order.iterrows(): p_name = row['product_name'] req_qty = row['qty'] # 筛选同商品、发货量达标的最早记录 match_record = df_ship_sorted[ (df_ship_sorted['product_name'] == p_name) & (df_ship_sorted['ship_qty'] >= req_qty) ].iloc[0] match_result.append(match_record['ship_date'].strftime('%m/%d/%Y')) df_order['ship_date'] = match_result
注意:如果存在订单需求量大于对应商品所有单条发货记录的发货量,上述代码取
iloc[0]时会抛出索引错误,你可以在取数前先判断筛选结果是否为空,按需返回空值或者自定义标记即可。
内容的提问来源于stack exchange,提问作者Hannah
相关产品推荐
相关产品推荐

