是否可以在 Python 中加速这个循环?

     2023-02-25     166

关键词:

【中文标题】是否可以在 Python 中加速这个循环?【英文标题】:Is it possible to speed up this loop in Python? 【发布时间】:2017-11-30 04:47:34 【问题描述】:

numpy.narray(如np.array[map(some_func,x)]vectorize(f)(x))中映射函数的常规方法无法提供索引。 以下代码只是一个在许多应用程序中常见的简单示例。

dis_mat = np.zeros([feature_mat.shape[0], feature_mat.shape[0]])

for i in range(feature_mat.shape[0]):
    for j in range(i, feature_mat.shape[0]):
        dis_mat[i, j] = np.linalg.norm(
            feature_mat[i, :] - feature_mat[j, :]
        )
        dis_mat[j, i] = dis_mat[i, j]

有没有办法加快速度?


感谢您的帮助!加速此代码的最快方法是这样,使用@user2357112 评论的函数:

    from scipy.spatial.distance import pdist,squareform
    dis_mat = squareform(pdist(feature_mat))

@Julien 的method 如果feature_mat 很小也不错,但是当feature_mat 到2000 年达到1000 时,则需要将近40 GB 的内存。

【问题讨论】:

Are you looking for this? @Marco。 numba 听起来有点矫枉过正。这看起来可以直接矢量化,或者至少这是我的预感。我的直觉说要看 ufunc.outer。 顺便说一句,在数组上应用函数的“正常方式”不应该是mapvectorize;这些应该保留用于无法应用更有效方法的情况,或者您必须快速编写一些内容并且性能影响不会成为问题的情况。 user167122,这里的礼仪是您不要在问题中添加答案。您是否考虑将您上次编辑的问题恢复为问题(修订版 5)? 如果您认为 user2357112 的解决方案最好,您为什么接受 Julien 的回答? 【参考方案1】:

SciPy 带有一个专门用于计算您正在计算的成对距离类型的函数。它是scipy.spatial.distance.pdist,它以压缩格式生成距离,基本上只存储距离矩阵的上三角形,但您可以使用scipy.spatial.distance.squareform 将结果转换为正方形:

from scipy.spatial.distance import pdist, squareform

distance_matrix = squareform(pdist(feature_mat))

这样做的好处是避免了直接矢量化解决方案所需的巨大中间数组,因此速度更快,并且适用于更大的输入。不过,它失去了an approach that uses algebraic manipulations to have dot handle the heavy lifting 的时机。

pdist 还支持多种替代距离指标,如果您决定想要欧几里得距离以外的其他指标。

# Manhattan distance!
distance_matrix = squareform(pdist(feature_mat, 'cityblock'))

# Cosine distance!
distance_matrix = squareform(pdist(feature_mat, 'cosine'))

# Correlation distance!
distance_matrix = squareform(pdist(feature_mat, 'correlation'))

# And more! Check out the docs.

【讨论】:

【参考方案2】:

您可以创建一个新轴并广播:

dis_mat = np.linalg.norm(feature_mat[:,None] - feature_mat, axis=-1)

时间:

feature_mat = np.random.rand(100,200)

def a():
    dis_mat = np.zeros([feature_mat.shape[0], feature_mat.shape[0]])
    for i in range(feature_mat.shape[0]):
        for j in range(i, feature_mat.shape[0]):
            dis_mat[i, j] = np.linalg.norm(
                feature_mat[i, :] - feature_mat[j, :]
            )
            dis_mat[j, i] = dis_mat[i, j]

def b():
    dis_mat = np.linalg.norm(feature_mat[:,None] - feature_mat, axis=-1)



%timeit a()
100 loops, best of 3: 20.5 ms per loop

%timeit b()
100 loops, best of 3: 11.8 ms per loop

【讨论】:

feature_mat[:,None] - feature_mat 可能很大。您可能会在相当大的输入上耗尽您的 RAM。 速度和内存之间的常见困境... :) 感谢您的建议。总时间从 6363 ms 减少到 967 ms。而且它真的很容易理解和阅读。【参考方案3】:

