Страницы

Поиск по вопросам

понедельник, 30 декабря 2019 г.

Работа с apply и transform в Pandas

#python #pandas #dataframe


В продолжение вопроса . Теперь пробую осветить проблему максимально широко. 


Исходный датафрейм:


                                               price   qty  side  status                 
 tradeID                date
 71ZNeXwSQUqkxhKR9trvrQ 2018-09-03 17:00:00  7282.5  10.0   Buy  filled 
                        2018-09-05 11:00:00  7111.0  10.0  Sell  filled  
 WYgKLRv+Q9CuXic4FNEh0A 2018-09-08 10:00:00  6448.0  10.0   Buy  filled  
                        2018-09-08 18:00:00  6377.0  10.0  Sell  filled  
 /6WmcfJ1QcWWwPcwkXeoSw 2018-09-09 14:00:00  6376.5  10.0   Buy  filled   


Я хочу применять различные формулы (например подсчет прибыли и пр.) и заполнять исходный
датафрейм. Группируя обрабатываемые данные с помощью groupby('tradeID'). Например для
подсчета профита были предложены два отличных варианта.


1 Вариант (от @MaxU):



    In [277]: df['profit'] = (df.sort_values(['tradeID','date', 'side'])
                                .groupby('tradeID')
                                ['price'].diff())

    In [278]: df
    Out[278]:
                                                 price   qty  side  status  profit
    tradeID                date
    71ZNeXwSQUqkxhKR9trvrQ 2018-09-03 17:00:00  7282.5  10.0   Buy  filled     NaN
                           2018-09-05 11:00:00  7111.0  10.0  Sell  filled  -171.5
    WYgKLRv+Q9CuXic4FNEh0A 2018-09-08 10:00:00  6448.0  10.0   Buy  filled     NaN
                           2018-09-08 18:00:00  6377.0  10.0  Sell  filled   -71.0
    /6WmcfJ1QcWWwPcwkXeoSw 2018-09-09 14:00:00  6376.5  10.0   Buy  filled     NaN




2 Вариант(От @strawdog):



    df['profit']= df.groupby(level=[0]).transform(lambda x: x.shift(-1) - x)['price']
    print(df)

                                                 price   qty  side  status  profit
    71ZNeXwSQUqkxhKR9trvrQ 2018-09-03 17:00:00  7282.5  10.0   Buy  filled  -171.5
                           2018-09-05 11:00:00  7111.0  10.0  Sell  filled     NaN
    WYgKLRv+Q9CuXic4FNEh0A 2018-09-08 10:00:00  6448.0  10.0   Buy  filled   -71.0
                           2018-09-08 18:00:00  6377.0  10.0  Sell  filled     NaN



Но такие способы не подходят, если мы имеем несколько строк, где side==Sell(или Buy).
Я не понимаю как использовать выбор по значению столбца в таких условиях. Я пробовал,
что то вроде self.df['profit'] = df.sort_values(['tradeID','date', 'side'])['price'].groupby('tradeID').apply(lambda
x: x['price'][x['side']=='Sell'].mean() - x['price'][x['side']=='Buy'].mean()) Но получаю
те или иные ошибки при подобных попытках, из-за не понимая принципа работы.

Вопрос: Как провести подсчет прибыли внутри каждого tradeID на примере усложненного
датафрейма (среднее арифметическое по Sell минус среднее арифметическое по Buy)? Столбцами
qty, status, date можно пренебречь.


Усложненный(новый) датафрейм:


                                                 price   qty  side  status      
          
     tradeID                date
     71ZNeXwSQUqkxhKR9trvrQ 2018-09-03 17:00:00  7282.5  10.0   Buy  filled 
                            2018-09-05 11:00:00  7111.0  5.0  Sell  filled
                            2018-09-05 11:30:00  7200.0  5.0  Sell  filled
     WYgKLRv+Q9CuXic4FNEh0A 2018-09-08 10:00:00  6448.0  5.0   Buy  filled 
                            2018-09-08 10:02:00  6460.0  5.0   Buy  filled
                            2018-09-08 18:00:00  6500.0  10.0  Sell  filled   

    


Ответы

Ответ 1



Если я правильно понял вопрос, то нужно сначала сгруппировать датафрейм по столбцам "tradeID" и "side" и применить mean() к столбцу "price": (df.groupby(['tradeID', 'side']) ['price'].mean()) Получим: tradeID side 71ZNeXwSQUqkxhKR9trvrQ Buy 7282.5 Sell 7155.5 WYgKLRv+Q9CuXic4FNEh0A Buy 6454.0 Sell 6500.0 И далее нужно сгруппировать этот результат по "tradeID" и посчитать разницу. С использованием предыдущего куска кода: (df.groupby(['tradeID', 'side']) ['price'].mean() .groupby('tradeID').diff() .to_frame() .rename(columns={'price': 'profit'})) Результат: profit tradeID side 71ZNeXwSQUqkxhKR9trvrQ Buy NaN Sell -127.0 WYgKLRv+Q9CuXic4FNEh0A Buy NaN Sell 46.0

Ответ 2



Решение в стиле "PIVOT": res = (df.pivot_table(index='tradeID', columns='side', values='price', aggfunc='mean') .eval("profit = Sell - Buy")) Результат: In [397]: res Out[397]: side Buy Sell profit tradeID 71ZNeXwSQUqkxhKR9trvrQ 7282.5 7155.5 -127.0 WYgKLRv+Q9CuXic4FNEh0A 6454.0 6500.0 46.0

Ответ 3



Если честно, то вариант моего коллеги мне больше нравится из-за своей наглядности. в вашем случае я бы предложил сделать так: 1 - меняем знак поля price в зависимости от значения side df.loc[df["side"] == 'Sell', 'price'] *= -1 price qty side status tradeID date 71ZNeXwSQUqkxhKR9trvrQ 2018-09-03 17:00:00 7282.5 10.0 Buy filled 2018-09-05 11:00:00 -7111.0 10.0 Sell filled 2018-09-05 11:00:00 -7050.0 10.0 Sell filled WYgKLRv+Q9CuXic4FNEh0A 2018-09-08 10:00:00 6448.0 10.0 Buy filled 2018-09-08 18:00:00 -6377.0 10.0 Sell filled 2018-09-08 18:00:00 -6200.0 10.0 Sell filled 2 - делаем кумулятивное суммирование по группам: df['profit'] = (df.sort_values(['tradeID','date', 'side']) .groupby('tradeID') ['price'].cumsum()) price qty side status profit tradeID date 71ZNeXwSQUqkxhKR9trvrQ 2018-09-03 17:00:00 7282.5 10.0 Buy filled 7282.5 2018-09-05 11:00:00 -7111.0 10.0 Sell filled 171.5 2018-09-05 11:00:00 -7050.0 10.0 Sell filled -6878.5 WYgKLRv+Q9CuXic4FNEh0A 2018-09-08 10:00:00 6448.0 10.0 Buy filled 6448.0 2018-09-08 18:00:00 -6377.0 10.0 Sell filled 71.0 2018-09-08 18:00:00 -6200.0 10.0 Sell filled -6129.0 Update забыл добавить - финальные значения по tradeID можно получить потом так: df.groupby('tradeID').nth(-1)["profit"] tradeID 71ZNeXwSQUqkxhKR9trvrQ -6878.5 WYgKLRv+Q9CuXic4FNEh0A -6129.0 Name: profit, dtype: float64

Комментариев нет:

Отправить комментарий