基于Pandas的groupby、apply、transform实现列值对比与新列生成
解决Pandas分组生成Listing-history字段的问题
原始DataFrame
import pandas as pd d= {'Time': [0,1,2,0,1,2,2,3,4], 'Price': ['Auction', 'Auction','800','900','By Negotiation','700','250','250','Make Offer'], 'Item': ['Picasso', 'Picasso', 'Picasso', 'DaVinci', 'DaVinci', 'DaVinci', 'Dali', 'Dali', 'Dali']} df = pd.DataFrame(data=d)
需求规则
- 若为该商品首次出现的记录,标记为
first seen(不一定是Time == 0) - 若相邻时间点的Price字段无变化,标记为
ongoing listing - 若Price字段从数值型字符串变为非数值字符串(如'Auction'、'By Negotiation'),标记为
Price->[目标非数值字符串] - 若Price字段从非数值字符串变为数值型字符串,标记为
[原非数值字符串]->Price - 需按
Item分组后应用上述逻辑
解决方案
可以通过groupby.apply对每个分组单独处理,结合自定义函数实现所有规则:
完整代码
import pandas as pd d= {'Time': [0,1,2,0,1,2,2,3,4], 'Price': ['Auction', 'Auction','800','900','By Negotiation','700','250','250','Make Offer'], 'Item': ['Picasso', 'Picasso', 'Picasso', 'DaVinci', 'DaVinci', 'DaVinci', 'Dali', 'Dali', 'Dali']} df = pd.DataFrame(data=d) # 标记每个Price是否为数值型字符串 df['is_numeric'] = pd.to_numeric(df['Price'], errors='coerce').notna() def process_group(group): listing_history = [] for i in range(len(group)): if i == 0: # 分组第一条记录标记为first seen listing_history.append('first seen') else: prev_price = group.iloc[i-1]['Price'] curr_price = group.iloc[i]['Price'] prev_is_num = group.iloc[i-1]['is_numeric'] curr_is_num = group.iloc[i]['is_numeric'] if curr_price == prev_price: listing_history.append('ongoing listing') else: if prev_is_num and not curr_is_num: # 数值转非数值格式 listing_history.append(f'Price->{curr_price}') elif not prev_is_num and curr_is_num: # 非数值转数值格式 listing_history.append(f'{prev_price}->Price') else: # 同类型但内容变化的情况(可根据需求调整标记) listing_history.append('price updated') group['Listing-history'] = listing_history return group # 按Item分组处理,group_keys=False避免保留分组索引 df = df.groupby('Item', group_keys=False).apply(process_group) # 移除辅助列 df = df.drop('is_numeric', axis=1) print(df)
代码说明
- 标记数值型Price:通过
pd.to_numeric(..., errors='coerce').notna()生成is_numeric列,快速判断每个Price是否为可转换的数值型字符串。 - 分组处理逻辑:
- 分组内第一条记录直接标记为
first seen - 后续记录对比前一条的Price内容与类型:
- 内容完全一致则标记
ongoing listing - 数值转非数值时,生成
Price->[目标值]格式的标记 - 非数值转数值时,生成
[原值]->Price格式的标记 - 同类型但内容不同的场景(如两个不同非数值、两个不同数值),暂时标记为
price updated,可根据实际需求修改
- 内容完全一致则标记
- 分组内第一条记录直接标记为
- 分组应用:使用
groupby.apply将逻辑应用到每个Item分组,保证规则仅在同商品内生效。
运行结果
Time Price Item Listing-history 0 0 Auction Picasso first seen 1 1 Auction Picasso ongoing listing 2 2 800 Picasso Auction->Price 3 0 900 DaVinci first seen 4 1 By Negotiation DaVinci Price->By Negotiation 5 2 700 DaVinci By Negotiation->Price 6 2 250 Dali first seen 7 3 250 Dali ongoing listing 8 4 Make Offer Dali Price->Make Offer
内容的提问来源于stack exchange,提问作者doctorblaze
相关产品推荐
相关产品推荐

