从多个 OHLCV 数据帧创建单个 pandas 数据帧

Posted

技术标签:

【中文标题】从多个 OHLCV 数据帧创建单个 pandas 数据帧【英文标题】:Create a single pandas dataframe from multiple OHLCV dataframes 【发布时间】:2017-10-26 15:12:39 【问题描述】:

我有一个文件夹,其中包含 S&P500 组件的历史盘中数据(1 分钟频率),保存为单个 .parquet 表(500 个文件,总共 7.60GB)。

每个表都有一个日期时间索引和五列('Open'、'High'、'Low'、'Close'、'Volume'),但它们都有不同的长度(取决于它们的IPO):

如果它们从同一年开始,它们可能在不同的季度开始 如果它们从同一年季度开始,它们可能会在不同月份开始 如果它们以相同的年-季-月开始,它们可能会在不同的周开始 如果它们以相同的年-季-月-周开始,它们可能会在不同的日子开始 如果它们以相同的年-季-月-周-日开始,它们可能会以不同的分钟开始

为了测试我的投资组合策略,我需要同时在多个资产上测试我的模型,其中时间是一个共同的日期时间索引,一行接一行。我还需要使用 groupby 函数(按年、季度、月、周和日)将我的模型应用于不同数据框的切片。

我想要做的是将所有这些单个数据帧合并到一个更大的数据帧中,并具有足够长的日期时间索引以包含所有较小的索引。在这个大数据框中,我希望(我对不同的解决方案持开放态度)将单个资产作为不同的列,例如:

       Apple                             Amazon
       Open  High  Low  Close  Volume    Open  High  Low  Close  Volume
index
2002
.
.
.
2017

如何合并所有数据帧,同时保持它们之间的共同索引?

我的工作站有一个 i7 CPU (4C/8T) 和 16GB 的 RAM,所以我认为我可以将这个大数据帧完全加载到内存中,但我不知道像 Dask 这样的解决方案是否更有效.我对 Dask 的问题是文档很差而且缺乏示例,我不是专业开发人员,所以对我来说实现它并不容易。

【问题讨论】:

到目前为止,您为将它们合并到 Pandas 中做了什么,出了什么问题? 这就是我要问的,我不知道如何“水平”合并单个数据帧以及如何为所有数据帧设置单个索引。 你试过dd.concat(..., axis=1)吗?如果是这样,您遇到问题了吗? 是的,为了保持资产的名称高于它的相对子列,我使用了 dd.concat([df1, df2,...,dfn], axis=1, keys=[asset1_name, asset2_name,.. .,assetn_name])。有用。唯一的问题是,这样做会将列 dtype 从 int32 更改为 float64。 【参考方案1】:

下面的代码部分包含两个函数。 df_sample() 创建所需大小、起点和列名的数据框。函数 multiJoin() 采用预定义的数据帧列表,并使用可用于pandas Join 的任何方法连接它们。使用该设置,您需要做的就是运行multiJoin(dfs = [df1, df2, df3], method = 'outer', names = ['Apple', 'Amazon', 'SomeOther']) 以获得示例数据帧的所需结果。我添加了一个函数newNames(df, sep, name1, name2) 来处理分层列名:

Apple                           Amazon
Open  High  Low  Close  Volume  Open  High  Low  Close  Volume

# imports
import pandas as pd
import numpy as np
np.random.seed(1234)

# Function for reproducible data sample
def df_sample(start, rows, names):
    ''' Function to create data sample with random returns

    Parameters
    ==========
    rows : number of rows in the dataframe
    names: list of names to represent assets

    Example
    =======

    >>> returns(rows = 2, names = ['A', 'B'])

                  A       B
    2017-01-01  0.0027  0.0075
    2017-01-02 -0.0050 -0.0024
    '''
    listVars= names
    rng = pd.date_range(start, periods=rows, freq='D')
    df_temp = pd.DataFrame(np.random.randint(-100,200,size=(rows, len(listVars))), columns=listVars) 
    df_temp = df_temp.set_index(rng)
    #df_temp = df_temp / 10000

    return df_temp

colNames = ['Open', 'High', 'Low', 'Close']

# Reproducible dataframes
df1 = df_sample('1/1/2017', 150,colNames)
df2 = df_sample('2/1/2017', 150,colNames)
df3 = df_sample('3/1/2017', 150,colNames)

