【问题标题】:Increasing performance of nearest neighbors of rows in Pandas提高 Pandas 中行最近邻的性能
【发布时间】:2018-11-20 22:29:52
【问题描述】:

我得到了类似这个的 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) 相同(但除此之外的任何东西都可能变得不合理)

【问题讨论】:

  • 如果您计划预先计算所有数据点的最近点,我建议您使用 Apache Spark,它为您提供针对此类用例的开箱即用并行处理。事实上,它几乎没有用于类似工作的内置方法。例如:它已经有一个推荐系统的 ALS
  • 很难概括 w.r.t. Q1 和 Q2。在某种程度上,您正在对同质类型的规则形状数组中的数据进行纯粹的数字处理,n​​umpy 将比 pandas 快很多(请注意,我添加了一个 numpy 标记 btw)。所以我会从寻找好的基于 numpy 的方法开始。除此之外,也许看看 numba、numexpr、cython。

标签: python numpy pandas


【解决方案1】:

您最大(也是最简单)的性能提升可能来自仅在 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

【讨论】:

  • 哇,谢谢。我觉得这有点令人惊讶,因为我认为数据框是 numpy 数组的子类。
  • 很好的答案。这里的技巧是使用numpy 而不是pandas。感谢您的有用建议。 python 3.6更新:使用sort_values("dist")替换sort("dist")
猜你喜欢
  • 2014-07-11
  • 2013-08-22
  • 2011-08-10
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 2014-10-31
  • 2019-01-24
  • 2018-11-29
相关资源
最近更新 更多