Python Pandas 特征生成作为聚合函数

Python Pandas Feature Generation as aggregate function

我有一个 pandas df 有点像

        ID  key dist
   0    1   57  1
   1    2   22  1
   2    3   12  1
   3    4   45  1
   4    5   94  1
   5    6   36  1
   6    7   38  1
   .....

这个 DF 包含几百万个点。我现在正在尝试生成一些描述符以合并数据的时间性质。这个想法是我应该为每一行创建一个长度为 x 的 window 返回数据并计算 window 中特定键的出现次数。我做了一个实现,但根据我对 23 个不同 windows 的估计,计算将 运行 32 天。这是代码

def features_wind2(inp):
   all_window = inp
   all_window['window1'] = 0
   for index, row in all_window.iterrows():
      lid = index
      lid1 = lid - 200
      pid = row['key']
      row['window1'] = all_window.query('index < %d & index > %d & key == %d' % (lid, lid1, key)).count()[0]     
   return all_window

有多个不同的 windows 长度不同。然而,我有一种不安的感觉,即迭代可能不是进行此数据聚合的最明智的方法。有没有办法更快地实现 运行?

在玩具示例数据框上,您可以通过使用 apply() 而不是 iterrows() 实现大约 7 倍的加速。

这是一些示例数据,从 OP 扩展了一点以包含多个 key 值:

    ID  key dist
0    1   57  1
1    2   22  1
2    3   12  1
3    4   45  1
4    5   94  1
5    6   36  1
6    7   38  1
7    8   94  1
8    9   94  1
9   10   38  1

import pandas as pd
df = pd.read_clipboard()

根据这些数据和 OP 定义的计数标准,我们预计输出为:

    key  dist  window
ID                   
1    57     1       0
2    22     1       0
3    12     1       0
4    45     1       0
5    94     1       0
6    36     1       0
7    38     1       0
8    94     1       1
9    94     1       2
10   38     1       1

使用OP的方法:

def features_wind2(inp):
    all_window = inp
    all_window['window1'] = 0
    for index, row in all_window.iterrows():
        lid = index
        lid1 = lid - 200
        pid = row['key']
        row['window1'] = all_window.query('index < %d & index > %d & key == %d' % (lid, lid1, pid)).count()[0]     
    return all_window

print('old solution: ')
%timeit features_wind2(df) 

old solution: 
10 loops, best of 3: 25.6 ms per loop

使用apply():

def compute_window(row):
    # when using apply(), .name gives the row index
    # pandas indexing is inclusive, so take index-1 as cut_idx
    cut_idx = row.name - 1 
    key = row.key
    # count the number of instances key appears in df, prior to this row
    return sum(df.ix[:cut_idx,'key']==key)

print('new solution: ')
%timeit df['window1'] = df.apply(compute_window, axis='columns')

new solution: 
100 loops, best of 3: 3.71 ms per loop

请注意,对于数百万条记录,这仍然需要一段时间,并且与这个小测试用例相比,相对性能提升可能会有所减弱。

更新
这是一个更快的解决方案,使用 groupby()cumsum()。我做了一些样本数据,看起来与提供的示例大致一致,但有 1000 万行。平均计算时间不到一秒:

# sample data
import numpy as np
import pandas as pd

N = int(1e7)
idx = np.arange(N)
keys = np.random.randint(1,100,size=N)
dists = np.ones(N).astype(int)
df = pd.DataFrame({'ID':idx,'key':keys,'dist':dists})
df = df.set_index('ID')

现在进行性能测试:

%timeit df['window'] = df.groupby('key').cumsum().subtract(1)

1 loop, best of 3: 755 ms per loop

这里有足够的输出表明计算正在运行:

    dist  key  window
ID                   
0      1   83       0
1      1    4       0
2      1   87       0
3      1   66       0
4      1   31       0
5      1   33       0
6      1    1       0
7      1   77       0
8      1   49       0
9      1   49       1
10     1   97       0
11     1   36       0
12     1   19       0
13     1   75       0
14     1    4       1

注意:要将 ID 从索引还原为列,请在末尾使用 df.reset_index()