通过Python字典迭代非常慢

时间:2016-01-07 14:01:30

标签: python

我试图根据分配给他们的项目的值来计算用户对之间的距离。但是,当两个用户没有任何相交项时,距离计算应为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

2 个答案:

答案 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