Используйте столбцы 1 и 2 для заполнения столбца 3 - PullRequest
0 голосов
/ 11 декабря 2018

Я новичок в Python и имею следующий пандас-фрейм данных - я пытаюсь написать код, который заполняет столбец 'signal', как показано ниже:

Days    long_entry_flag long_exit_flag  signal
 1      FALSE           TRUE    
 2      FALSE           FALSE   
 3      TRUE            FALSE            1
 4      TRUE            FALSE            1
 5      FALSE           FALSE            1
 6      TRUE            FALSE            1
 7      TRUE            FALSE            1
 8      FALSE           TRUE    
 9      FALSE           TRUE    
 10     TRUE            FALSE            1
 11     TRUE            FALSE            1
 12     TRUE            FALSE            1
 13     FALSE           FALSE            1
 14     FALSE           TRUE    
 15     FALSE           FALSE   
 16     FALSE           TRUE    
 17     TRUE            FALSE            1
 18     TRUE            FALSE            1
 19     FALSE           FALSE            1
 20     FALSE           FALSE            1
 21     FALSE           TRUE    
 22     FALSE           FALSE
 23     FALSE           FALSE

Моя версия pseudo-code будетвыполните следующие шаги

  1. Просмотрите столбец ['long_entry_flag'], пока условие входа не станет True (изначально 3-й день)
  2. Затем мы вводим '1' в ['signal']столбец каждый день до тех пор, пока условие выхода не станет True ['long_exit_flag'] == True в 8-й день
  3. Затем мы возвращаемся к столбцу ['long_entry_flag'], чтобы дождаться следующего условия входа (происходит в 10-й день)
  4. И снова мы вводим '1' в столбец ['signal'] каждый день, пока условие выхода не станет True (день 14)
  5. и т. Д.

Приветственные идеи о путяхпо возможности быстро заполнить столбец «сигнал» (используя векторизацию?) - это подмножество большого кадра данных с десятками тысяч строк, и это один из многих кадров данных, которые анализируются последовательно.

Многиезаранее спасибо!

Ответы [ 4 ]

0 голосов
/ 11 декабря 2018

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

Шаг 1 : если long_exit_flag равен True, можно указать Np.nan or между long_entry_flag и long_exit_flag

df['d'] = np.where(df['long_exit_flag'], np.nan, df['long_entry_flag'] | df['long_exit_flag'])

Шаг 2: Теперь это состояние, в котором оба столбца false.Мы должны игнорировать это и заменить значения предыдущим состоянием.Что можно сделать с помощью where и select

df['new_signal']= np.where(df['d']==0, 
                  np.select([df['d'].shift()==0, df['d'].shift()==1],[1,1], np.nan),
                  df['d'])

    Days  long_entry_flag  long_exit_flag  signal    d  new_signal
0      1            False            True     NaN  NaN         NaN
1      2            False           False     NaN  0.0         NaN
2      3             True           False     1.0  1.0         1.0
3      4             True           False     1.0  1.0         1.0
4      5            False           False     1.0  0.0         1.0
5      6             True           False     1.0  1.0         1.0
6      7             True           False     1.0  1.0         1.0
7      8            False            True     NaN  NaN         NaN
8      9            False            True     NaN  NaN         NaN
9     10             True           False     1.0  1.0         1.0
10    11             True           False     1.0  1.0         1.0
11    12             True           False     1.0  1.0         1.0
12    13            False           False     1.0  0.0         1.0
13    14            False            True     NaN  NaN         NaN
14    15            False           False     NaN  0.0         NaN
15    16            False            True     NaN  NaN         NaN
16    17             True           False     1.0  1.0         1.0
17    18             True           False     1.0  1.0         1.0
18    19            False           False     1.0  0.0         1.0
19    20            False           False     1.0  0.0         1.0
20    21            False            True     NaN  NaN         NaN
0 голосов
/ 11 декабря 2018

Вы можете сделать

