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()
。
我有一个 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()
。