考虑可以做什么,并在k x k 矩阵上使用np.dot 优化,在小内存位置(kxk):

def c(m): 
    xy=np.dot(m,m.T) # O(k^3)
    x2=y2=(m*m).sum(1) #O(k^2)
    d2=np.add.outer(x2,y2)-2*xy  #O(k^2)
    d2.flat[::len(m)+1]=0 # Rounding issues
    return np.sqrt(d2)  # O (k^2)

为了比较:

def d(m):
   return  squareform(pdist(m))

这里是 k*k 初始矩阵的“时间(它)”:

这两种算法都是 O(k^3),但是 c(m) 通过 np.dot 使 O(k^3) 成为工作的一部分,这是线性代数的关键节点,它对所有 optimizations(多核)都有好处等等)。 pdist 只是在 source 中看到的循环。

这解释了大数组的 15 倍因子,即使 pdist 通过仅计算一半项来利用矩阵的对称性。

【讨论】:

哦,是的,这个解决方案。我很确定我以前在另一个答案中见过它,但我不记得了。没有巨大的中间体,你得到一个 BLAS 矩阵乘法来完成大部分工作(大部分?)。性能应该还不错。我不记得它是否击败了pdist,但无论哪种方式我都不会感到惊讶;上次我检查时,pdist 并没有像 BLAS 矩阵乘法那样优化。 我可能记得 this answer 的内容,但我觉得我看到了 NumPy 版本。无论如何,我的时间安排说它与pdist 竞争,在更大的阵列上获胜,但在更小的阵列上输了。通过更多的优化,它可能会在较小的输入上击败pdist (不过,squareformpdist 输出需要足够长的时间才能在小输入上与此答案相关联。)【参考方案4】:

我想到避免混合 NumPy 和 for 循环的一种方法是使用允许替换的版本 of this index creator 创建一个索引数组:

import numpy as np
from itertools import product, chain
from scipy.special import comb

def comb_index(n, k):
    count = comb(n, k, exact=True, repetition=True)
    index = np.fromiter(chain.from_iterable(product(range(n), repeat=k)),
                        int, count=count*k)
    return index.reshape(-1, k)

然后,我们简单地获取这些数组对中的每一个,计算它们之间的差异,重塑结果数组,并获取数组中每一行的范数:

reshape_mat = np.diff(feature_mat[comb_index(feature_mat.shape[0], 2), :], axis=1).reshape(-1, feature_mat.shape[1])
dis_list = np.linalg.norm(reshape_mat, axis=-1)

请注意,dis_list 只是所有n*(n+1)/2 可能的norms 的列表。这与他提供的feature_mat 的另一个答案的运行速度几乎相同,并且在比较我们最大部分的字节大小时,

(feature_mat[:,None] - feature_mat).nbytes == 16000000

同时

np.diff(feature_mat[comb_index(feature_mat.shape[0], 2), :], axis=1).reshape(-1, feature_mat.shape[1]).nbytes == 8080000

对于大多数输入,我的只使用了一半的存储空间:仍然不是最优的,但有边际改进。

【讨论】:

【参考方案5】:

基于np.triu_indices,如果你真的想用纯 NumPy 做到这一点:

s = feature_mat.shape[0]
i, j = np.triu_indices(s, 1)         # All possible combinations of indices
dist_mat = np.empty((s, s))          # Don't waste time filling with zeros
np.einsum('ii->i', dist_mat)[:] = 0  # When you can just fill the diagonal
dist_mat[i, j] = dist_mat[j, i] = np.linalg.norm(feature_mat[i] - feature_mat[j], axis=-1)
                                     # Vectorized version of your original process

这种方法相对于广播的好处是你可以分块进行:

n = 10000000   # Based on your RAM available
for k in range (0, i.size, n):
    i_ = i[k: k + n]
    j_ = j[k: k + n]
    dist_mat[i_, j_] = dist_mat[j_, i_] = \
                     np.linalg.norm(feature_mat[i_] - feature_mat[j_], axis = -1)

