如何用Python Pandas高效识别客户持仓的买卖交易?
用Pandas高效识别客户股票持仓的买入与卖出行为
需求说明
处理包含多日期客户股票持仓的数据集,需标记两类行为:
- 买入:股票在当前日期的客户持仓中出现,但在上一期持仓中未出现
- 卖出:股票在当前日期的客户持仓中存在,但在下一期持仓中消失
示例数据
import pandas as pd df = pd.DataFrame({ 'Date_ID': [1,1,1,1,2,2,2,2,2,2,3,3,3,3], 'Person': ['a', 'a', 'b', 'b', 'a', 'a', 'a', 'a', 'b', 'b', 'a', 'a', 'a', 'b'], 'Stock': ['x1', 'x2', 'x2', 'x3', 'x1', 'x2', 'x3', 'x4', 'x2', 'x3', 'x1', 'x2', 'x3', 'x3'] })
高效实现方案
避免循环,利用Pandas的groupby、shift和集合操作实现:
排序数据
先按客户和日期排序,确保时间顺序正确,这是后续对比的基础:df = df.sort_values(['Person', 'Date_ID']).reset_index(drop=True)生成客户-日期的持仓股票集合
将每个客户在每个日期的持仓股票转为集合,方便快速判断股票是否存在:person_date_stocks = df.groupby(['Person', 'Date_ID'])['Stock'].apply(set).reset_index(name='Stock_Set')获取上下期持仓集合
通过groupby+shift,为每个客户的当前日期匹配上一期和下一期的持仓集合:person_date_stocks['Prev_Stock_Set'] = person_date_stocks.groupby('Person')['Stock_Set'].shift(1) person_date_stocks['Next_Stock_Set'] = person_date_stocks.groupby('Person')['Stock_Set'].shift(-1)合并回原数据集
将上下期持仓集合关联到每条持仓记录:df = df.merge(person_date_stocks, on=['Person', 'Date_ID'])标记买入行为
客户第一期的所有持仓默认是买入;非第一期则判断当前股票是否不在上期持仓集合中:df['bought'] = df.apply( lambda row: row['Stock'] not in row['Prev_Stock_Set'] if pd.notna(row['Prev_Stock_Set']) else True, axis=1 )标记卖出行为
客户最后一期的持仓没有下期,所以不标记卖出;非最后一期则判断当前股票是否不在下期持仓集合中:df['sell'] = df.apply( lambda row: row['Stock'] not in row['Next_Stock_Set'] if pd.notna(row['Next_Stock_Set']) else False, axis=1 )清理中间列(可选)
删除用于计算的临时列,保留核心数据:df = df.drop(['Stock_Set', 'Prev_Stock_Set', 'Next_Stock_Set'], axis=1)
最终结果示例
执行后得到的数据集如下:
| Date_ID | Person | Stock | bought | sell | |
|---|---|---|---|---|---|
| 0 | 1 | a | x1 | True | False |
| 1 | 1 | a | x2 | True | False |
| 2 | 2 | a | x1 | False | False |
| 3 | 2 | a | x2 | False | False |
| 4 | 2 | a | x3 | True | False |
| 5 | 2 | a | x4 | True | True |
| 6 | 3 | a | x1 | False | False |
| 7 | 3 | a | x2 | False | False |
| 8 | 3 | a | x3 | False | False |
| 9 | 1 | b | x2 | True | False |
| 10 | 1 | b | x3 | True | False |
| 11 | 2 | b | x2 | False | True |
| 12 | 2 | b | x3 | False | False |
| 13 | 3 | b | x3 | False | False |
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

