Самый эффективный способ преобразовать словарь со списком numpy массивов в pandas dataframe? - PullRequest
0 голосов
/ 05 февраля 2020

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

Вот как исходные данные будут выглядеть как фрейм данных:

 Ticker Date        Close
0   A   1999-11-18  31.473534
1   A   1999-11-19  28.880543
2   A   1999-11-22  31.473534
3   A   1999-11-23  28.612303
4   A   1999-11-24  29.372318

Вот воспроизводимый код:

d = {'Ticker': ['A','A','A','A','A','A','A','A','A','A','A','A','A','A','A'], 'Date': ['1999-11-18', '1999-11-19', '1999-11-22', '1999-11-23', '1999-11-24','1999-11-26', '1999-11-29', '1999-11-30', '1999-12-01', '1999-12-02','1999-12-03', '1999-12-06', '1999-12-07', '1999-12-08', '1999-12-09'], 'Close': ['31.473534','28.880543','31.473534','28.612303','29.372318','29.461731','30.132332','30.177038','30.713520','31.562946','31.831188','32.725323','32.367668','32.322960','32.770027']}
df = pd.DataFrame(data=d)

Вычисления будут выполняться с плавающей запятой числа.

Затем я создаю массив numpy из запрашиваемых результатов и нахожу уникальные значения (тикеры) для повторения в целях индексации:

import pandas as pd
import numpy as np
import tulipy as ti

a = np.array([(df['Ticker']),(df['Date']),(df['Close'])])
x_unique = np.unique(a[0], return_counts=True, return_index=True)

Вот что исходные данные будут выглядеть как массив numpy:

array([['A', Timestamp('1999-11-18 00:00:00'), 31.473533630371094],
       ['A', Timestamp('1999-11-19 00:00:00'), 28.880542755126953],
       ['A', Timestamp('1999-11-22 00:00:00'), 31.473533630371094],

Затем я перебираю каждый уникальный тикер и вычисляю Индекс относительной силы (RSI) из библиотеки технических индикаторов "tulipy", а также поддержание соответствующей даты для каждой точки данных о цене и сохранение этих значений в словаре:

dictt = {}
start = 0
d = 0
for i in x_unique[0]:
    dictt[i] = [ti.rsi(a[2, start:start+x_unique[2][d]].astype(float), 14), a[1, start:start+x_unique[2][d]]]                    
    start = start+x_unique[2][d]
    d+=1 

Все это работает отлично и довольно быстро, но я чувствую, что существует способ numpy итерация каждого уникального тикера вместо использования «for i in x_unique [0]», поскольку это может стать ресурсоемким, как только я реализую более 6000 тикеров. Вот пример выходных данных словаря (dictt):

{'A': [array([54.98280996, 51.72842265, 53.80685853, ..., 71.42460267,
         68.75692746, 65.20371964]),
  array([Timestamp('1999-11-18 00:00:00'), Timestamp('1999-11-19 00:00:00'),
         Timestamp('1999-11-22 00:00:00'), ...,
         Timestamp('2019-11-27 00:00:00'), Timestamp('2019-11-29 00:00:00'),
         Timestamp('2019-12-02 00:00:00')], dtype=object),
  array([31.47353363, 30.89731344, 31.02536237, ..., 79.59926089,
         79.85942439, 79.96844085]),

Следующая проблема заключается в том, как наиболее эффективно передать это в pandas массив данных и / или sql, потому что получение данные в удобочитаемом виде - вот где лежит большая часть неэффективности скрипта. Вот результат моего преобразования в pandas фрейм данных, который находится не в желаемом формате, который я бы хотел:

pd.DataFrame(dict([ (k,pd.Series(v)) for k,v in dictt.items() ]))

A   AAPL    AEP AMAT    ATVI
0   [54.98280995952094, 51.72842265345178, 53.8068...   [64.49276119489626, 58.970440366273245, 54.374...   [48.78048780487805, 38.63298662704309, 37.7864...   [41.830064699495054, 45.295506922269944, 44.34...   [45.23809231868367, 45.23809231868366, 58.8677...
1   [1999-11-18 00:00:00, 1999-11-19 00:00:00, 199...   [1980-12-12 00:00:00, 1980-12-15 00:00:00, 198...   [1970-01-02 00:00:00, 1970-01-05 00:00:00, 197...   [1980-03-17 00:00:00, 1980-03-18 00:00:00, 198...   [1993-10-25 00:00:00, 1993-10-26 00:00:00, 199...
2   [31.473533630371094, 30.897313435872395, 31.02...   [0.5133928656578064, 0.5074404809210036, 0.494...   [30.625, 30.708333333333332, 30.71759259259259...   [0.0954861119389534, 0.09510030928585264, 0.09...   [0.9375, 0.9212962918811374, 0.908693407788688...

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

    rsi date    ema8    ema20   ema50   ema100  ema200
A   54.9828 1999-11-18  31.4735 31.4735 31.4735 31.4735 31.4735
A   51.7284 1999-11-19  30.8973 31.2266 31.3718 31.4222 31.4477

Итак, мои основные вопросы:

  1. Есть ли лучший способ выполнять векторизованные вычисления для этого набора данных при сохранении порядка / соответствия биржевых символов, дат и их вычисленных значений?
  2. Могу ли я игнорировать хранение всех данных в словаре и хранить все в массиве numpy?
  3. Могу ли я напрямую хранить значения из массива numpy в базе данных sql или pandas датафрейм табличного характера?

ОБНОВЛЕНИЕ для дат присоединения и rsi на фрейме данных:

df_dictt = pd.DataFrame(dictt).T

output = pd.DataFrame()
for i in range(len(df_dictt)):
    df_join = pd.DataFrame(df_dictt[1][i]).rename(columns={1:'date'}).join(pd.DataFrame(df_dictt[0][i]).rename(columns={0:'rsi'}), how='left')
    df_join['rsi'] = df_join['rsi'].shift(14)
    df_join['ticker'] = df_dictt.index[i]
    output = output.append(df_join, ignore_index=False)

1 Ответ

0 голосов
/ 05 февраля 2020

Чтобы создать словарь, вы должны использовать pandas DataFrame.groupby вместо обработки в массивы, а затем повторять их.

dictt = {ticker: ti.rsi(group["Close"], 14)
         for ticker, group in df.groupby("Ticker")}

group["Close"] будет серией pandas. Если функция rsi не принимает это, вам может понадобиться добавить что-то вроде group["Close"].to_numpy(dtype=float), чтобы преобразовать их в numpy массивы.

Вы можете добавить значения rsi непосредственно в фрейм данных, один Группировать за раз, например, так:

for _, group in df.groupby("Ticker"):
    df.loc[group.index[14:], "rsi"] = ti.rsi(group["Close"], 14)

Вы также можете apply функцию для групп, но в этом случае вам необходимо каждый раз преобразовывать в ряд pandas, чтобы индексы сохранялись :

def rsi(group, period):
    return pd.Series(ti.rsi(group["Close"], period), index=group.index[period:])

df["rsi"] = df.groupby("Ticker").apply(rsi, 14).reset_index(level=0, drop=True)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...