【讨论】:

【参考方案6】:

让我们首先用函数重写它:

dist(mat, i, j):
    return np.linalg.norm(mat[i, :] - mat[j, :])

size = feature_mat.shape[0]

for i in range(size):
    for j in range(size):
        dis_mat[i, j] = dist(feature_mat, i, j)

这可以用(稍微多一点的)矢量化形式重写为:

v = [dist(feature_map, i, j) for i in range(size) for j in range(size)]
dist_mat = np.array(v).reshape(size, size)

请注意,我们仍然依赖 Python 而不是 NumPy 进行一些计算,但这是朝着向量化迈出的一步。还要注意dist(i, j) 是对称的,所以我们可以进一步减少大约一半的计算量。或许考虑:

v = [dist(feature_map, i, j) for i in range(size) for j in range(i + 1)]

现在棘手的一点是将这些计算值分配给dist_mat 中的正确元素。

它的执行速度取决于计算dist(i, j) 的成本。对于小的feature_mats,重新计算的成本还不够高,不用担心这个。但对于大型矩阵,您绝对不想重新计算。

【讨论】:

考虑你的代码的结果, v 可能是一个列表?

Python - 循环加速 - 大型数据集

】Python-循环加速-大型数据集【英文标题】:Python-Speed-upforloops-largedatasets【发布时间】:2021-06-2919:16:29【问题描述】:我是Python新手,我需要加速这个简单的代码。我在Matlab中创建了这段代码,它“立即”运行。我试图在Python中... 查看详情

是否可以在python中循环遍历运算符(大于/小于)?

