将行分组到一个新的 Pandas DataFrame 中,每组一行
Posted
技术标签:
【中文标题】将行分组到一个新的 Pandas DataFrame 中,每组一行【英文标题】:Grouping rows into a new Pandas DataFrame with one row per group 【发布时间】:2022-01-21 02:50:32 【问题描述】:我有以下数据框:
from datetime import datetime as dt
import numpy as np
import pandas as pd
inputs =
'indicator':[69.88, 85.05, 50.19, 71.08, 44.83, 36.32, 29.42, 44.47, 34.71, 37.91, 32.78, 35.85, 38.98, 23.16, 73.22, 77.77, 49.22, 59.1, 83.38, 88.5, 47.78],
'short_trade':[0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0],
'pnl':[-0.0, -0.0, 0.05, -0.06, 0.05, 0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0, -0.0, 0.0, -0.0, -0.0, 0.01, -0.0, -0.0, -0.01, 0.03]
_idx = pd.date_range('2018-08-10','2018-09-09',freq='D').to_series()
_idx = _idx[_idx.dt.dayofweek < 5]
data = pd.DataFrame(inputs, index = _idx)
我的目标是创建一个新的 DataFrame,如下面的屏幕截图所示。在short_trade != 0
或pnl != 0
时进行分组(相同)。
新 DataFrame (trade_n
) 的第一列只是每个不同交易的 ID。新列 pnl
是初始 DataFrame 中每个组的总和。最后,duration (D)
是每笔交易持续的天数。
我找到了一种解决方法,循环遍历 DataFrame 并检查每一行,但我很确定使用 pandas/numpy 有一个更有效的解决方案。
【问题讨论】:
您选择的逻辑是什么?因为您首先选择 3 行,然后选择 1,然后选择 2 您要选择 pnl != 0 的行吗?加上之前的 0 是的,或者 short_trade != 0,都是一样的。将编辑Q,谢谢指出 在您的预期输出中,第二行的pnl
是0.01
- 不应该是0.005
,因为这是0.00
和0.01
的平均值,或者应该是它被夹在0.01
。
@richardec 这只是short_trade == 1
时我会得到的价格(回报)的pct_change()
。稍后我使用该列进行cumsum()
,然后获得总 PnL。我没有在此处发布该部分,因为我想让它尽可能简单(出于同样的目的,我还对 pnl
和 indicator
数字进行了四舍五入)
【参考方案1】:
试试这个:
s = df \
.groupby((df['short_trade'].astype(bool) | df['short_trade'].shift(1)).diff().cumsum()) \
.apply(lambda x: [x.shape[0] - 1, x['pnl'].tolist()]) \
[::2] \
.reset_index(drop=True) \
.tolist()
df = pd.DataFrame(s, columns=['duration (D)', 'pnl']) \
.reset_index() \
.rename('index': 'trade_n', axis=1)
输出:
>>> df
trade_n duration (D) pnl
0 0 3 [-0.0, 0.05, -0.06, 0.05]
1 1 1 [-0.0, 0.01]
2 2 2 [-0.0, -0.01, 0.03]
【讨论】:
应该过滤您在pnl
中的列表,并且您必须只保留只有pnl
不为0 的值并对其应用总和。
@Corralien,我将pnl
作为列表留下,因为 OP 在 cmets 中说输出 df 中 pnl
的值来自 pct_change
,所以我怀疑它可能是最好只保留原始值。【参考方案2】:
IIUC:
m = df.short_trade.ne(0) | df.pnl.ne(0)
g = mask.eq(True) & mask.shift().eq(False)
out = df.assign(trade_n=g.cumsum().sub(1)[m]).groupby('trade_n') \
.agg(**'pnl': ('pnl', lambda x: sum(x[x.ne(0)])),
'duration (D)': ('short_trade', lambda x: len(x.ne(0)))) \
.reset_index().astype('trade_n': int)
输出:
>>> out
trade_n pnl duration (D)
0 0 0.04 4
1 1 0.01 2
2 2 0.02 3
【讨论】:
以上是关于将行分组到一个新的 Pandas DataFrame 中,每组一行的主要内容,如果未能解决你的问题,请参考以下文章
pandas一些基本操作(DataFram和Series)_1
pandas一些基本操作(DataFram和Series)_2
pandas一些基本操作(DataFram和Series)_3