如何加速 groupby multiindex 的嵌套循环
How to speed up nested loops for groupby multiindex
我有两个 Multiindex 数据框,即 panel1 和 panel2:它们都具有相同的 0 级索引——日期,但 1 级索引不同;请参阅下面的示例代码:
# panel1:
idx1 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],['id1', 'id2', 'id3']],names=['Dates', 'id'])
panel1=pd.DataFrame(np.random.randn(9,2), index=idx1,columns=['ytm','mat'])
# panel2:
idx2 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],['0.5', '1.5', '2.5']],names=['Dates', 'yr'])
panel2=pd.DataFrame(np.random.randn(9), index=idx2,columns=['curve'])
我想按日期(0 级索引)遍历两个面板。因此,对于每一天(例如“2017-05-02”),我在 yr[= 中搜索每个 id/row(面板 1)的 mat 28=] 列(panel2),如果匹配,我想获取相应的 curve 值(panel2)并将其添加为新列(命名为 CDB) 在 panel1.
我目前的代码如下:
group1=panel1.groupby(level=0)
group2=panel2.groupby(level=0)
lst=[]
for ytm in group1: # loop over each day
for yr in group2: # loop over each day
df_ytm=ytm[1] # get df of id, yt & mat
df_ytm=df_ytm.assign(CDB=np.nan) # add a col of nan, later will be replaced by matched curve values
df_curve=yr[1].reset_index() # need get rid of index to match yr with t_mat
df_curve.yr=df_curve.yr.astype(float)
for i in range(df_ytm.shape[0]): # loop over each row
if (df_ytm.iloc[i,1]==df_curve.yr).any()==True: # search if each 'mat' value in 'yr' column
df_ytm.iloc[i,2]=df_curve[df_curve.yr.isin([df_ytm.t_mat[i]])].curve.values # if matched, set 'CDB' as curve value
lst.append(df_ytm) # need get modified 'df_ytm' (with matched 'CDB')
代码的工作原理与我尝试使用一个小样本时一样,但我有一个巨大的面板 1(大小为 800 天乘以 10000 个 ID)和大面板 2。所以代码已经运行超过24小时
我想知道如何重写代码(使用可能的矢量化)来加快速度?
如有任何意见,我们将不胜感激!
如果我理解正确,您需要从 Dates
索引和 mat
列构造新的 MultiIndex
并为该索引获取 curve
的值。
import pandas as pd
import numpy as np
np.random.seed(12)
idx1 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["id1", "id2", "id3"]],
names=["Dates", "id"],
)
panel1 = pd.DataFrame(
np.random.randint(3, size=(9, 2)), index=idx1, columns=["ytm", "mat"]
)
idx2 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["0", "1", "2"]], names=["Dates", "yr"]
)
panel2 = pd.DataFrame(np.random.randint(3, size=9), index=idx2, columns=["curve"])
print(panel1)
# ytm mat
# Dates id
# 2017-05-02 id1 2 1
# id2 1 2
# id3 0 0
# 2017-05-03 id1 2 1
# id2 0 1
# id3 1 1
# 2017-05-04 id1 2 2
# id2 2 0
# id3 1 0
print(panel2)
# curve
# Dates yr
# 2017-05-02 0 0
# 1 1
# 2 2
# 2017-05-03 0 1
# 1 2
# 2 0
# 2017-05-04 0 1
# 1 2
# 2 0
panel1["CDM"] = panel2.loc[
pd.MultiIndex.from_arrays(
[panel1.index.get_level_values(0), panel1.mat.astype(str).rename("yr")]
)
].to_numpy()
print(panel1)
# ytm mat CDM
# Dates id
# 2017-05-02 id1 2 1 1
# id2 1 2 2
# id3 0 0 0
# 2017-05-03 id1 2 1 2
# id2 0 1 2
# id3 1 1 2
# 2017-05-04 id1 2 2 0
# id2 2 0 1
# id3 1 0 1
编辑
比较 mat
和 yr
作为浮点数并使用 .reindex
而不是 .loc
。
import pandas as pd
import numpy as np
np.random.seed(12)
idx1 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["id1", "id2", "id3"]],
names=["Dates", "id"],
)
panel1 = pd.DataFrame(
np.random.randint(3, size=(9, 2)), index=idx1, columns=["ytm", "mat"]
)
panel1.iloc[0, 1] = np.nan
idx2 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["0", "1", "2"]], names=["Dates", "yr"]
)
panel2 = pd.DataFrame(np.random.randint(3, size=9), index=idx2, columns=["curve"])
panel2 = panel2.rename(float, level=1)
print(panel1)
# ytm mat
# Dates id
# 2017-05-02 id1 2 NaN
# id2 1 2.0
# id3 0 0.0
# 2017-05-03 id1 2 1.0
# id2 0 1.0
# id3 1 1.0
# 2017-05-04 id1 2 2.0
# id2 2 0.0
# id3 1 0.0
print(panel2)
# curve
# Dates yr
# 2017-05-02 0.0 0
# 1.0 1
# 2.0 2
# 2017-05-03 0.0 1
# 1.0 2
# 2.0 0
# 2017-05-04 0.0 1
# 1.0 2
# 2.0 0
panel1["CDM"] = panel2.reindex(
pd.MultiIndex.from_arrays(
[panel1.index.get_level_values(0), panel1.mat.rename("yr")]
)
).to_numpy()
print(panel1)
# ytm mat CDM
# Dates id
# 2017-05-02 id1 2 NaN NaN
# id2 1 2.0 2.0
# id3 0 0.0 0.0
# 2017-05-03 id1 2 1.0 2.0
# id2 0 1.0 2.0
# id3 1 1.0 2.0
# 2017-05-04 id1 2 2.0 0.0
# id2 2 0.0 1.0
# id3 1 0.0 1.0
为了生成我的代码的任何非空且可重复的结果,
我稍微改变了两个面板的创建方式:
np.random.seed(0)
idx1 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],
['id1', 'id2', 'id3']], names=['Dates', 'id'])
panel1 = pd.DataFrame({'ytm': np.random.randn(9),
'mat': [0.5, 0.82, 1.06, -0.27, 1.5, 0.59, 0.62, 1.89, 2.5]}, index=idx1)
idx2 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],
[0.5, 1.5, 2.5]], names=['Dates', 'yr'])
panel2 = pd.DataFrame(np.random.randn(9), index=idx2, columns=['curve'])
变化包括:
np.random.seed
- 获得可修复的结果。
- 只有 ytm panel1 列被创建为随机数。为了
在 mat 中有一些匹配值,我把预定义的值放在那里,
为每个日期提供 yr 的匹配项。
- idx2 的级别 1 是 float 类型。您的样本包括 strings,
这显然不等于 mat 值。
我还假设对于 panel1 中的每个组,查找匹配应该
在 panel2 中 同一日期 的行中执行(不在
所有日期的组)。
要生成结果(CDB 列),请执行以下操作:
定义一个为当前组生成CDB列的函数
行(每个日期):
def getCDB(grp):
cdb = panel2.xs(grp.index[0][0], level=0).reindex(grp.mat).curve
return pd.Series(cdb.values, index=grp.index)
然后应用它并将结果保存在新列中:
panel1['CDB'] = panel1.groupby(level=0).apply(getCDB)\
.reset_index(level=0, drop=True)
对于我的输入数据,结果是:
ytm mat CDB
Dates id
2017-05-02 id1 1.764052 0.50 0.410599
id2 0.400157 0.82 NaN
id3 0.978738 1.06 NaN
2017-05-03 id1 2.240893 -0.27 NaN
id2 1.867558 1.50 0.121675
id3 -0.977278 0.59 NaN
2017-05-04 id1 0.950088 0.62 NaN
id2 -0.151357 1.89 NaN
id3 -0.103219 2.50 -0.205158
我有两个 Multiindex 数据框,即 panel1 和 panel2:它们都具有相同的 0 级索引——日期,但 1 级索引不同;请参阅下面的示例代码:
# panel1:
idx1 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],['id1', 'id2', 'id3']],names=['Dates', 'id'])
panel1=pd.DataFrame(np.random.randn(9,2), index=idx1,columns=['ytm','mat'])
# panel2:
idx2 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],['0.5', '1.5', '2.5']],names=['Dates', 'yr'])
panel2=pd.DataFrame(np.random.randn(9), index=idx2,columns=['curve'])
我想按日期(0 级索引)遍历两个面板。因此,对于每一天(例如“2017-05-02”),我在 yr[= 中搜索每个 id/row(面板 1)的 mat 28=] 列(panel2),如果匹配,我想获取相应的 curve 值(panel2)并将其添加为新列(命名为 CDB) 在 panel1.
我目前的代码如下:
group1=panel1.groupby(level=0)
group2=panel2.groupby(level=0)
lst=[]
for ytm in group1: # loop over each day
for yr in group2: # loop over each day
df_ytm=ytm[1] # get df of id, yt & mat
df_ytm=df_ytm.assign(CDB=np.nan) # add a col of nan, later will be replaced by matched curve values
df_curve=yr[1].reset_index() # need get rid of index to match yr with t_mat
df_curve.yr=df_curve.yr.astype(float)
for i in range(df_ytm.shape[0]): # loop over each row
if (df_ytm.iloc[i,1]==df_curve.yr).any()==True: # search if each 'mat' value in 'yr' column
df_ytm.iloc[i,2]=df_curve[df_curve.yr.isin([df_ytm.t_mat[i]])].curve.values # if matched, set 'CDB' as curve value
lst.append(df_ytm) # need get modified 'df_ytm' (with matched 'CDB')
代码的工作原理与我尝试使用一个小样本时一样,但我有一个巨大的面板 1(大小为 800 天乘以 10000 个 ID)和大面板 2。所以代码已经运行超过24小时
我想知道如何重写代码(使用可能的矢量化)来加快速度?
如有任何意见,我们将不胜感激!
如果我理解正确,您需要从 Dates
索引和 mat
列构造新的 MultiIndex
并为该索引获取 curve
的值。
import pandas as pd
import numpy as np
np.random.seed(12)
idx1 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["id1", "id2", "id3"]],
names=["Dates", "id"],
)
panel1 = pd.DataFrame(
np.random.randint(3, size=(9, 2)), index=idx1, columns=["ytm", "mat"]
)
idx2 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["0", "1", "2"]], names=["Dates", "yr"]
)
panel2 = pd.DataFrame(np.random.randint(3, size=9), index=idx2, columns=["curve"])
print(panel1)
# ytm mat
# Dates id
# 2017-05-02 id1 2 1
# id2 1 2
# id3 0 0
# 2017-05-03 id1 2 1
# id2 0 1
# id3 1 1
# 2017-05-04 id1 2 2
# id2 2 0
# id3 1 0
print(panel2)
# curve
# Dates yr
# 2017-05-02 0 0
# 1 1
# 2 2
# 2017-05-03 0 1
# 1 2
# 2 0
# 2017-05-04 0 1
# 1 2
# 2 0
panel1["CDM"] = panel2.loc[
pd.MultiIndex.from_arrays(
[panel1.index.get_level_values(0), panel1.mat.astype(str).rename("yr")]
)
].to_numpy()
print(panel1)
# ytm mat CDM
# Dates id
# 2017-05-02 id1 2 1 1
# id2 1 2 2
# id3 0 0 0
# 2017-05-03 id1 2 1 2
# id2 0 1 2
# id3 1 1 2
# 2017-05-04 id1 2 2 0
# id2 2 0 1
# id3 1 0 1
编辑
比较 mat
和 yr
作为浮点数并使用 .reindex
而不是 .loc
。
import pandas as pd
import numpy as np
np.random.seed(12)
idx1 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["id1", "id2", "id3"]],
names=["Dates", "id"],
)
panel1 = pd.DataFrame(
np.random.randint(3, size=(9, 2)), index=idx1, columns=["ytm", "mat"]
)
panel1.iloc[0, 1] = np.nan
idx2 = pd.MultiIndex.from_product(
[["2017-05-02", "2017-05-03", "2017-05-04"], ["0", "1", "2"]], names=["Dates", "yr"]
)
panel2 = pd.DataFrame(np.random.randint(3, size=9), index=idx2, columns=["curve"])
panel2 = panel2.rename(float, level=1)
print(panel1)
# ytm mat
# Dates id
# 2017-05-02 id1 2 NaN
# id2 1 2.0
# id3 0 0.0
# 2017-05-03 id1 2 1.0
# id2 0 1.0
# id3 1 1.0
# 2017-05-04 id1 2 2.0
# id2 2 0.0
# id3 1 0.0
print(panel2)
# curve
# Dates yr
# 2017-05-02 0.0 0
# 1.0 1
# 2.0 2
# 2017-05-03 0.0 1
# 1.0 2
# 2.0 0
# 2017-05-04 0.0 1
# 1.0 2
# 2.0 0
panel1["CDM"] = panel2.reindex(
pd.MultiIndex.from_arrays(
[panel1.index.get_level_values(0), panel1.mat.rename("yr")]
)
).to_numpy()
print(panel1)
# ytm mat CDM
# Dates id
# 2017-05-02 id1 2 NaN NaN
# id2 1 2.0 2.0
# id3 0 0.0 0.0
# 2017-05-03 id1 2 1.0 2.0
# id2 0 1.0 2.0
# id3 1 1.0 2.0
# 2017-05-04 id1 2 2.0 0.0
# id2 2 0.0 1.0
# id3 1 0.0 1.0
为了生成我的代码的任何非空且可重复的结果, 我稍微改变了两个面板的创建方式:
np.random.seed(0)
idx1 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],
['id1', 'id2', 'id3']], names=['Dates', 'id'])
panel1 = pd.DataFrame({'ytm': np.random.randn(9),
'mat': [0.5, 0.82, 1.06, -0.27, 1.5, 0.59, 0.62, 1.89, 2.5]}, index=idx1)
idx2 = pd.MultiIndex.from_product([['2017-05-02', '2017-05-03', '2017-05-04'],
[0.5, 1.5, 2.5]], names=['Dates', 'yr'])
panel2 = pd.DataFrame(np.random.randn(9), index=idx2, columns=['curve'])
变化包括:
np.random.seed
- 获得可修复的结果。- 只有 ytm panel1 列被创建为随机数。为了 在 mat 中有一些匹配值,我把预定义的值放在那里, 为每个日期提供 yr 的匹配项。
- idx2 的级别 1 是 float 类型。您的样本包括 strings, 这显然不等于 mat 值。
我还假设对于 panel1 中的每个组,查找匹配应该 在 panel2 中 同一日期 的行中执行(不在 所有日期的组)。
要生成结果(CDB 列),请执行以下操作:
定义一个为当前组生成CDB列的函数 行(每个日期):
def getCDB(grp): cdb = panel2.xs(grp.index[0][0], level=0).reindex(grp.mat).curve return pd.Series(cdb.values, index=grp.index)
然后应用它并将结果保存在新列中:
panel1['CDB'] = panel1.groupby(level=0).apply(getCDB)\ .reset_index(level=0, drop=True)
对于我的输入数据,结果是:
ytm mat CDB
Dates id
2017-05-02 id1 1.764052 0.50 0.410599
id2 0.400157 0.82 NaN
id3 0.978738 1.06 NaN
2017-05-03 id1 2.240893 -0.27 NaN
id2 1.867558 1.50 0.121675
id3 -0.977278 0.59 NaN
2017-05-04 id1 0.950088 0.62 NaN
id2 -0.151357 1.89 NaN
id3 -0.103219 2.50 -0.205158