】是否可以在python中循环遍历运算符(大于/小于)?【英文标题】:Isitpossibletoloopthroughoperators(greaterthan/lessthan)inpython?【发布时间】:2020-10-2209:38:09【问题描述】:我希望能够遍历关系运算符。我有以下代码工作:TP=df[(df.Truth==1... 查看详情

是否可以循环这个 createjs.sound?

】是否可以循环这个createjs.sound?【英文标题】:Isitpossibletoloopthiscreatejs.sound?【发布时间】:2018-05-1501:01:53【问题描述】:我使用这个脚本可以打开和关闭我的音频。但是,因为我没有选项可以在我的时间线上单击并循环播放我... 查看详情

如何加速这个 Python 循环

】如何加速这个Python循环【英文标题】:HowtoSpeedUpThisPythonLoop【发布时间】:2022-01-2118:49:10【问题描述】:downloadStart=datetime.now()while(True):requestURL=transactionAPI.format(page=tempPage,limit=5000)response=requests.get(requestURL,headers=h 查看详情

在bash中循环元组?

...ertuplesinbash?【发布时间】:2012-03-3113:40:38【问题描述】:是否可以在bash中循环元组?例如,如果以下方法可行,那就太好了:for(i,j)in((c,3),(e,5));doecho"$iand$j";done是否有一种解决方法可以让我循环遍历元组?【问题讨论】:来自pyt... 查看详情

在 Python 中使用 if 条件加速逐行循环

】在Python中使用if条件加速逐行循环【英文标题】:Speedinguprow-by-rowloopwithif-conditioninPython【发布时间】:2018-10-2217:46:03【问题描述】:我有一个600万行的数据集,列是:symbol、timeStamp、openprice和closeprice。我运行以下循环,虽然非... 查看详情

是否可以在 for 循环中添加两个浮点数?

】是否可以在for循环中添加两个浮点数?【英文标题】:Isitpossibletoaddtwofloatinsidetheforloop?【发布时间】:2019-12-3109:12:54【问题描述】:我正在用C++创建一个简单的成绩计算器,我是C++的新手,我正在练习它,但是我的方法或代码... 查看详情

是否可以在 Android 中运行 python libsvm?

】是否可以在Android中运行pythonlibsvm?【英文标题】:IsitpossibletorunpythonlibsvminAndroid?【发布时间】:2013-11-1119:53:06【问题描述】:我正在android中开发加速度计模式识别应用程序。问题是否可以在Android中设置pythonlibsvm?如果可能,... 查看详情

是否可以使用 for 循环在构造函数中声明二维数组类成员?

】是否可以使用for循环在构造函数中声明二维数组类成员?【英文标题】:Isitpossibletodeclarea2darrayclassmemberinsidetheconstructorwithaforloop?【发布时间】:2016-05-1519:42:11【问题描述】:目前我有这个//gameboard.hclassGameBoardpublic:GameBoard(boolsh... 查看详情

是否可以在调试期间跳过任意数量的循环?视觉工作室

】是否可以在调试期间跳过任意数量的循环?视觉工作室【英文标题】:Isitpossibletoskipoveranabitraryamountofaloopduringdebug??VisualStudio【发布时间】:2010-05-2423:07:10【问题描述】:我试图在我的代码中查找错误。问题是错误发生在循环... 查看详情

Python中可能有无限的for循环吗?

...on?【发布时间】:2016-03-1903:56:27【问题描述】:for循环中是否可以无限循环?我的猜测是Python中可能存在无限的for循环。我想知道这一点以供将来参考。【问题讨论】:我在寻找类似的东西,发现了这个:***.com/questions/9884213/…L... 查看详情

For循环中的Python最后一次迭代

...题,我们可以提出替代解决方案?我想知道标准的csv模块是否可以以另一种方式解决您的问题。我不确定为什么对这个问题的回复都没有 查看详情

是否可以在swift中使用加速框架计算两个已知点之间的线性插值

】是否可以在swift中使用加速框架计算两个已知点之间的线性插值【英文标题】:Isitpossibletocalculatelinearinterpolationbetweentwoknownpointswithaccelerateframeworkinswift【发布时间】:2017-11-0506:24:36【问题描述】:作为标题:示例:一行->p1(x:... 查看详情

是否可以在 python 中并行创建字典?

】是否可以在python中并行创建字典?【英文标题】:Isitpossibletoparallelisecreationofadictinpython?【发布时间】:2020-06-2013:30:52【问题描述】:我实际上有这个代码来创建一个字典。importmultiprocessingcpus=multiprocessing.cpu_count()pool=multiprocessi... 查看详情

是否可以通过在 matlab 中调用 c/c++ 代码来加速 matlab 绘图?

】是否可以通过在matlab中调用c/c++代码来加速matlab绘图?【英文标题】:isitpossibletospeed-upmatlabplottingbycallingc/c++codeinmatlab?【发布时间】:2012-02-1507:37:05【问题描述】:在Matlab中调用mex文件(用c/c++编写)来加速某些计算通常非常... 查看详情

有啥方法可以在for循环中保存多个图而不用python覆盖?

】有啥方法可以在for循环中保存多个图而不用python覆盖?【英文标题】:anywaytosavemultipleplotsinforloopwithoutoverwritinginpython?有什么方法可以在for循环中保存多个图而不用python覆盖?【发布时间】:2020-06-0319:53:37【问题描述】:我有... 查看详情

是否可以在 qml 中循环滑动项目?

】是否可以在qml中循环滑动项目?【英文标题】:Isitpossibletomakeacycleswipeitemsinqml?【发布时间】:2018-04-1523:28:34【问题描述】:我在qml中使用swipeview。我可以将项目从第一个到最后一个再向后滑动。是否可以立即从最终滑到第一... 查看详情

这个css的animation怎么使两次循环间不出现中断,不是循环一次顿一下然后再继续下次循环,

...操作方法:第一步:调整键盘设置(如果您不习惯这样也可以不用更改设置)首先,我们建议您更改游戏中的键盘设置,系统默认的设置对于操作还不太熟练的玩家来说有些小小的阻碍。更改路径:游戏设置—按键设置玩家推荐... 查看详情