从多个 OHLCV 数据帧创建一个 pandas 数据帧
Create a single pandas dataframe from multiple OHLCV dataframes
我有一个包含标准普尔 500 成分股历史盘中数据(1 分钟频率)的文件夹,保存为单个 .parquet tables(500 个文件,总计 7.60GB)。
每个 table 都有一个日期时间索引和五个列('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 的问题是糟糕的文档和缺乏示例,我不是专业开发人员,所以对我来说实现它并不容易。
下面的代码部分包含两个函数。 df_sample()
创建所需大小的数据框、起点和列名。函数 multiJoin() 采用 pre-defined 数据帧列表,并使用任何可用于 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,因此变量资源管理器中数据框的屏幕截图如下所示(注意 headers 列中的括号):
但是如果您 运行 print(df_multi.tail())
,您会看到 headers 列看起来就像您请求的那样:
#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
我有一个包含标准普尔 500 成分股历史盘中数据(1 分钟频率)的文件夹,保存为单个 .parquet tables(500 个文件,总计 7.60GB)。
每个 table 都有一个日期时间索引和五个列('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 的问题是糟糕的文档和缺乏示例,我不是专业开发人员,所以对我来说实现它并不容易。
下面的代码部分包含两个函数。 df_sample()
创建所需大小的数据框、起点和列名。函数 multiJoin() 采用 pre-defined 数据帧列表,并使用任何可用于 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,因此变量资源管理器中数据框的屏幕截图如下所示(注意 headers 列中的括号):
但是如果您 运行 print(df_multi.tail())
,您会看到 headers 列看起来就像您请求的那样:
#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