Python 如何加速groupby多索引的嵌套循环

Python 如何加速groupby多索引的嵌套循环,python,pandas,performance,vectorization,multi-index,Python,Pandas,Performance,Vectorization,Multi Index,我有两个多索引数据帧,即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,co

我有两个多索引数据帧,即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列(面板2)中搜索每个id/行(面板1)的mat,如果存在匹配项,我希望获得相应的曲线值(面板2),并将其作为新列(命名为CDB)添加到面板1中

我的当前代码如下:

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
列构建新的
多索引
,并获取该索引的
曲线

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
    -te获取可修复的结果
  • 只有panel1的ytm列被创建为随机数。整齐 为了在mat中有一些匹配的值,我将预定义的值放在那里, 为每个日期提供一个yr匹配项
  • idx2的1级为浮动类型。您的示例包括字符串, 这显然不等于mat值
我还认为,对于panel1中的每组,查找匹配项应该是 从panel2开始,在日期相同的情况下按行执行 所有日期的分组)

要生成结果(CDB列),请执行以下操作:

  • 为当前组定义函数生成CDB列 行(每个日期):

  • 然后应用它并将结果保存在新列中:

     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
    

    非常感谢您的解决方案!它似乎可以工作,但代码
    panel1.mat.astype(str)
    中出现了一个问题。由于
    panel1.mat
    是带2位小数的浮点数据(例如3.60),故意设置为与
    panel2.yr
    (带2个小数点的浮点值)匹配,因此,
    panel1.mat.astype(str)
    将3.60转换为3.6,因为其格式与
    panel2.yr
    中的(3.60)不匹配。我想知道是否有更好的方法来处理这个问题?或者需要格式化panel2.yr@V.ayrat只是为了跟进,还返回了一个未来警告“main:2:FutureWarning:Passing list likes to.loc或[]如果缺少任何标签,将在将来引发KeyError,您可以使用.reindex()作为替代方法。”。我想知道这是否会成为一个问题?我认为使用浮点格式更好。您可以执行
    panel2=panel2.重命名(float,level=1)
    。但这就提出了比较浮点数的问题。如果为
    yr
    mat
    分配了相同的值,则精确匹配将起作用,但如果有一些中间计算,这可能会稍微改变该值,并且它们将不匹配。。。如果缺少值,则最好按照建议使用
    .reindex()
    来抑制警告。我想知道在这种情况下如何正确使用
    .reindex
    mat
    列确实有
    nan
    ,但是
    yr
    没有缺失数据。再次感谢!我认为
    panel1.mat.astype(str)
    mat
    格式为float(例如3.60)但
    yr
    格式为string(例如3.6)时会引起不匹配。。。?简而言之,我试图将
    yr
    设置为float;它会导致所有级别的多索引不匹配,因此,使原始建议不起作用…相反,首先将
    yr
    设置为float,然后再次将其设置为str,匹配过程起作用。此外,我想知道如何正确使用
    .reindex
    @艾拉特
                         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