Как векторизовать этот цикл Python, включающий миллионы записей - PullRequest
0 голосов
/ 05 июня 2018

У меня есть pandas dataframe, df, с 4 000 000 временных шагов для одной акции.

Задача состоит в том, чтобы для каждого временного шага я хотел определить, растет ли он сначала на 0,1% или падает на 0,1%.Итак, сейчас я конвертирую фрейм данных в массивы и перебираю каждый шаг, начиная с 0 до 4 000 000.

Для каждого временного шага я повторяю следующие временные шаги, пока не найду один, где есть разница в цене на 0,1%.Если цена выросла на 0,1%, этикетка равна 1, если она упала на 0,1%, этикетка равна 0. Это занимает очень много времени.

Возможно ли даже векторизовать это? Я пытался придумать решение для динамического программирования, чтобы уменьшить сложность времени, но я не уверен, есть ли оно.

high_bid = df['high_bid'].values
high_ask = df['high_ask'].values
low_bid = df['low_bid'].values
low_ask = df['low_ask'].values
open_bid = df['open_bid'].values
open_ask = df['open_ask'].values
labels = np.empty(len(data))
labels[:] = np.nan

for i in range(len(labels)-1):
    for j in range(i+1,len(labels)-1):
        if (open_ask[i] + (open_ask[i]*target) <= high_bid[j]):
            labels[i] = 1
            break
        elif (open_bid[i] - (open_bid[i]*target) >= low_ask[j]):
            labels[i] = 0
            break
df['direction'] = labels

Пример

                 time  open_bid  open_ask  high_bid  high_ask  low_bid  \
0 2006-09-19 12:00:00   1.26606   1.26621   1.27063   1.27078  1.26504   

1 2006-09-19 13:00:00   1.27010   1.27025   1.27137   1.27152  1.26960   

2 2006-09-19 14:00:00   1.27076   1.27091   1.27158   1.27173  1.26979   

3 2006-09-19 15:00:00   1.27008   1.27023   1.27038   1.27053  1.26708   

4 2006-09-19 16:00:00   1.26816   1.26831   1.26821   1.26836  1.26638   

5 2006-09-19 17:00:00   1.26648   1.26663   1.26762   1.26777  1.26606   

6 2006-09-19 18:00:00   1.26756   1.26771   1.26781   1.26796  1.26733   

7 2006-09-19 19:00:00   1.26763   1.26778   1.26785   1.26800  1.26754   

8 2006-09-19 20:00:00   1.26770   1.26785   1.26825   1.26840  1.26765   

9 2006-09-19 21:00:00   1.26781   1.26796   1.26791   1.26806  1.26703   

   low_ask  direction  
0  1.26519          1  
1  1.26975          1  
2  1.26994          0  
3  1.26723          0  
4  1.26653          0  
5  1.26621          1  
6  1.26748        NaN  
7  1.26769        NaN  
8  1.26780        NaN  
9  1.26718        NaN  

Я хочу добавить этот столбец направления для всех 4 миллионов строк.

Ответы [ 2 ]

0 голосов
/ 05 июня 2018

Вы также можете проверить функцию окна expanding () , но в обратном направлении, чтобы вычислить max_future_high_bid и min_future_low_ask после каждой строки:

# 0.1% increae/decrease
target = 0.001

# new column names
new_columns = [ "max_future_high_bid", "min_future_low_ask" ]

df[new_columns] = df[::-1].expanding(1)\                                                          
                          .agg({'high_bid':'max', 'low_ask':'min'})[::-1] \
                          .shift(-1)

# after you have these two values, you can calculate the direction with apply() function
def get_direction(x):
    if x.max_future_high_bid >= (1 + target) * x.open_ask :
        return 1
    elif (1 - target) * x.open_bid  >= x.min_future_low_ask:
        return 0
    else:
        return None

# calculate the direction
df['direction'] = df.apply(get_direction, axis=1)
0 голосов
/ 05 июня 2018

Первое решение попробовать: Cython.В аналогичном режиме я увеличил скорость в 20-90 раз, просто добавив %% cython в мой код.

В одной ячейке Jupyter

%load_ext Cython
cimport numpy as np
import numpy as np

cpdef func(np.ndarray high_bid, np.ndarray high_ask, np.ndarray low_bid, np.ndarray low_ask, np.ndarray open_bid, np.ndarray open_ask, np.ndarray labels):
    target = 0.001
    cdef Py_ssize_t i, j, n = len(labels)
    for i in range(n):
        for j in range(i+1, n):
            # The following are just a copy paste of your code
            if (open_ask[i] + (open_ask[i]*target) <= high_bid[j]):
                labels[i] = 1
                break
            elif (open_bid[i] - (open_bid[i]*target) >= low_ask[j]):
                labels[i] = 0
                break

В другой ячейке Jupyter

func(high_bid, high_ask, low_bid, low_ask, open_bid, open_ask, labels, target)

Дополнительная оптимизация

Вот отличное введение в Cython для панд

Вы можете ускорить процесс, добавив тип данных (np.ndarray[double])


Второе решение : используйте cummax, cummin для high_bid, low_ask в обратном порядке

target = 0.001
df['highest_bid_from_on'] = df.high_bid.sort_index(ascending=False).cummax().sort_index(ascending=True)
df['lowest_ask_from_on'] = df.low_ask.sort_index(ascending=False).cummin().sort_index(ascending=True)
df['direction'] = np.nan

df.loc[df.open_bid * (1 - target) >= df.lowest_ask_from_on, 'direction'] = 0
df.loc[df.open_ask * (1 + target) <= df.highest_bid_from_on, 'direction'] = 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...