【问题标题】:Why cdist from scipy.spatial.distance is so fast?为什么来自 scipy.spatial.distance 的 cdist 这么快?
【发布时间】:2018-08-01 09:41:00
【问题描述】:

我想为 10060 个记录/点创建一个距离接近矩阵,其中每个记录/点具有 23 个使用欧几里德距离作为度量的属性。我使用嵌套的 for 循环编写代码来计算每个点之间的距离(导致 (n(n-1))/2) 计算)。花了很长时间(大约8分钟)。当我使用 cdist 时,它花费的时间要少得多(只需 3 秒!!!)。当我查看源代码时,cdist 还使用嵌套的 for 循环,而且它进行 n^2 计算(这比我的逻辑所做的比较次数要多)。 是什么让 cdist 执行得更快并提供正确的输出? 请帮助我理解。提前致谢。

【问题讨论】:

    标签: python euclidean-distance scipy-spatial


    【解决方案1】:

    您在哪里阅读了源代码? python code 调用(如果你在默认的metric='euclidean' 情况下一直遵循它)c 代码

    static NPY_INLINE int
    cdist_seuclidean(const double *XA, const double *XB, const double *var,
                     double *dm, const npy_intp num_rowsA, const npy_intp num_rowsB,
                     const npy_intp num_cols)
    {
        npy_intp i, j;
    
        for (i = 0; i < num_rowsA; ++i) {
            const double *u = XA + (num_cols * i);
            for (j = 0; j < num_rowsB; ++j, ++dm) {
                const double *v = XB + (num_cols * j);
                *dm = seuclidean_distance(var, u, v, num_cols);
            }
        }
        return 0;
    }
    

    seuclidean_distance 在哪里

    static NPY_INLINE double
    seuclidean_distance(const double *var, const double *u, const double *v,
                        const npy_intp n)
    {
        double s = 0.0;
        npy_intp i;
    
        for (i = 0; i < n; ++i) {
            const double d = u[i] - v[i];
            s += (d * d) / var[i];
        }
        return sqrt(s);
    }
    

    所以它实际上是一个三重循环,但这是高度优化的 C 代码。 Python for 循环很慢,它们会占用大量开销,并且永远不应该与 numpy 数组一起使用,因为 scipy/numpy 可以以 python 无法利用的方式利用 ndarray 对象中保存的底层内存数据。

    【讨论】:

    • 我在“github.com/scipy/scipy/blob/v1.1.0/scipy/spatial/…”这个位置找到了代码。是的,我也明白它是一个三重循环(只是忽略了这样一个事实,即我需要在内部使用第三个循环来计算距离)。但我无法理解,我正在做的计算数量在代码中是相同的(而且我的代码做的计算更少),但它仍然比我的代码运行得快得多,为什么会这样?
    • 因为它是用 C 语言编写的。在 numpy 对象上使用 python 循环非常慢,如果你发现自己这样做了,那你就做错了。除非您将代码粘贴到您的问题中,否则我无法为您提供更多帮助。您已经粘贴了 python 代码,但是除非您传递一个函数,否则该循环实际上并不是正在运行的,实际算法在调用 c 代码的 cdist_fn = getattr(_distance_wrap, "cdist_%s_%s_wrap" % (metric_name, typ)); cdist_fn(XA, XB, dm, **kwargs) 行中。
    • 好的。我认为这可能是唯一的情况,它是用 C 语言编写的,这就是它如此之快的原因。我在python中找到了源代码,所以很困惑。感谢您指出这一点。
    • @SushodhanVaishampayan pandas.pydata.org/pandas-docs/stable/user_guide/… 有一些将 Python 代码转换为 C 的示例(通过 Cython)以及由此产生的加速......
    • 这不是答案。我修改了代码,当使用自定义函数调用时,不涉及 C 代码,而且速度仍然非常快(至少对于 pdist 而言)。这个问题仍然没有答案。
    猜你喜欢
    • 2022-01-14
    • 2018-03-22
    • 2021-12-19
    • 2013-09-09
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 2014-02-05
    相关资源
    最近更新 更多