将带有 numpy 数组列表的字典转换为 pandas 数据帧的最有效方法?

Most efficient way to convert a dictionary with list of numpy arrays into pandas dataframe?

我正在尝试使用 numpy 向量化在多个时间段内对多个股票代码进行批量计算,但我不确定我处理问题的方式是否最有效。我正在使用 "tulipy" 技术指标库对不同股票的 "Close" 价格进行计算。

这是源数据作为数据框的样子:

 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],

然后,我遍历每个唯一的代码并从 "tulipy" 技术指标库计算相对强度指数 (RSI),同时还维护每个价格数据点的相应日期,并存储这些值在字典中:

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]",因为一旦我实施这可能会占用大量资源6,000 多个代码。这是字典 (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 数据框 and/or 和 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. 有没有更好的方法对此进行矢量化计算 数据集,同时保持股票代码的 order/matching, 日期及其计算值?
  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)

要制作字典,您应该使用 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)