我获得了与此类似的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)相同(但不止于此,可能会变得不合理)
答案 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