Панды функционируют слишком медленно - оптимизировать с помощью dict / numpy? - PullRequest
0 голосов
/ 04 июня 2018

У меня есть ~ 10 больших df (5 миллионов + строки и растущие), по которым я хочу выполнить вычисления.Делать это с сырыми пандами даже на супер быстрой машине AWS невыносимо медленно.Большинство функций, которые мне нужны, являются базовыми, поэтому я думаю, что можно было бы экспортировать из pandas в dict (?), Выполнить мои вычисления и затем отправить их обратно в df?

Оригинальный df - это просто ценаи размер захвата сделок, как указано ниже (миллионы строк, как упоминалось).

                            size      price
time        
2018-05-18 12:05:11.521 -0.026600   8100.000000
2018-05-18 12:05:11.674 -0.115616   8100.000000
2018-05-18 12:05:11.677 -0.026611   8100.000000
2018-05-18 12:05:11.678 -0.074000   8098.400000
2018-05-18 12:05:11.680 -0.783772   8096.600000
2018-05-18 12:05:11.807 -1.000000   8096.600000
2018-05-18 12:05:12.024 -0.100600   8096.600000
2018-05-18 12:05:12.198 -0.899400   8096.600000
2018-05-18 12:05:12.199 -1.600600   8095.100000
2018-05-18 12:05:14.949 1.000000    8092.600000
2018-05-18 12:05:14.951 0.258350    8092.600000
2018-05-18 12:05:30.191 -0.017330   8092.500000
2018-05-18 12:05:30.192 -0.161670   8088.300000
2018-05-18 12:05:30.712 -0.002000   8088.300000
2018-05-18 12:05:30.773 -0.002000   8088.300000
2018-05-18 12:05:34.688 0.003328    8088.400000

Теперь я хочу применить следующее (которое действует как агрегирование миллионов строк в 5-секундные окна):

df = df.groupby(pd.Grouper(freq='5S')).apply(tick_features).shift()[1:]

, где tick_features() - это:

def tick_features(x):
    if not x.empty:
        open = x['price'].iloc[0]
        close = x['price'].iloc[-1]
    else:
        open = np.nan
        close = np.nan
    high = x['price'].max()
    low = x['price'].min()
    volume = np.abs(x['size']).sum()
    buy_volume = x['size'][x['size'] > 0].sum()
    sell_volume = np.abs(x['size'][x['size'] < 0].sum())
    pct_buy_volume = (buy_volume) / ((buy_volume) + (sell_volume))
    pct_sell_volume = (sell_volume) / ((buy_volume) + (sell_volume))
    num_trades = x['size'].count()
    num_buy_trades = (x['size'] > 0).sum()
    num_sell_trades = (x['size'] < 0).sum()
    pct_buy_trades = (x['size'] > 0).mean() * 100
    pct_sell_trades = (x['size'] < 0).mean() * 100

    return pd.Series([open,high,low,close,volume,buy_volume,sell_volume,pct_buy_volume,pct_sell_volume,
                      num_trades,num_buy_trades,num_sell_trades,pct_buy_trades,pct_sell_trades], 
                     index=['open','high','low','close','volume','buy_volume','sell_volume','pct_buy_volume','pct_sell_volume',
                            'num_trades','num_buy_trades','num_sell_trades','pct_buy_trades','pct_sell_trades'])

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

1 Ответ

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

Код медленный, потому что существует очень много групп, и для каждой группы Pandas необходимо создать объект DataFrame и передать его в tick_features(), цикл выполняется в Python.

Чтобы ускорить вычисление, вы можете вызвать методы агрегирования, которые выполнялись в цикле Cython:

Сначала подготовьте несколько фиктивных данных:

import pandas as pd
import numpy as np

idx = pd.date_range("2018-05-01", "2018-06-02", freq="0.1S")
x = np.random.randn(idx.shape[0], 2)

df = pd.DataFrame(x, index=idx, columns=["size", "price"]) 

добавьте в него дополнительные столбцы,вычисления выполняются быстро, если у вас достаточно памяти:

df["time"] = df.index
df["volume"] = df["size"].abs()
df["buy_volume"] = np.clip(df["size"], 0, np.inf)
df["sell_volume"] = np.clip(df["size"], -np.inf, 0)
df["buy_trade"] = df["size"] > 0
df["sell_trade"] = df["size"] < 0    

, затем сначала сгруппируйте объект DataFrame и вызовите методы агрегирования:

g = df.groupby(pd.Grouper(freq="5s"))
df2 = pd.DataFrame(
    dict(
    open = g["time"].first(),
    close = g["time"].last(),
    high = g["price"].max(),
    low = g["price"].min(),
    volume = g["volume"].sum(),
    buy_volume = g["buy_volume"].sum(),
    sell_volume = -g["sell_volume"].sum(),
    num_trades = g["size"].count(),
    buy_trade = g["buy_trade"].sum(),
    sell_trade = g["sell_trade"].sum(),
    pct_buy_trades  = g["buy_trade"].mean() * 100,
    pct_sell_trades = g["sell_trade"].mean() * 100,
    )
)

d = df2.eval("buy_volume + sell_volume")
df2["pct_buy_volume"] = df2.eval("buy_volume / @d")
df2["pct_sell_volume"] = df2.eval("sell_volume / @d")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...