我已经实施了Pearson的相似度分数,用于比较两个值词典。此方法花费的时间比其他任何地方都多(可能有数百万次调用),因此这显然是优化的关键方法。
即使是最轻微的优化也会对我的代码产生重大影响,所以我很想探索最小的改进。
这是我到目前为止所拥有的:
def simple_pearson(v1,v2):
si = [val for val in v1 if val in v2]
n = len(si)
if n==0: return 0.0
sum1 = 0.0
sum2 = 0.0
sum1_sq = 0.0
sum2_sq = 0.0
p_sum = 0.0
for v in si:
val_1 = v1[v]
val_2 = v2[v]
sum1+=val_1
sum2+=val_2
sum1_sq+=pow(val_1,2)
sum2_sq+=pow(val_2,2)
p_sum+=val_1*val_2
# Calculate Pearson score
num = p_sum-(sum1*sum2/n)
temp = (sum1_sq-pow(sum1,2)/n) * (sum2_sq-pow(sum2,2)/n)
if temp < 0.0:
temp = -temp
den = sqrt(temp)
if den==0: return 1.0
r = num/den
return r
答案 0 :(得分:4)
真正的速度增加将通过移动到numpy或scipy来获得。除此之外,还有微观优化:例如: x*x
比pow(x,2)
快;您可以通过执行而不是:
si = [val for val in v1 if val in v2]
类似
vs = [ (v1[val],v2[val]) for val in v1 if val in v2]
然后
sum1 = sum(x for x, y in vs)
等等;是否每个都带来时间优势需要微基准测试。根据您使用这些系数的方式返回,方形将为您节省一个sqrt(这与使用几何点之间的距离平方,而不是距离本身类似,并且出于同样的原因 - 为您节省一个sqrt ;这是有道理的,因为系数是一个距离,有点......; - )。
答案 1 :(得分:2)
如果你可以使用scipy,你可以使用pearson函数:http://www.scipy.org/doc/api_docs/SciPy.stats.stats.html#pearsonr
或者您可以从http://svn.scipy.org/svn/scipy/trunk/scipy/stats/stats.py复制/粘贴代码(它有自由许可)(搜索def pearson()
)。
在代码中np
只是numpy(代码执行import numpy as np
)。
答案 2 :(得分:2)
Scipy是最快的!
我已经使用上面的代码进行了一些测试,还有我在comp上找到的版本,请参阅下面的结果和代码:
pearson 14.7597990757 sim_pearson 15.6806837987 scipy:pearsonr 0.451986019188
try: import psyco psyco.full() except ImportError: pass from math import sqrt def sim_pearson(set1, set2): si={} for item in set1: if item in set2: si[item] = 1 #number of elements n = len(si) #if none common, return 0 similarity if n == 0: return 0 #add up all the preferences sum1 = sum([set1[item] for item in si]) sum2 = sum([set2[item] for item in si]) #sum up the squares sum_sq1 = sum([pow(set1[item], 2) for item in si]) sum_sq2 = sum([pow(set2[item], 2) for item in si]) #sum up the products sum_p = sum([set1[item] * set2[item] for item in si]) nom = sum_p - ((sum1 * sum2) / n ) den = sqrt( (sum_sq1 - (sum1)**2 / n) * (sum_sq2 - (sum2)**2 / n) ) if den==0: return 0 return nom/den # from http://stackoverflow.com/questions/1307016/pearson-similarity-score-how-can-i-optimise-this-further def pearson(v1, v2): vs = [(v1[val],v2[val]) for val in v1 if val in v2] n = len(vs) if n==0: return 0.0 sum1,sum2,sum1_sq,sum2_sq,p_sum = 0.0, 0.0, 0.0, 0.0, 0.0 for v1,v2 in vs: sum1+=v1 sum2+=v2 sum1_sq+=v1*v1 sum2_sq+=v2*v2 p_sum+=v1*v2 # Calculate Pearson score num = p_sum-(sum1*sum2/n) temp = max((sum1_sq-pow(sum1,2)/n) * (sum2_sq-pow(sum2,2)/n),0) if temp: return num / sqrt(temp) return 1.0 if __name__ == "__main__": import timeit tsetup = """ from random import randrange from __main__ import pearson, sim_pearson from scipy.stats import pearsonr v1 = [randrange(0,1000) for x in range(1000)] v2 = [randrange(0,1000) for x in range(1000)] #gc.enable() """ t1 = timeit.Timer(stmt="pearson(v1,v2)", setup=tsetup) t2 = timeit.Timer(stmt="sim_pearson(v1,v2)", setup=tsetup) t3 = timeit.Timer(stmt="pearsonr(v1,v2)", setup=tsetup) tt = 1000 print 'pearson', t1.timeit(tt) print 'sim_pearson', t2.timeit(tt) print 'scipy:pearsonr', t3.timeit(tt)
答案 3 :(得分:1)
我建议改变:
[val for val in v1 if val in v2]
到
set(v1) & set(v2)
DO
if not n: return 0.0 # and similar for den
而不是
if n == 0: return 0.0
并且值得用以下代码替换最后6行:
try:
return num / sqrt(abs(temp))
except ZeroDivisionError:
return 1.0
答案 4 :(得分:1)
因为看起来你正在进行相当多的数值计算,所以你应该给出 Psyco 一个镜头。它是一个JIT编译器,可以分析运行代码并优化某些操作。安装它,然后在文件顶部放置:
try:
import psyco
psyco.full()
except ImportError:
pass
这将启用Psyco的JIT并且应该在某种程度上加速你的代码,免费:)(实际上不是,它会占用更多内存)
答案 5 :(得分:0)
如果您的任何数学函数的输入受到相当大的约束,则可以使用查找表而不是数学函数。这可以以存储表格的额外内存为代价获得一些性能(速度)。
答案 6 :(得分:0)
我不确定这是否适用于Python。但计算sqrt是处理器密集型计算。
您可以选择快速近似newton
答案 7 :(得分:0)
我会发布我迄今为止的答案,以区别于问题。这是上述一些技术的组合,似乎已经给出了最好的改进。
def pearson(v1,v2):
vs = [(v1[val],v2[val]) for val in v1 if val in v2]
n = len(vs)
if n==0: return 0.0
sum1,sum2,sum1_sq,sum2_sq,p_sum = 0.0, 0.0, 0.0, 0.0, 0.0
for v1,v2 in vs:
sum1+=v1
sum2+=v2
sum1_sq+=v1*v1
sum2_sq+=v2*v2
p_sum+=v1*v2
# Calculate Pearson score
num = p_sum-(sum1*sum2/n)
temp = max((sum1_sq-pow(sum1,2)/n) * (sum2_sq-pow(sum2,2)/n),0)
if temp:
return num / sqrt(temp)
return 1.0
编辑:看起来psyco为这个版本提供了15%的改进,这个版本并不大,但足以证明其使用的合理性。