Ячейка кадра данных должна быть заблокирована и использована для расчета текущего остатка, условного результата для другой ячейки в той же строке - PullRequest
6 голосов
/ 28 февраля 2020

Скажем, у меня есть следующий фрейм данных:

import pandas as pd
df = pd.DataFrame()

df['A'] = ('1/05/2019','2/05/2019','3/05/2019','4/05/2019','5/05/2019','6/05/2019','7/05/2019','8/05/2019','9/05/2019','10/05/2019','11/05/2019','12/05/2019','13/05/2019','14/05/2019','15/05/2019','16/05/2019','17/05/2019','18/05/2019','19/05/2019','20/05/2019')

df['B'] = ('SIT','SCLOSE', 'SHODL', 'SHODL', 'SHODL', 'SHODL', 'SHODL', 'SELL','SIT','SIT','BCLOSE', 'BHODL', 'BHODL', 'BHODL', 'BHODL', 'BHODL', 'BHODL','BUY','SIT','SIT')

df['C'] = (0.00,1.00,10.00, 5.00,6.00,-6.00, 6.00, 0.00,0.00,0.00,-8.00,33.00,-15.00,6.00,-1.00,5.00,10.00,0.00,0.00,0.00)

df.loc[19, 'D'] = 100.0000

Как видно, я начинаю столбец D со 100 в последней строке.

Я пытаюсь закодировать вычисление для столбца D, поэтому начиная с нижней строки (строка 19), когда в столбце B показано ПОКУПКА или ПРОДАЖА, число в столбце D блокируется (например, 100) и используется для расчета на основе col C для каждого SHODL или BHODL до тех пор, пока не появится строка после BCLOSE или SCLOSE.

Заблокированное число используется для расчета текущего баланса на основе процентов в столбце C. Как видно из строки 16, в столбце C указано «10», представляющее 10%. Если 10% от 100 = 10, то новый текущий баланс равен 110.

В столбце строки 15 C содержится 5%, так как 5 добавляется к текущему балансу, в результате чего получается 115.

Следующая строка 14 столбца C имеет изменение на -1%, так как 1% от 100 равен = 1, и поэтому новый текущий баланс равен 114 и т. д.

Ниже приведены результаты, которые должны быть возвращены в столбец D кадра данных после запуска правильного кода

df['D'] = ('158.60','158.60', '157.30', '144.30', '137.80', '130.00', '137.80', '130.00','130.00','130.00','130.00', '138.00', '105.00', '120.00', '114.00', '115.00', '110.00','100.00','100.00','100.00')

Это продолжается до тех пор, пока после SCLOSE или BCLOSE не будет показано, как строка BCLOSE или SCLOSE является последней строкой, в которой вычисляется текущий баланс.

Как видите, этот процесс перезапускается, когда отображается новая покупка или продажа.

Ответы [ 3 ]

2 голосов
/ 03 марта 2020

Следующее начальное значение зависит от последнего значения предыдущей группы, поэтому я думаю, что его нельзя векторизовать. Это требует некоторого итеративного процесса. Я придумал решение, делающее итеративно по групповым группам. Обратный df и присвоить df1. Работа над каждой группой df1 и присвоение окончательного списка групп исходному df

df1 = df[::-1]
s = df1.B.isin(['BCLOSE','SCLOSE']).shift(fill_value=False).cumsum()
grps = df1.groupby(s)
init_val= 100
l = []
for _, grp in grps:
    s = grp.C * 0.01 * init_val
    s.iloc[0] = init_val
    s = s.cumsum()
    init_val = s.iloc[-1]
    l.append(s)

df['D'] = pd.concat(l)

Out[50]:
             A       B     C      D
0    1/05/2019     SIT   0.0  158.6
1    2/05/2019  SCLOSE   1.0  158.6
2    3/05/2019   SHODL  10.0  157.3
3    4/05/2019   SHODL   5.0  144.3
4    5/05/2019   SHODL   6.0  137.8
5    6/05/2019   SHODL  -6.0  130.0
6    7/05/2019   SHODL   6.0  137.8
7    8/05/2019    SELL   0.0  130.0
8    9/05/2019     SIT   0.0  130.0
9   10/05/2019     SIT   0.0  130.0
10  11/05/2019  BCLOSE  -8.0  130.0
11  12/05/2019   BHODL  33.0  138.0
12  13/05/2019   BHODL -15.0  105.0
13  14/05/2019   BHODL   6.0  120.0
14  15/05/2019   BHODL  -1.0  114.0
15  16/05/2019   BHODL   5.0  115.0
16  17/05/2019   BHODL  10.0  110.0
17  18/05/2019     BUY   0.0  100.0
18  19/05/2019     SIT   0.0  100.0
19  20/05/2019     SIT   0.0  100.0        
0 голосов
/ 02 марта 2020

Эта часть ниже должна помочь вам. Он выдает ожидаемый результат и является относительно быстрым, потому что избегает прямой итерации строк данных.

endpoints = [df.first_valid_index(), df.last_valid_index()]
# occurrences of 'BCLOSE' or 'SCLOSE'
breakpoints = df.index[(df.B =='BCLOSE') | (df.B == 'SCLOSE')][::-1]
# remove the endpoints of the dataframe that do not break the structure
breakpoints = breakpoints.drop(endpoints, errors='ignore')

PERCENTAGE_CONST = 100
top = 100  # you can specify any initial value here

for i in range(len(breakpoints) + 1):

    prv = breakpoints[i - 1] - 1 if i else -1  # previous or first breakpoint
    try:
        nex = breakpoints[i] - 1  # next breakpoint
    except IndexError:
        nex = None  # last breakpoint

    # cumulative sum of appended to 'D' column
    res = top + (df['C'][prv: nex: -1] * top / PERCENTAGE_CONST).cumsum()[::-1]
    df.loc[res.index, 'D'] = res

    # saving the value that will be the basis for percentage calculations
    # for the next breakpoint
    top = res.iloc[0]
0 голосов
/ 28 февраля 2020

Я думаю, что есть более оптимизированный и pythoni c способ решить эту проблему. Но решение с итерацией:

df['D'] = pd.to_numeric(df['D'])
df['C'] = pd.to_numeric(df['C'])
D_val = None
for i in range(len(df)-1, 0, -1):
    if df.loc[i, 'B'] == 'BUY':
        D_val = df.loc[i, 'D']
        continue
    if D_val is None:
        continue
    df.loc[i, 'D'] = df.loc[i+1, 'D'] + (D_val * df.loc[i, 'C']/100)

Каждый раз, когда вы сталкиваетесь с BUY в column D, вы обновляете D_val. У нас также может быть условие остановки, как указано в OP, например SCLOSE или BCLOSE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...