0

我有一只熊猫 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 的窗口,返回数据并计算窗口中特定键的出现次数。我做了一个实现,但根据我对 23 个不同窗口的估计,计算将运行 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

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

4

1 回答 1

1

在一个玩具示例数据框上,您可以通过使用apply()而不是iterrows().

这是一些示例数据,从 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()and 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()在末尾使用。

于 2017-04-30T20:06:38.937 回答