快速将奇怪的门应用于熊猫数据

时间:2019-06-10 19:54:23

标签: python pandas optimization pandas-groupby

因此,对于一长串奇怪的数据处理问题中的另一个问题,我有一个数据框,如下所示:

                 id        tof
0              10.0  2004847.0
1              10.0  2066116.0
2              10.0  5441996.0
3              10.0  5642443.0
4              15.0  1979815.0
5              15.0  1992399.0
6              15.0  2008208.0
7              15.0  2098060.0
8              15.0  3980280.0
9              27.0  2027878.0
10             27.0  2047992.0
11             27.0  5308106.0
12             27.0  6743403.0

,我想对数据进行门控,按ID分组。我只想保留数据,以使tof总和为一定范围的数字,而不同之处在于一定范围的数字之间。

例如,我将有tof_sum = [7000000,80000000]tof_dif = [3000000,3500000],门将保留行0 2,因为它们的总和在上面的数字之间,它们的差也是如此。由于与第2行的关系,它还将保留第1行。但是,不会保留ID为15的任何行,因为没有两行的总和超过7000000。

我可以想象设置

a = pd.DataFrame([[k, c0, c1] for k, tof in Da.groupby('id').tof
                              for c0, c1 in combinations(tof, 2)
                                ], columns=['id', 'tof0', 'tof1'])

会为每个id成对生成所有组合,然后像这样对它们进行补偿:

      id       tof0       tof1
0   10.0  2004847.0  2066116.0 (sum not bigger than 7000000, next)
1   10.0  2004847.0  5441996.0 (this sum is in range, difference is in range, ✓)
2   10.0  2004847.0  5642443.0 (sum and difference are in range here too, ✓ )
3   10.0  2066116.0  5441996.0 (etc, etc)
4   10.0  2066116.0  5642443.0
5   10.0  5441996.0  5642443.0
6   15.0  1979815.0  1992399.0
7   15.0  1979815.0  2008208.0
8   15.0  1979815.0  2098060.0
9   15.0  1979815.0  3980280.0
10  15.0  1992399.0  2008208.0
11  15.0  1992399.0  2098060.0
12  15.0  1992399.0  3980280.0
13  15.0  2008208.0  2098060.0
14  15.0  2008208.0  3980280.0
15  15.0  2098060.0  3980280.0
16  27.0  2027878.0  2047992.0
17  27.0  2027878.0  5308106.0
18  27.0  2027878.0  6743403.0
19  27.0  2047992.0  5308106.0
20  27.0  2047992.0  6743403.0
21  27.0  5308106.0  6743403.0

并且每次满足求和和差的条件都在各自的范围内时,它将保存带有tof的行。

但是对于要使用的数百万行数据数据文件,此命令花费了惊人的时间(例如数分钟)。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

如评论中所述,处理几分钟的数百万行数据不是很长时间。您可以通过广播对过程进行矢量化处理:

def get_pairs(g):
    x = g.tof.values

    # upper triangle matrix to avoid duplicate pairs: (a,b) and (b,a)
    uniques = np.arange(len(x)) > np.arange(len(x))[:, None]

    sums = x + x[:, None]
    sum_mask = (sums>7000000) & (sums<80000000)

    diffs = np.abs(x - x[:, None])
    diff_mask = (diffs > 3000000) & (diffs < 3500000)

    mask = sum_mask & diff_mask & uniques

    ret_df = pd.DataFrame(mask, index=x, columns=x)
    ret_df = ret_df.stack()
    return ret_df[ret_df]

然后

new_df = df.groupby('id').apply(get_pairs).reset_index().drop(0, axis=1)

输出:

     id    level_1    level_2
0  10.0  2004847.0  5441996.0
1  10.0  2066116.0  5441996.0
2  27.0  2027878.0  5308106.0
3  27.0  2047992.0  5308106.0

稍作修改即可为您提供行的索引,您可以使用它们来过滤原始数据:

def get_pairs(g):
    x = g.tof.values

    # upper triangle matrix
    uniques = np.arange(len(x)) > np.arange(len(x))[:, None]

    sums = x + x[:, None]
    sum_mask = (sums>7000000) & (sums<80000000)

    diffs = np.abs(x - x[:, None])
    diff_mask = (diffs > 3000000) & (diffs < 3500000)

    mask = sum_mask & diff_mask & uniques

    # note the different columns and index.
    ret_df = pd.DataFrame(mask, index=g.index, columns=g.index)
    ret_df = ret_df.stack()
    return ret_df[ret_df]

输出:

     id  level_1  level_2
0  10.0        0        2
1  10.0        1        2
2  27.0        9       11
3  27.0       10       11