найти минимальное значение столбца между двумя вхождениями значения в другом столбце во фрейме данных Python - PullRequest
0 голосов
/ 02 мая 2019

У меня есть данные по ценам на акции, содержащие цены открытия, максимума, минимума, закрытия на ежедневной основе.Я создаю новый столбец «сигнал», который будет принимать значения «сигнал» или «нет» на основе некоторых условий.

Каждый раз df['signal']=="signal", мы должны сравнить его с предыдущими 3 вхождениямиdf['signal']=="signal".давайте представим, что текущее явление является 4-м сигналом.Итак, предыдущее вхождение df['signal']=="signal" будет 3-м сигналом, четное предыдущее будет 2-м сигналом, а предыдущий сигнал будет первым сигналом.

Мне нужно проверить, является ли минимальное значение df['low'] между сигналом 4 и сигналом 3 больше, чем минимальное значение df ['low'] между сигналом 1 и сигналом 2.

Если оно больше, мне нужен новый столбец df ['trade '] == "Buy".

Sample data

No Open High Low Close signal Trade 

1   75   95   65  50    signal
2   78   94   74  77    none
3   83   91   81  84    none
4   91   101  88  93    signal
5   104  121  95  103   none
6   101  111  99  105   none
7   97   108  95  101   signal
8   103  113  102 106   none
9   108  128  105 114   signal  BUY
10  104  114  99  102   none
11  110  130  105 115   signal  BUY
12  112  122  110 115   none
13  118  145  112 123   none
14  123  143  71  133   signal  NONE
15  130  150  120 140   none

В приведенных выше примерах данных в строке № 9 происходит df [' Trade '] == "BUY", поскольку минимальное значение df [' Low'] = 95 между этим df [' signal '] = "signal" и предыдущим df [' signal '] = "signal" БОЛЬШЕ, чем минимальное значение df [' Low '] = 65 между двумя предыдущими вхождениями df['signal'] = "signal".

Аналогично, в строке № 14 df ['Trade'] = "None" произошло, поскольку минимальное значение df ['Low'] = 71 между этим сигналоми предыдущий сигнал НЕ БОЛЬШЕ, чем минимальное значение df ['Low'] = 99 между двумя предыдущими сигналами.

Мне нужна помощь с кодом для реализации этого,


    import pandas as pd
    import numpy as np
    import bisect as bs

    df = pd.read_csv("Nifty.csv")
    cols = ['No', 'Low', 'signal']
    df['5EMA'] = df['Close'].ewm(span=5).mean()
    df['10EMA'] = df['Close'].ewm(span=10).mean()
    condition1 = df['5EMA'].shift(1) < df['10EMA'].shift(1)
    condition2 = df['5EMA'] > df['10EMA']
    df['signal'] = np.where(condition1 & condition2, 'signal', None)
    df1 = pd.concat([df[cols], df.loc[df.signal=='signal',cols].assign(signal='temp')]) \
            .sort_values(['No', 'signal'],ascending=[1,0])
    df1['g'] = (df1.signal == 'signal').cumsum()
    df1['Low_min'] = df1.groupby('g').Low.transform('min')
    s = df1.groupby('g').Low.min()
    buy = s[s.shift(1) > s.shift(3)].index.tolist()
    m1 = df1.signal.eq('signal') & df1.g.gt(3)
    m2 = df1.g.isin(buy) & m1
    df1['trade'] = np.select([m2, m1], ['Buy', 'None'], '')
    df['trade'] = ''
    df.trade.update(df1.loc[df1.signal=='signal',"trade"])
    print(df)

1 Ответ

1 голос
/ 06 мая 2019

Ваша проблема может быть упрощена после добавления дополнительных временных строк.Я установил новый фрейм данных, который содержит только обязательные поля из исходного df , и клонировал все строки, помеченные как «сигнал», но переименованные в «temp» df.loc[df.signal=='signal',cols].assign(signal='temp').Сортированные строки будут сгруппированы с помощью «signal» и cumsum ().см. ниже код:

str="""No Open High Low Close signal 
1   75   95   65  50    signal 
2   78   94   74  77    none 
3   83   91   81  84    none 
4   91   101  88  93    signal 
5   104  121  95  103   none 
6   101  111  99  105   none 
7   97   108  95  101   signal 
8   103  113  102 106   none 
9   108  128  105 114   signal 
10  104  114  99  102   none 
11  110  130  105 115   signal 
12  112  122  110 115   none 
13  118  145  112 123   none 
14  123  143  71  133   signal 
15  130  150  120 140   none"""

df = pd.read_csv(pd.io.common.StringIO(str), sep='\s+')

# cols which are used in this task 
cols = ['No', 'Low', 'signal']

# create a new dataframe, cloned all 'signal' rows but rename signal to 'temp', sort the rows
df1 = pd.concat([df[cols], df.loc[df.signal=='signal',cols].assign(signal='temp')]) \
        .sort_values(['No', 'signal'],ascending=[1,0])

# set up group-number with cumsum() and get min() value from each group
df1['g'] = (df1.signal == 'signal').cumsum()
# the following field just for reference, no need for calculation
df1['Low_min'] = df1.groupby('g').Low.transform('min')

Новый кадр данных df1 будет выглядеть следующим образом.За исключением первой и последней группы, теперь каждая группа начинается с «сигнала» и заканчивается «темпом» (который также является «сигналом»):

enter image description here

Исходя из вашего описания, для строки № 9 (желтый фон, первый элемент в df1.g == 4) мы можем проверить df1.loc[df1.g==3, "Low_min"] (с красной рамкой) против df1.loc[df1.g==1, "Low_min"] (с зеленой рамкой)

если у нас есть следующее:

s = df1.groupby('g').Low.min()

список группы покупок должен удовлетворять s.shift (1)> s.shift (3)

buy = s[s.shift(1) > s.shift(3)].index.tolist()

Итак, давайте установимусловия:

# m1: row marked with signal
# skip the first 3 groups which do not have enough signals
m1 = df1.signal.eq('signal') & df1.g.gt(3)

# m2: m1 plus must in buy list
m2 = df1.g.isin(buy) & m1
df1['trade'] = np.select([m2, m1], ['Buy', 'None'], '')
#In [36]: df1
#Out[36]: 
#    No  Low  signal  g  Low_min trade
#0    1   65    temp  0       65      
#0    1   65  signal  1       65      
#1    2   74    none  1       65      
#2    3   81    none  1       65      
#3    4   88    temp  1       65      
#3    4   88  signal  2       88      
#4    5   95    none  2       88      
#5    6   99    none  2       88      
#6    7   95    temp  2       88      
#6    7   95  signal  3       95      
#7    8  102    none  3       95      
#8    9  105    temp  3       95      
#8    9  105  signal  4       99   Buy
#9   10   99    none  4       99      
#10  11  105    temp  4       99      
#10  11  105  signal  5       71   Buy
#11  12  110    none  5       71      
#12  13  112    none  5       71      
#13  14   71    temp  5       71      
#13  14   71  signal  6       71  None
#14  15  120    none  6       71      

После того, как у нас есть df1.trade, мы можем обновить исходный фрейм данных:

# set up column `trade` with EMPTY as default and update 
# the field based on df1.trade (using the index)
df['trade'] = ''
df.trade.update(df1.loc[df1.signal=='signal',"trade"])
...