# Assuming we're starting from the "outside"
inside = False
for ix, row in df.iterrows():
    inside = (not row['long_exit_flag']
              if inside
              else row['long_entry_flag']
                  and not row['long_exit_flag']) # [True, True] case
    df.at[ix, 'signal'] = 1 if inside else np.nan

, который даст вам именно тот результат, который вы опубликовали.


Вдохновленный ответом @ jezrael , ясоздал чуть более производительную версию вышеупомянутого , все еще пытаясь сохранить его как можно более аккуратным:

# Same assumption of starting from the "outside"
df.at[0, 'signal'] = df.at[0, 'long_entry_flag']
for ix in df.index[1:]:
    df.at[ix, 'signal'] = (not df.at[ix, 'long_exit_flag']
                           if df.at[ix - 1, 'signal']
                           else df.at[ix, 'long_entry_flag']
                               and not df.at[ix, 'long_exit_flag']) # [True, True] case

# Adjust to match the requested output exactly
df['signal'] = df['signal'].replace([True, False], [1, np.nan])
0 голосов
/ 11 декабря 2018

Для повышения производительности используйте решение Numba:

arr = df[['long_exit_flag','long_entry_flag']].values

@jit
def f(A):
    inside = False
    out = np.ones(len(A), dtype=float)
    for i in range(len(arr)):
        inside = not A[i, 0] if inside else A[i, 1] 
        if not inside:
            out[i] = np.nan
    return out

df['signal'] = f(arr)

Производительность :

#[21000 rows x 5 columns]
df = pd.concat([df] * 1000, ignore_index=True)

In [189]: %%timeit
     ...: inside = False
     ...: for ix, row in df.iterrows():
     ...:     inside = not row['long_exit_flag'] if inside else row['long_entry_flag']
     ...:     df.at[ix, 'signal'] = 1 if inside else np.nan
     ...: 
1.58 s ± 9.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [190]: %%timeit
     ...: arr = df[['long_exit_flag','long_entry_flag']].values
     ...: 
     ...: @jit
     ...: def f(A):
     ...:     inside = False
     ...:     out = np.ones(len(A), dtype=float)
     ...:     for i in range(len(arr)):
     ...:         inside = not A[i, 0] if inside else A[i, 1] 
     ...:         if not inside:
     ...:             out[i] = np.nan
     ...:     return out
     ...: 
     ...: df['signal'] = f(arr)
     ...: 
171 ms ± 2.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [200]: %%timeit
     ...: df['d'] = np.where(~df['long_exit_flag'],df['long_entry_flag'] | df['long_exit_flag'],np.nan)
     ...: 
     ...: df['new_select']= np.where(df['d']==0, np.select([df['d'].shift()==0, df['d'].shift()==1],[1,1], np.nan), df['d'])
     ...: 
2.4 ms ± 561 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Вы также можете использовать numpy для сдвига, также код @Dark упрощает:

In [222]: %%timeit
     ...: d = np.where(~df['long_exit_flag'].values,  df['long_entry_flag'].values | df['long_exit_flag'].values, np.nan)
     ...: shifted = np.insert(d[:-1], 0, np.nan)
     ...: m = (shifted==0) | (shifted==1)
     ...: df['signal1']= np.select([d!=0, m], [d, 1], np.nan)
     ...: 
590 µs ± 35.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

РЕДАКТИРОВАТЬ:

Вы также можете проверить Есть ли у iterrows проблемы с производительностью? для общего порядка приоритета для выполнения различных операций в пандах.

0 голосов
/ 11 декабря 2018
#let the long_exit_flag equal to 0 when the exit is TRUE
df['long_exit_flag_r']=~df.long_exit_flag_r
df.temp=''

for i in range(1,len(df.index)):
    df.temp[i]=(df.signal[i-1]+df.long_entry_flag[i])*df.long_exit_flag_r

если температура положительная, то сигнал должен быть равен 1, если температура отрицательная, то сигнал должен быть пустым.(Я вроде застрял здесь)

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