我想按照他们的价值对字典的键进行分组。但是,这些值只是大致相等。在这种情况下,做组合的最佳方法是什么。我有:
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的东西,或者这是我能做的最好的?
答案 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 buckets
是OrderedDict而不是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)