Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python,向量之间的角度:计算效率的需要_Python_Performance_Loops_Math_Angle - Fatal编程技术网

python,向量之间的角度:计算效率的需要

python,向量之间的角度:计算效率的需要,python,performance,loops,math,angle,Python,Performance,Loops,Math,Angle,我有超过500万对的两个3D向量,我需要计算每对向量之间的角度。 我试过: # calculate angle def unit_vector(vector): return vector / np.linalg.norm(vector) def angle_between(v1, v2): v1_u = unit_vector(v1) v2_u = unit_vector(v2) return np.arccos(np.clip(np.dot(v1_u, v

我有超过500万对的两个3D向量,我需要计算每对向量之间的角度。 我试过:

# calculate angle 
def unit_vector(vector):
    return vector / np.linalg.norm(vector)

def angle_between(v1, v2):
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

例如:

a=[[1,1,1],[1,2,1],[6,4,5]]
b=[[1,0,0],[6,2,2],[1,9,2]]

anglevec=np.zeros(len(a))

for i in range(len(a)):
    anglevec[i]=angle_between(a[i], b[i])

print(anglevec)
但是循环的实现太慢了


有人能帮忙吗?

这里有一个使用Pool和Pool.map的解决方案

from multiprocessing import Pool, cpu_count
import numpy as np

def unit_vector(vector):
    return vector / np.linalg.norm(vector)

def angle_between(v1, v2):
    #if you want to maximize speed, avoid making variables
    #v1_u = unit_vector(v1)
    #v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(unit_vector(v1_u),unit_vector(v2_u)),-1.0,1.0))

def calc_angle(_list):
    #use list unpacking instead of instantiate variables
    return angle_between(*_list) 


a=[[1,1,1],[1,2,1],[6,4,5]]
b=[[1,0,0],[6,2,2],[1,9,2]]

with Pool(cpu_count()) as p: #use the context manager
    angles = p.map(calc_angle, zip(a,b))
输出:

>>> angles
[0.9553166181245092, 0.7398807743787404, 0.8775836986593762]

这里是一个使用Pool和Pool.map的解决方案

from multiprocessing import Pool, cpu_count
import numpy as np

def unit_vector(vector):
    return vector / np.linalg.norm(vector)

def angle_between(v1, v2):
    #if you want to maximize speed, avoid making variables
    #v1_u = unit_vector(v1)
    #v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(unit_vector(v1_u),unit_vector(v2_u)),-1.0,1.0))

def calc_angle(_list):
    #use list unpacking instead of instantiate variables
    return angle_between(*_list) 


a=[[1,1,1],[1,2,1],[6,4,5]]
b=[[1,0,0],[6,2,2],[1,9,2]]

with Pool(cpu_count()) as p: #use the context manager
    angles = p.map(calc_angle, zip(a,b))
输出:

>>> angles
[0.9553166181245092, 0.7398807743787404, 0.8775836986593762]
麻木的方法 这项任务很容易实现。 唯一的问题是Python循环非常慢。这可以通过使用Numba或Cython来避免

示例

import numba as nb
import numpy as np

#You can disable parallelization with parallel=False
@nb.njit(fastmath=True,error_model="numpy",parallel=True)
def angle(v1,v2):
    #Check the dimensions, this may also have an effect on SIMD-vectorization
    assert v1.shape[1]==3
    assert v2.shape[1]==3
    res=np.empty(v1.shape[0])

    for i in nb.prange(v1.shape[0]):
        dot=0.
        a=0.
        b=0.
        for j in range(3):
            dot+=v1[i,j]*v2[i,j]
            a+=v1[i,j]**2
            b+=v2[i,j]**2
        res[i]=np.arccos(dot/(np.sqrt(a*b)))
    return res
计时

#Use numpy-arrays when working with arrays
a=np.random.rand(500_000,3)
b=np.random.rand(500_000,3)

%timeit res=your_func(a,b)
5.65 s ± 45.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res=angle(a,b) #without using Numba 
3.15 s ± 13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res=angle(a,b) #with Numba
2.13 ms ± 441 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
麻木的方法 这项任务很容易实现。 唯一的问题是Python循环非常慢。这可以通过使用Numba或Cython来避免

示例

import numba as nb
import numpy as np

#You can disable parallelization with parallel=False
@nb.njit(fastmath=True,error_model="numpy",parallel=True)
def angle(v1,v2):
    #Check the dimensions, this may also have an effect on SIMD-vectorization
    assert v1.shape[1]==3
    assert v2.shape[1]==3
    res=np.empty(v1.shape[0])

    for i in nb.prange(v1.shape[0]):
        dot=0.
        a=0.
        b=0.
        for j in range(3):
            dot+=v1[i,j]*v2[i,j]
            a+=v1[i,j]**2
            b+=v2[i,j]**2
        res[i]=np.arccos(dot/(np.sqrt(a*b)))
    return res
计时

#Use numpy-arrays when working with arrays
a=np.random.rand(500_000,3)
b=np.random.rand(500_000,3)

%timeit res=your_func(a,b)
5.65 s ± 45.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res=angle(a,b) #without using Numba 
3.15 s ± 13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res=angle(a,b) #with Numba
2.13 ms ± 441 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

问题是:你真的需要这个角度吗?对于许多3d(和2d)计算,通常可以跳过角度(arccos)并比较点积本身。Arccos是这里性能最差的一个,点积是由简单的计算组成的。如果你足够绝望,你可以创建一个Arccos查找表,比如说有数千个条目。然后可以将参数缩放并截断为整数,即表中的索引。查找速度很快。你的精度会受到限制,但你可以估计误差,看看你是否能接受。当我预先计算单位向量时,我能做点积而不在a和b上循环吗?问题是:你真的需要角度吗?对于许多3d(和2d)计算,通常可以跳过角度(arccos)并比较点积本身。Arccos是这里性能最差的一个,点积是由简单的计算组成的。如果你足够绝望,你可以创建一个Arccos查找表,比如说有数千个条目。然后可以将参数缩放并截断为整数,即表中的索引。查找速度很快。你的精度是有限的,但你可以估计误差,看看你是否能接受。当我预先计算单位向量时,我能做点积而不在a和b上循环吗?