Есть ли лучший способ (по производительности) для выполнения следующего цикла в пандах (если df
является DataFrame
)?
for i in range(len(df)):
if df['signal'].iloc[i] == 0: # if the signal is negative
if df['position'].iloc[i - 1] - 0.02 < -1: # if the row above - 0.1 < -1 set the value of current row to -1
df['position'].iloc[i] = -1
else: # if the new col value above -0.1 is > -1 then subtract 0.1 from that value
df['position'].iloc[i] = df['position'].iloc[i - 1] - 0.02
elif df['signal'].iloc[i] == 1: # if the signal is positive
if df['position'].iloc[i - 1] + 0.02 > 1: # if the value above + 0.1 > 1 set the current row to 1
df['position'].iloc[i] = 1
else: # if the row above + 0.1 < 1 then add 0.1 to the value of the current row
df['position'].iloc[i] = df['position'].iloc[i - 1] + 0.02
Я буду благодарен за любые советы, потому что я только начал идти по маршруту Пандаса и, очевидно, может пропустить что-то важное.
Исходные данные CSV:
Date,sp500,sp500 MA,UNRATE,UNRATE MA,signal,position
2000-01-01,,,4.0,4.191666666666665,1,0
2000-01-02,,,4.0,4.191666666666665,1,0
2000-01-03,102.93,95.02135,4.0,4.191666666666665,1,0
2000-01-04,98.91,95.0599,4.0,4.191666666666665,1,0
2000-01-05,99.08,95.11245000000001,4.0,4.191666666666665,1,0
2000-01-06,97.49,95.15450000000001,4.0,4.191666666666665,1,0
2000-01-07,103.15,95.21575000000001,4.0,4.191666666666665,1,0
2000-01-08,103.15,95.21575000000001,4.0,4.191666666666665,1,0
2000-01-09,103.15,95.21575000000001,4.0,4.191666666666665,1,0
Желаемый результат:
Date,sp500,sp500 MA,UNRATE,UNRATE MA,signal,position
2000-01-01,,,4.0,4.191666666666665,1,0.02
2000-01-02,,,4.0,4.191666666666665,1,0.04
2000-01-03,102.93,95.02135,4.0,4.191666666666665,1,0.06
2000-01-04,98.91,95.0599,4.0,4.191666666666665,1,0.08
2000-01-05,99.08,95.11245000000001,4.0,4.191666666666665,1,0.1
2000-01-06,97.49,95.15450000000001,4.0,4.191666666666665,1,0.12
2000-01-07,103.15,95.21575000000001,4.0,4.191666666666665,1,0.14
2000-01-08,103.15,95.21575000000001,4.0,4.191666666666665,1,0.16
2000-01-09,103.15,95.21575000000001,4.0,4.191666666666665,1,0.18
Обновить Все ответы ниже (к моменту написания этого) дают значение постоянной position
0.02, которое отличается от моего метода наивного цикла. Другими словами, я ищу решение, которое дало бы 0.02
, 0.04
, 0.06
, 0.08
т.д. Для столбца position
.
Спасибо, что добавили данные и примеры. Во-первых, я уверен, что вы не можете векторизовать это, поскольку каждый расчет зависит от результата предыдущего. Так что это лучшее, что я смог сделать.
Ваш метод пришел около 0.116999
секунд на моей машине
Это произошло около 0.0039999
секунд
Не векторизован, но он получает хорошее увеличение скорости, так как быстрее использовать список для этого и добавить его обратно в кадр данных в конце.
def myfunc(pos_pre, signal):
if signal == 0: # if the signal is negative
# if the new col value above -0.2 is > -1 then subtract 0.2 from that value
pos = pos_pre - 0.02
if pos < -1: # if the row above - 0.2 < -1 set the value of current row to -1
pos = -1
elif signal == 1:
# if the row above + 0.2 < 1 then add 0.2 to the value of the current row
pos = pos_pre + 0.02
if pos > 1: # if the value above + 0.1 > 1 set the current row to 1
pos = 1
return pos
''' set first position value because you aren't technically calculating it correctly in your method since there is no
position minus 1... IE: it will always be 0.02'''
new_pos = [0.02]
# skip index zero since there is no position 0 minus 1
for i in range(1, len(df)):
new_pos.append(myfunc(pos_pre=new_pos[i-1], signal=df['signal'].iloc[i]))
df['position'] = new_pos
Выход:
df.position
0 0.02
1 0.04
2 0.06
3 0.08
4 0.10
5 0.12
6 0.14
7 0.16
8 0.18
Не используйте петлю. Pandas специализируется на векторизованных операциях, например, для signal == 0
:
pos_shift = df['position'].shift() - 0.02
m1 = df['signal'] == 0
m2 = pos_shift < -1
df.loc[m1 & m2, 'position'] = -1
df['position'] = np.where(m1 & ~m2, pos_shift, df['position'])
Вы можете написать что-то подобное для signal == 1
.
Есть, скорее всего, лучшие способы, но этот тоже должен работать:
df['previous'] = df.signal.shift()
def get_signal_value(row):
if row.signal == 0:
compare = row.previous - 0.02
if compare < -1:
return -1
else:
return compare
elif row.signal == 1:
compare = row.previous + 0.01
if compare > 1:
return 1
else:
return compare
df['new_signal'] = df.apply(lambda row: get_signal_value(row), axis=1)
Ага. При поиске производительности вы всегда должны работать с базовыми массивами numpy:
signal = df['signal'].values
position = df['position'].values
for i in range(len(df)):
if signal[i] == 0:
if position[i-1]-0.02 < -1:
position[i] = -1
else:
position[i] = position[i-1]-0.02
elif signal[i] == 1:
if position[i-1]+0.02 > 1:
position[i] = 1
else:
position[i] = position[i-1]+0.02
Вы будете удивлены приростом производительности, часто в 10 раз и более.