提高Pandas中行的最近邻居的性能

时间:2015-05-27 18:14:03

标签: python numpy pandas

我获得了与此类似的8000x3数据集:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.rand(8000,3), columns=list('XYZ'))

因此,对于视觉参考,df.head(5)看起来像这样:

          X         Y         Z
0  0.462433  0.559442  0.016778
1  0.663771  0.092044  0.636519
2  0.111489  0.676621  0.839845
3  0.244361  0.599264  0.505175
4  0.115844  0.888622  0.766014

我正在尝试实现一种方法,当从数据集中获取索引时,它将从数据集中返回类似的项目(以某种合理的方式)。现在我有:

def find_similiar_items(item_id):
    tmp_df = df.sub(df.loc[item_id], axis='columns')
    tmp_series = tmp_df.apply(np.square).apply(np.sum, axis=1)
    tmp_series.sort()
    return tmp_series

此方法获取您的行,然后从数据框中的每个其他行中减去它,然后计算每行的范数。所以这个方法只是使用欧氏距离返回给定点的一系列最近点。

所以你可以得到最接近的5分,例如:

df.loc[find_similiar_items(5).index].head(5)

产生:

             X         Y         Z
5     0.364020  0.380303  0.623393
4618  0.369122  0.399772  0.643603
4634  0.352484  0.402435  0.619763
5396  0.386675  0.370417  0.600555
3229  0.355186  0.410202  0.616844

这种方法的问题在于每次调用它大约需要半秒钟。这对我来说是不可接受的,所以我需要弄清楚如何在某种程度上提高这种方法的性能。所以我有几个问题:

问题1 是否有一种更有效的方法可以简单地计算上面的欧氏距离?

问题2 是否有其他技术可以产生合理的结果(例如,欧几里德距离不是导入的)。在这个问题上,计算时间比记忆更重要,预处理时间并不重要;所以我愿意,例如,构建一个新的数据帧,其大小与原始数据帧的笛卡尔积(n ^ 2)相同(但不止于此,可能会变得不合理)

1 个答案:

答案 0 :(得分:4)

你最大的(也是最容易的)性能提升可能仅仅是在numpy而不是pandas中做到这一点。从快速转换代码到numpy,我看到了200倍的改进:

arr = df.values
def fsi_numpy(item_id):
    tmp_arr = arr - arr[item_id]
    tmp_ser = np.sum( np.square( tmp_arr ), axis=1 )
    return tmp_ser

df['dist'] = fsi_numpy(5)
df = df.sort_values('dist').head(5)

             X         Y         Z      dist
5     0.272985  0.131939  0.449750  0.000000
5130  0.272429  0.138705  0.425510  0.000634
4609  0.264882  0.103006  0.476723  0.001630
1794  0.245371  0.175648  0.451705  0.002677
6937  0.221363  0.137457  0.463451  0.002883

检查它是否与您的函数给出相同的结果(因为我们有不同的随机抽取):

df.loc[ pd.DataFrame( find_similiar_items(5)).index].head(5)

             X         Y         Z
5     0.272985  0.131939  0.449750
5130  0.272429  0.138705  0.425510
4609  0.264882  0.103006  0.476723
1794  0.245371  0.175648  0.451705
6937  0.221363  0.137457  0.463451

时序:

%timeit df.loc[ pd.DataFrame( find_similiar_items(5)).index].head(5)
1 loops, best of 3: 638 ms per loop

In [105]: %%timeit
     ...: df['dist'] = fsi_numpy(5)
     ...: df = df.sort_values('dist').head(5)
     ...: 
100 loops, best of 3: 2.69 ms per loop