我有一个数据框,有两列:数量和价格。
df = pd.DataFrame([
[ 1, 5],
[-1, 6],
[ 2, 3],
[-1, 2],
[-1, 4],
[ 1, 2],
[ 1, 3],
[ 1, 4],
[-2, 5]], columns=['quantity', 'price'])
df['amount'] = df['quantity'] * df['price']
df['cum_qty'] = df['quantity'].cumsum()
我添加了两列金额和cum_qty(累计数量)。现在数据框如下所示(正数量代表买入,负数量代表卖出):
quantity price amount cum_qty
0 1 5 5 1
1 -1 6 -6 0
2 2 3 6 2
3 -1 2 -2 1
4 -1 4 -4 0
5 1 2 2 1
6 1 3 3 2
7 1 4 4 3
8 -2 5 -10 1
我想计算平均购买价格。
每次当cum_qty=0时,数量和数量都应该重置为零。所以我们看到索引=[5,6,7]的行。对于每一行,一件商品以2,3和4的价格购买,这意味着我有3件库存,每件平均价格为3[(2 3 4)/3]。
在指数=8卖出后(卖出交易不会改变买入价格),我将在价格3时各有一个。
所以,基本上,我必须将所有累积购买量除以上次不为零的累积量。
如何计算与熊猫DataFrame的所有交易的手头购买?
这是使用循环的不同解决方案:
import pandas as pd
import numpy as np
# Original data
df = pd.DataFrame({
'quantity': [ 1, -1, 2, -1, -1, 1, 1, 1, -2],
'price': [5, 6, 3, 2, 4, 2, 3, 4, 5]
})
# Process the data and add the new columns
df['amount'] = df['quantity'] * df['price']
df['cum_qty'] = df['quantity'].cumsum()
df['prev_cum_qty'] = df['cum_qty'].shift(1, fill_value=0)
df['average_price'] = np.nan
for i, row in df.iterrows():
if row['quantity'] > 0:
df.iloc[i, df.columns == 'average_price' ] = (
row['amount'] +
df['average_price'].shift(1, fill_value=df['price'][0])[i] *
df['prev_cum_qty'][i]
)/df['cum_qty'][i]
else:
df.iloc[i, df.columns == 'average_price' ] = df['average_price'][i-1]
df.drop('prev_cum_qty', axis=1)
这种方法的一个优点是,如果在cum_qty
归零之前有新的购买,它也会起作用。例如,假设有一个新的购买5的价格为3,即在处理数据之前运行以下行:
# Add more data, exemplifying a different situation
df = df.append({'quantity': 5, 'price': 3}, ignore_index=True)
我预计会有以下结果:
quantity price amount cum_qty average_price
0 1 5 5 1 5.0
1 -1 6 -6 0 5.0
2 2 3 6 2 3.0
3 -1 2 -2 1 3.0
4 -1 4 -4 0 3.0
5 1 2 2 1 2.0
6 1 3 3 2 2.5
7 1 4 4 3 3.0
8 -2 5 -10 1 3.0
9 5 3 15 6 3.0 # Not 4.0
也就是说,由于以前还是有1件商品是以3的价格买的,所以cum_qty
现在是6,均价还是3。
根据我的理解,你需要每个交易圈的买入价,那么你可以试试这个。
df['new_index'] = df.cum_qty.eq(0).shift().cumsum().fillna(0.)#give back the group id for each trading circle.*
df=df.loc[df.quantity>0]# kick out the selling action
df.groupby('new_index').apply(lambda x:(x.amount.sum()/x.quantity.sum()))
new_index
0.0 5.0# 1st ave price 5
1.0 3.0# 2nd ave price 3
2.0 3.0# 3nd ave price 3 ps: this circle no end , your position still pos 1
dtype: float64
EDIT1为您提供额外要求
DF=df.groupby('new_index',as_index=False).apply(lambda x : x.amount.cumsum()/ x.cum_qty).reset_index()
DF.columns=['Index','AvePrice']
DF.index=DF.level_1
DF.drop(['level_0', 'level_1'],axis=1,inplace=True)
pd.concat([df,DF],axis=1)
Out[572]:
quantity price amount cum_qty new_index 0
level_1
0 1 5 5 1 0.0 5.0
2 2 3 6 2 1.0 3.0
5 1 2 2 1 2.0 2.0
6 1 3 3 2 2.0 2.5
7 1 4 4 3 2.0 3.0
df[df['cum_qty'].map(lambda x: x == 0)].index
会告诉你哪些行的cum_qty为0
df[df['cum_qty'].map(lambda x: x == 0)].index.max()
给你最后一行0cum_qty
start = df[df['cum_qty'].map(lambda x: x == 0)].index.max() + 1
end = len(df) - 1
为您提供开始和结束行号,即您所指的范围
df['price'][start:end].sum() / df['quantity'][start:end].sum()
给出了你在你给出的例子中所做的答案
如果您想知道每次出现cum_qty0时的这个值,那么您可以通过使用each的索引(我的第一行代码的结果)来应用开始/结束逻辑。