为什么Python代码要花这么长时间才能在数据集中找到接近点?
我试图制作一个高效的Python代码,在给定表单中的一组数据点的情况下为什么Python代码要花这么长时间才能在数据集中找到接近点?,python,performance,numpy,Python,Performance,Numpy,我试图制作一个高效的Python代码,在给定表单中的一组数据点的情况下 a1 a2 a3 ... an (point 1) b1 b2 b3 ... bn (point 2) . . . 将查找其中哪些太接近(在阈值内) 我已经有了以下Fortran例程来解决这个问题: program check_closeness implicit none
a1 a2 a3 ... an (point 1)
b1 b2 b3 ... bn (point 2)
.
.
.
将查找其中哪些太接近(在阈值内)
我已经有了以下Fortran例程来解决这个问题:
program check_closeness
implicit none
integer, parameter :: npoints = 10000, ndis = 20
real(8), parameter :: r = 1e0, maxv = 3e0, minv = 0e0
real(8), dimension(npoints,ndis) :: dis
real(8) :: start, ende
call RANDOM_NUMBER(dis)
dis = (maxv - minv) * dis + minv
call cpu_time(start)
call remove_close(npoints, ndis,dis, r)
call cpu_time(ende)
write(*,*) 'Time elapsed', ende - start
endprogram
subroutine remove_close(npoints, ndis, points, r)
implicit none
integer, intent(in) :: npoints, ndis
integer :: i, i_check
real(8), dimension(npoints, ndis), intent(in) :: points
real(8), intent(in) :: r
logical :: is_close
do i=1,npoints
is_close = .FALSE.
i_check = i
do while (.not. is_close .and. i_check < npoints)
i_check = i_check + 1
is_close = all(abs(points(i,:) - points(i_check,:)) < r)
enddo
enddo
end subroutine
此执行耗时102.60617995262146(秒),比Fortran版本慢得多
我尝试了这个例程的另一个Python版本,它的速度要快得多,但仍然无法接近Fortran版本:
import numpy as np
import time
def check_close(a, r):
for i, vec1 in enumerate(a[:-1]):
d = np.abs(a[i+1:,:] - vec1)
is_close = np.any(np.all(d < r, axis=1))
maxv, minv = 3, 0
a = np.random.rand(10000, 20)
a = (maxv - minv) * a + minv
r = 1e0
ini = time.time()
check_close(a, r)
fin = time.time()
print('Time elapsed {}'.format(fin - ini))
测量它们和更新时间如下:
Python实现1:经过的时间98.63728801719844(秒)
Python实现2:耗时3.3923211600631475(秒)之所以创建Numpy,是因为Python速度非常慢。正如您所展示的,fortran代码在没有太多算法差异的情况下速度更快。 因此,要回答您的问题:
顺便说一句,我不确定您使用
np.abs()
的目的是什么,但它不是在计算两点之间的欧几里德距离。使用np.linalg.norm()
。我在我的机器上尝试了两种方法,都不到3秒钟,但还没有FORTRAN那么快
我生成了类似的随机数据,但增加了r,否则我得到了零个案例
import time
import numpy as np
maxv, minv = 3, 0
a = np.random.rand(10000, 20)
a = (maxv - minv) * a + minv
r = 3e0
1-BallTree(sklearn)
给
Balltree (sklearn) based - time elapsed 1.5845096111297607
pdist (scipy) based - time elapsed 0.6769788265228271
编辑:奇怪(有人能解释吗?)一个疯狂的高leafsize=5000
在我的情况下效果最好。通常leafsize
约为10
2-pdist(scipy)
pdist
返回点的压缩形式标识,因此需要执行一些(i,j)
索引管理,但这是通过vectors=fast完成的。您可以在Python中做得更好,但这需要一些工作。令人惊讶的是,我认为您仅使用NumPy的方法与您在不涉及其他模块的情况下所能获得的方法一样好。然而,如果你愿意使用numba,你甚至可以比Fortran做得更好
下面的函数返回闭合向量。它实际上是用Python编写的C代码,然后使用numba将其编译为本机代码
将numpy导入为np
从数学进口工厂
来自numba import njit
@njit
def检查关闭2(a,r):
n=a.形状[0]
m=a.形状[1]
#选择数组,如果以后要使用它
close=np.zero(n,dtype=np.bool_u2;)
对于范围(n)中的i:
对于范围(i+1,n)内的j:
对于范围内的k(m):
#短路比较
last=fabs(a[i,k]-a[j,k])>=r
如果最后:
打破
#如果我们到了这里,我们就找到了一条近路
如果k==(m-1)且不是最后一个:
#一旦找到关闭值,停止查找
关闭[i]=1
打破
回程结束
当我在我的机器上使用%timeit
运行它时,我得到
%timeit check_close_nb2(a, r)
544 ms ± 4.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
当我运行上面的代码时,我看到
%timeit check_close(a, r)
4.91 s ± 69.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
因此,我的比较表明,我的机器比你的机器慢一点,建议numba版本的时间大约为400ms。不要使用
time.time
对代码进行基准测试,因为它可以测量墙时间(即,它包括系统上运行的所有内容)。要么使用,要么使用。然后,请用新的数字更新您的问题。@a_客人,我不认为缩短10%的时间会有什么帮助。请注意,这可能不是一种特别有效的方法。首先将点放入宽度为r的箱子中。然后,唯一可以靠近的点位于与参考点相同的箱子中,或者位于相邻的箱子中。生成的算法将是O(N),而不是这里的O(N**2)。对于大N,这可能会使代码的数量级比您在这里的速度快,无论您选择何种语言。我使用的是may,因为我只在3D中使用过这种方法,而您的维度更高一些。@Pytity我不理解您的评论。通过time报告的数字。time
不可靠,因此我要求更新这些数字。还要注意,real(8)在Fortran中不可移植,对于某些编译器将失败。了解内部iso_fortran_env模块,并参见类似的fortran norm2计算数组的2-范数。但是如果你真的想要性能,你会避开平方根,比较长度的平方,不知道你从哪里得到了欧几里德距离。OP显然实现了切比雪夫距离。这可能与问答无关。非常感谢。注意,我刚刚更新了Fortran时间,我犯了一个错误,所以您的第二个方法实际上在时间上更好。它的问题是内存。对于我的典型问题,我必须加载大约1e5个数据点,因此距离矩阵无法存储在内存中。@pablo您应该在问题帖中提到约束(距离矩阵无法存储在内存中)。pdist不是存储完整的距离矩阵,而是大约一半的距离矩阵。可能正是内存问题出现的时候。balltree和pdist支持多个距离度量,这也是一个好消息。我对所使用的metic做出了(错误的?)假设。pdist解决方案给出的答案与原始帖子的答案不一样。您需要更改度量值吗?看起来应该是“切比雪夫”。这也会在我的机器上大大加快解决方案的速度(670ms->400ms),并生成正确的答案(在np.unique
之后,用于重复数据消除i
)。
from scipy.spatial.distance import pdist
ini = time.time()
condensed_distance_small = pdist(a) <= r
condensed_indici = np.argwhere(condensed_distance_small)
n = 10000
#
# i,j contain the indici of points that are within r of each other
# based on condensed-indici formulas of
# https://stackoverflow.com/questions/13079563/how-does-condensed-distance-matrix-work-pdist
i = np.ceil((1/2.) * (- (-8*condensed_indici + 4 *n**2 -4*n - 7)**0.5 + 2*n -1) - 1).astype(int)
elem_in_i_rows = (i+1) * (n - 1 - (i+1)) + ((i+ 1)*(i + 2))//2
j = (n - elem_in_i_rows + condensed_indici)
fin = time.time()
print('pdist (scipy) based - time elapsed {}'.format(fin - ini))
pdist (scipy) based - time elapsed 0.6769788265228271
%timeit check_close_nb2(a, r)
544 ms ± 4.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit check_close(a, r)
4.91 s ± 69.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)