2016-12-04 66 views
2

在python应用程序中,用户上传照片并根据upvotes和唯一注释进行评分。现在想象一下,每张照片都有一个元组,格式为:(uploader_id, vote_score, comment_count)。接下来,想象一下,我们维护上传的所有照片的元组列表。优化复杂列表操作,涉及多个元组求和(在Python中)

E.g.样本列表可以是:[(1,12,3),(1,-1,6),(2,30,10),(1,0,0),(2,0,1)]。这显示上传了5张照片,3个由uploader_id 1和2个由uploader_id 2.

我想将上述列表缩小为[(uploader_id, net_score)]。这里net_score是特定用户的所有vote_scorecomment_count值的总和除以所述用户上传的照片的数量。

E.g.前面提到的示例列表将产生[(1,(11+9)/3),(2,(30+11)/2)] = [(1,6.67),(2,20.5)]

执行此列表操作的最高性能方式是什么?我已经完成了它(见下文),但有多个for循环。有没有更高效的方法?


这里是目前我在做什么:

from collections import defaultdict, Counter 

photos_score_list = # list of the format [(uploader_id, vote_score, comment_count)] 
photos_total_score_list= [ (k,v1+v2) for k,v1,v2 in photos_score_list] 
total_photos = Counter(elem[0] for elem in photos_total_score_list) #dictionary, e.g. Counter({2: 8, 1: 7}) 
total_scores = defaultdict(int) 
for key,val in photos_total_score_list: 
    total_scores[key] += val 
uploader_scores = [] 
for key,val in total_scores.items(): 
    uploader_scores.append(key) 
    uploader_scores.append(val/total_photos[key]) 
set_benchmark(uploader_scores) 

注意uploader_scores与解压的元组(即的,而不是[1,6.67,2,20.5][(1,6.67),(2,20.5)]是故意的结束了 - 我喂列表到Redis的。 - 这是最好的方式来做到这一点(即不要担心)

+0

你对当前的代码有任何性能问题吗?我会为了它而反对优化 – user312016

+1

是的。这是一个高流量的现场应用程序,其中性能变得至关重要。否则我不会问这个问题。 –

+1

嗯,你为什么不想在DB或redis中存储已经计算好的聚集?在这种情况下,每次上传新照片时都会更新它,并在没有任何附加步骤的情况下获取数据。 – opalczynski

回答

1

我想你会得到更好的性能使用numpy/pandas我会加载数据在一个DataFrame中,并使用分组像这样的方法:

import pandas as pd 
import numpy as np 
from pandas import DataFrame 

lst = [(1,12,3),(1,-1,6),(2,30,10),(1,0,0),(2,0,1)] 
df = DataFrame(lst) 
df[3] = df[1]+df[2] # adding the comment and vote count 

该柱用数0包含uploader_id:

gb = df.groupby(0) 
gb.agg(sum) 

最后一个表达式产生一个数据帧。最后一列包含你感兴趣的数据:

1 2 3 
0    
1 11 9 20 
2 30 11 41 

如果您还希望除以由用户上传的照片数量:这将产生

gb = df.groupby(0).aggregate(lambda x: np.sum(x)/float(len(x))) 

1   2 3 
0   
1 3.666667 3.0 6.666667 
2 15.000000 5.5 20.500000 
0

您可以使用collections.defaultdict作为:

from collections import defaultdict 

my_list = [(1,12,3),(1,-1,6),(2,30,10),(1,0,0),(2,0,1)] 
vote_score_dict, comment_count_dict = defaultdict(int), defaultdict(int) 

for uploader_id, vote_score, comment_count in my_list: 
    vote_score_dict[uploader_id] += vote_score 
    comment_count_dict[uploader_id] += comment_count 

final_dict = {id: vote_score_dict[id]/float(comment_count_dict[id]) for id in vote_score_dict} 

终值保持为final_dict将是:

{1: 1.2222222222222223, 2: 2.727272727272727} 
0

你可以不photos_total_score_list做,它只是保持vote_score and comment_count的总和,这样做,当你计算的平均值。

你的算法可以重做这样:在photos_score_list遍历元素,并保持关键id和值[scores, count]字典,那么到底划分得到的平均值。这样,他们唯一要创建的就是内存中的字典对象,并且只需循环一次数据。

0

尽管我的评论,在这里我的解决方案 - 降低维权的数量从4比2:

from collections import defaultdict, namedtuple 

UserData = namedtuple('UserData', ['user_id', 'vote_score', 'comment_count']) 

u_data = [ 
    UserData(1, 12, 3), 
    UserData(1, -1, 6), 
    UserData(2, 30, 10), 
    UserData(1, 0, 0), 
    UserData(2, 0, 1) 
] 

data = defaultdict(int) 
count = defaultdict(int) 

for s_data in u_data: 
    data[s_data.user_id] += s_data.vote_score + s_data.comment_count 
    count[s_data.user_id] += 1.0 

output = [ 
    (user_id, score/count[user_id]) for user_id, score in data.iteritems() 
] 

编码愉快!