【问题标题】:Match IntervalIndex as part of a MultiIndex将 IntervalIndex 匹配为 MultiIndex 的一部分
【发布时间】:2018-03-27 23:40:34
【问题描述】:

我有一个使用 pandas IntervalIndex 设置的问题,类似于 herehere 提出的问题,但到目前为止,这些差异使我无法成功实施他们的解决方案。

我有两个 DataFrame,构造如下:

df1

    month   group   time    distance
0   1       A       10      100
1   1       A       20      120
2   1       A       25      110
3   1       B       5       90
4   1       B       7       99
5   1       B       17      60
6   1       C       30      200
7   1       C       35      170
8   2       A       5       40
9   2       A       10      27
10  2       B       17      33
11  2       B       19      65

df1.set_index(['month', 'group', 'time'], inplace=True)

df2

    month   group   start   end     period
0   1       A       5       15      1
1   1       A       15      21      2
2   1       A       21      30      3
3   1       B       2       10      1
4   1       B       10      20      2
5   2       A       3       8       1
6   2       A       8       15      2
7   2       B       10      20      1

df2.set_index(['month', 'group'], inplace=True)

df2 中的startend 可用于定义间隔。我的真实数据要大得多,df1 大约 100,000 行,df2 大约 10,000 行。

我想做的是将period变量分配给df1,匹配monthgroup,如果df1['time']df2的区间内。

上述问题中提出的问题有两个复杂性:

  1. IntervalIndex 需要只是MultiIndex 的一部分,并且需要在索引的所有三个级别上进行匹配。

  2. 有时df1 中的时间与df2 中的时间间隔不匹配。如果这些行填充有NaN 或其他缺失值,或者它们是否被完全删除,我没有强烈的偏好,但我不能拥有KeyError

我的第一步是:

df2Index = pd.IntervalIndex.from_arrays(df2['start'], df2['end'], closed='left')
df2.set_index(df2Index, append=True, inplace=True)

将 IntervalIndex 应用于 df2,同时将 monthgroup 保留为更高级别的索引。

我尝试了一些方法,例如

period = df2.loc[df2.index.get_indexer(df1.index), 'period']
period = df2.get_loc(df1.index), 'period']

但我一直无法获得正确的索引行为。为了完整起见,我想要的结果是:

    month   group   time    distance    period
0   1       A       10      100         1
1   1       A       20      120         2
2   1       A       25      110         3
3   1       B       5       90          1
4   1       B       7       99          1
5   1       B       17      60          2
6   1       C       30      200         NaN
7   1       C       35      170         NaN
8   2       A       5       40          1
9   2       A       10      27          2
10  2       B       17      33          1
11  2       B       19      65          1

(或同一张表,但没有periodNaN 的两行)。

我的备份计划是遍历monthgroup 的相关组合,适当地对两个表进行子集化,在这些子集表上使用IntervalIndex,然后重新组合它们。但这似乎是错误的解决方案。

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    我会使用左连接将merge 两个帧放在一起,然后过滤以仅显示time 在所需startend 期间内的那些行:

    import pandas as pd
    
    # setup the frame
    df1 = pd.DataFrame(
        data={
            'month': [1,1,1,1,1,1,1,1,2,2,2,2],
            'group': ['A','A','A','B','B','B','C','C','A','A','B','B'],
            'time': [10,20,25,5,7,17,30,35,5,10,17,19],
            'distance': [100,120,110,90,99,60,200,170,40,27,33,65],
        })
    
    df2 = pd.DataFrame(
        data={
            'month': [1,1,1,1,1,2,2,2],
            'group': ['A','A','A','B','B','A','A','B'],
            'start': [5,15,21,2,10,3,8,10],
            'end': [15,21,30,10,20,8,15,20],
            'period': [1,2,3,1,2,1,2,1],
        })
    
    # merge, and filter
    df = df1.merge(df2, how='left', on=['month','group'])
    df = df[(df.time >= df.start) & (df.time <= df.end)][['month','group','time','distance','period']].reset_index(drop=True)
    
    print df
    
        month   group   time    distance    period
    0   1       A       10      100         1.0
    1   1       A       20      120         2.0
    2   1       A       25      110         3.0
    3   1       B       5       90          1.0
    4   1       B       7       99          1.0
    5   1       B       17      60          2.0
    6   2       A       5       40          1.0
    7   2       A       10      27          2.0
    8   2       B       17      33          1.0
    9   2       B       19      65          1.0
    

    请注意,上述框架不包括 NaN。如果需要,请更新过滤条件:df[((df.time &gt;= df.start) &amp; (df.time &lt;= df.end)) | (df.period.isnull())]

    【讨论】:

    • 所以这可能会奏效,我会试一试,但我担心的是可扩展性。在我的真实数据集中,df1 至少有几十万行(大约 15 列),df2 有数万行。我担心从性能角度来看,这种左合并不是最佳的。
    • 是的,上述解决方案可能不适用于较大的数据集(确保发布数据集的一般大小,以便我们为您提供适当的答案)。您可能需要研究 pandas HDF 功能。
    【解决方案2】:

    我最近遇到了这个问题,由于我没有遇到使用 MultiIndex 的解决方案,我认为您可能需要对分组数据进行操作并在之后进行连接。

    使用您的 df1 和 df2:

    df1_grouped = df1.groupby(['month','group'])
    results = []
    for gid, grp in df1_grouped:
        # Filter df2 for the group of df1
        df2_grouped = df2[(df2['month']==gid[0]) & (df2['group']==gid[1])]
        
        # Create the group interval index for the lookup table
        int_idx = pd.IntervalIndex.from_arrays(df2_grouped['start'], df2_grouped['end'], 'left')
        
        if len(int_idx) > 0:
            # Use the interval index and slice the lookup table based on the group of df1
            results.append(grp.assign(period=df2_grouped.set_index(int_idx).loc[grp['time']]['period'].values))
        else:
            results.append(grp)
    pd.concat(results).reset_index(drop=True)
    

    输出:

        month   group   time    distance    period
    0   1   A   10  100 1.0
    1   1   A   20  120 2.0
    2   1   A   25  110 3.0
    3   1   B   5   90  1.0
    4   1   B   7   99  1.0
    5   1   B   17  60  2.0
    6   1   C   30  200 NaN
    7   1   C   35  170 NaN
    8   2   A   5   40  1.0
    9   2   A   10  27  2.0
    10  2   B   17  33  1.0
    11  2   B   19  65  1.0
    

    【讨论】:

      【解决方案3】:

      我现在正在尝试这样做。我的方法是使用

      创建一个连接索引列
      df.ID.str + df.date.astype(str).replace('-','').
      

      【讨论】:

        猜你喜欢
        • 2018-03-04
        • 2020-03-02
        • 2012-12-02
        • 2022-12-07
        • 1970-01-01
        • 2021-10-23
        • 2015-07-29
        • 1970-01-01
        • 2018-03-09
        相关资源
        最近更新 更多