Python近似分组

时间:2014-03-07 19:26:15

标签: python itertools

我想按照他们的价值对字典的键进行分组。但是,这些值只是大致相等。在这种情况下,做组合的最佳方法是什么。我有:

buckets = defaultdict(list)
for k, v in my_dict.iteritems():
    closest = next((rep for rep in buckets if abs(rep - v) < 1e-3), None)
    if closest:
        buckets[closest].append(k)
    else:
        buckets[v].append(k)

任何itertools魔法或其他可以简化这个/使其更加pythonic的东西,或者这是我能做的最好的?

3 个答案:

答案 0 :(得分:3)

您的算法为O(n**2),因为它在O(n)循环内执行O(n)次操作:

for k, v in my_dict.iteritems():
    closest = next((rep for rep in buckets if abs(rep - v) < 1e-3), None)

您可以通过按值对O(n log n)进行排序,然后循环遍历已排序的项目来使其成为my_dict.items()。请注意,如果for rep in bucketsOrderedDict而不是buckets,则只需查看最后一个存储桶,因为OrderedDict的键将按排序顺序排列。因此,如果下一个值接近任何存储桶,则必须接近最后一个存储桶。因此,通过使用OrderedDict,您不需要遍历所有存储桶。只需与上一个进行比较:

import random
random.seed(123)
N = 10
my_dict = dict(zip(range(N), [random.randint(0, 10)/10.0 for k in range(N)]))
print(my_dict)    
# {0: 0.0, 1: 0.0, 2: 0.4, 3: 0.1, 4: 0.9, 5: 0.0, 6: 0.5, 7: 0.3, 8: 0.9, 9: 0.1}

import operator
import collections
items = sorted(my_dict.items(), key=operator.itemgetter(1))
buckets = collections.OrderedDict([(items[0][1], [items[0][0]])])
for k, v in items[1:]:
    last_val = next(reversed(buckets))
    closest = last_val if abs(last_val - v) < 1e-3 else v
    buckets.setdefault(closest, []).append(k) 

print(buckets)

打印

OrderedDict([(0.0, [0, 1, 5]), (0.1, [3, 9]), (0.3, [7]), (0.4, [2]), (0.5, [6]), (0.9, [4, 8])])

答案 1 :(得分:0)

这将是一个稍微“pythonic”:

buckets = defaultdict(list)
for k, v in my_dict.iteritems():
    try:
        closest = next((rep for rep in buckets if abs(rep - v) < 1e-3))
        buckets[closest].append(k)
    except StopIteration:
        buckets[v].append(k)

答案 2 :(得分:0)

除了您的代码效率低下之外,由于.itetitems()顺序可能是任意的,因此每次都不能保证相同或任何特定结果。要解决这两个问题,您只需使用键功能:

key = lambda x: round(x, 3)

然后按常规方式分组,但使用key(v)作为索引:

buckets = defaultdict(list)
for k, v in my_dict.iteritems():
    buckets[key(v)].append(k)