关键词:
【中文标题】是否可以在 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。 顺便说一句,在数组上应用函数的“正常方式”不应该是map
或vectorize
;这些应该保留用于无法应用更有效方法的情况,或者您必须快速编写一些内容并且性能影响不会成为问题的情况。
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
。
(不过,squareform
pdist
输出需要足够长的时间才能在小输入上与此答案相关联。)【参考方案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_mat
s,重新计算的成本还不够高,不用担心这个。但对于大型矩阵,您绝对不想重新计算。
【讨论】:
考虑你的代码的结果, 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怎么使两次循环间不出现中断,不是循环一次顿一下然后再继续下次循环,
...操作方法:第一步:调整键盘设置(如果您不习惯这样也可以不用更改设置)首先,我们建议您更改游戏中的键盘设置,系统默认的设置对于操作还不太熟练的玩家来说有些小小的阻碍。更改路径:游戏设置—按键设置玩家推荐... 查看详情