#%%

def multiJoin(dfs, method, names):
    """ Takes a pre-defined list of pandas dataframes and joins them
        by the method specified and available in df.join().
        This is a specific case for joining a bunch og OHLCV tables,
        so column names will overlap. You should therefore specify 
        a list for each dataframe to provide unique column names.

        Joining dataframes with different indexes will result in
        omitted and / or missing data.

        Using method = 'outer' will display missing values for mismatching dates.

        Using method = 'inner' will keep only dates where all dataframes have values and omit
                        all other.

    """

    # Isolate a df to join all other dfs on
    df_left = dfs[0]
    df_left.columns = [names[0]+ '_' + col for col in df_left.columns]
    df_other = dfs[1:]

    # Manage names
    names_other = names[1:]

    # Loop through list of dataframes to join on the first one,
    # and rename columns
    counter = 0
    for df in df_other:
        df.columns = [names_other[counter] + '_' + col for col in df.columns]
        df_left = df_left.join(df, how = method)
        counter = counter + 1

    return df_left

dfJoined_outer = multiJoin(dfs = [df1, df2, df3], method = 'outer', names = ['Apple', 'Amazon', 'SomeOther'])

输出:

如果你运行dfJoined_inner = multiJoin(dfs = [df1, df2, df3], method = 'inner', names = ['Apple', 'Amazon', 'SomeOther']),你会得到:

考虑到OP的评论后添加:

我添加了一个基于pandas.MultiIndex.from_arrays 的函数,它将为您提供分层列名称,使数据框看起来就像您所要求的那样。只需运行df_multi = newNames(df = dfJoined_inner, sep = '_')

def newNames(df, sep, name1, name2):
    """ Takes a single column index from a pandas dataframe,
        splits the original titles by a specified separator,
        and replaces the single column index with a 
        multi index. You can also assign names to levels of your new index
    """

    df_temp = dfJoined_inner
    sep = '_'

    single = pd.Series(list(df_temp))
    multi= single.str.split(sep, expand = True)

    multiIndex = pd.MultiIndex.from_arrays((multi[0], multi[1]), names = (name1, name2))


    df_new = pd.DataFrame(df_temp.values, index = df_temp.index, columns = multiIndex)

    return(df_new)


df_multi = newNames(df = dfJoined_inner, sep = '_', name1 = 'Stock', name2 = 'Category')

我使用的是 Spyder,因此变量资源管理器中数据框的屏幕截图如下所示(注意列标题​​中的括号):

但是如果你运行print(df_multi.tail()),你会看到列标题看起来就像你请求的那样:

#Output
Stock       Apple                 Amazon                    SomeOther            
Category    Open High Low Close   Open High  Low Close      Open High  Low  Close   
2017-05-26   -92  140  47   -53    -73  -50  -94   -72        16  115   96     74
2017-05-27   169  -34 -78   120     46  195   28   186        -9  102  -13    141
2017-05-28   -98  -10  57   151    169  -17  148   150       -26  -43  -53     63
2017-05-29     1   87  38     0     28   71   52   -57         6   86  179     -6
2017-05-30   -31   52  33    63     46  149  -71   -30       -20  188  -34    -60

【讨论】:

谢谢!我没有考虑使用 'asset_name_Open' 作为列名,但结果实际上与我正在寻找的结果相同。如果您知道如何为列构建 MultiIndex,请告诉我。 我刚刚添加了另一个功能,可以做到这一点。现在,您可以轻松地在两种不同的数据框命名方式之间切换 @ilpomo,我最后添加的答案是否解决了您的问题? 感谢您出色的工作,我真的很感激。再次感谢您花时间帮助我。 我在这里设置了聊天室:chat.***.com/rooms/177076/…

以上是关于从多个 OHLCV 数据帧创建单个 pandas 数据帧的主要内容,如果未能解决你的问题,请参考以下文章

从单个数据帧创建多个子集,无需替换

如何将 OHLCV 命名数据数组转换为 numpy 数据帧?

如何从 for 循环返回多个具有唯一名称的 pandas 数据帧?

使用单个数据帧的多个标签调整图例 Seaborn 联合图

Pandas:使用循环和分层索引将多个 csv 文件导入数据帧

Pandas 将多个数据帧与时间戳索引对齐