我试图根据分配给他们的项目的值来计算用户对之间的距离。但是,当两个用户没有任何相交项时,距离计算应为null。我也只计算距离矩阵的下半部分(例如,UserA-UserB等同于UserB-UserA所以只计算一个)。
所以我有以下Python脚本可以工作,但是当我提供超过几百个用户时,它真的开始了。下面的示例脚本显示了输入结构,但我试图做成千上万,而不仅仅是我在这里展示的四个。
行s = {k:v for k,v in data.items() if k in (user1,user2)}
似乎增加了最多的开销
import math
from decimal import *
def has_matching_product(data,user1,user2):
c1=set(data[user1].keys())
c2=[k for k in data[user2].keys()]
return any([x in c1 for x in c2])
def get_euclidean_dist(data,user1,user2):
#Tried subsetting to run quicker?
s = {k:v for k,v in data.items() if k in (user1,user2)}
#Ignore users with no overlapping items
if has_matching_product(s,user1,user2):
items=set()
for k,v in s.items():
for ki in v.keys():
items.add(ki)
rs=Decimal(0)
for i in items:
p1 = s.get(user1).get(i)
p2 = s.get(user2).get(i)
v1 = p1 or 0
v2 = p2 or 0
rs+= Decimal((v1-v2)**2)
return math.sqrt(rs)
else:
return None
#User/Product/Value
raw_data = {
'U1': {
'I1':5,
'I4':2
},
'U2': {
'I1':1,
'I3':6
},
'U3': {
'I3':11
},
'U4': {
'I4':9
}
}
users = sorted(raw_data.keys())
l = len(users)
data_out = set()
#Compute lower half of a distance matrix (unique pairs only)
for u1 in range(0,l-1):
for u2 in range(1+u1,l):
dist = get_euclidean_dist(raw_data,users[u1],users[u2])
print('{x} | {y} | {d}'.format(x=users[u1],y=users[u2],d=dist)) #Sample output
正确的输出应该是什么样的:
U1 | U2 | 7.483314773547883
U1 | U3 | None
U1 | U4 | 8.602325267042627
U2 | U3 | 5.0990195135927845
U2 | U4 | None
U3 | U4 | None
答案 0 :(得分:3)
问题是您每次都要走ENTIRE字典,只是为了找到您想要的两个项目。从它的外观来看,你要拔出user
,然后花费所有时间试图再次在data
找到它们。 @Peter Wood的建议将帮助一大堆 - 首先只抓住你想要的两个user
,但是那种错过了树林中的森林 - 你不会“首先需要减少你的字典。把它们放在一起:
import itertools
for kv1, kv2 in itertools.combinations(data.items(), 2):
## calculate distance directly here
答案 1 :(得分:2)
你正在使用十进制,这不是很快。字典已经是一组键,因此不需要创建额外的组。
您创建一个包含any
的列表,该列表必须计算所有值,而是使用生成器。
您正在使用get
,因此您可以提供默认值。
所以我明白了:
import math
def get_euclidean_dist(data,user1,user2):
c1 = data[user1]
c2 = data[user2]
#Ignore users with no overlapping items
if any(x in c1 for x in c2):
items = set(c1)
items.update(c2)
rs = sum((c1.get(i, 0)-c2.get(i, 0))**2 for i in items)
return math.sqrt(rs)
else:
return None