Python:如何从python中的分组列表中返回相对数量

时间:2012-12-27 04:16:00

标签: python math

假设我有一个事物列表,它们的频率,(按频率排序)和项目总数(为了清楚起见,我在这里使用dict,但实际上它们是具有频率属性的对象):

items = {"bananas":12, "oranges":12, "apples":11, "pears":2}

现在,我想从我的37(max_results)个项目中挑选出10个项目(total_frequency),但与其频率成比例(最多,比方说,3个项目) - max_proportion)。在这个例子中,我最终得到3个香蕉,橙子和苹果,以及1个梨。

def get_relative_quantities(total_frequency, items, max_results, max_proportion):
    results = {}
    num_added = 0
    for freq, the_group in it.groupby(items, lambda x: x.frequency):
        if num_added == max_results:
            break

        the_group_list = list(the_group)
        group_size = len(the_group_list)
        shuffle(the_group_list)

        for item in the_group_list:
            if num_added == max_results:
                break

            rel_freq = min(math.ceil((freq/total_frequency)*max_results), max_proportion)
            results[item] = rel_freq
            num_added += rel_freq

    return results

我担心的一件事是,如果只有1项,这种方法,我将得不到足够的结果。我将得到3(假设一个max_proportion f 3中的10)。我该如何处理这个问题?

3 个答案:

答案 0 :(得分:0)

这将取决于哪种策略对您的需求更有意义。我们假设您max_results10max_proportion2。应该归还什么?第一次迭代将获得每个2

  • 如果您丢弃结果并重做所有内容,将max_proportion增加到3,则梨的数量将降至1(即结果将类似于您的示例);
  • 如果您保留结果并使用max_results = 2max_proportion = 1进行新的迭代,则会增加一个香蕉和一个橙色;
    • 如果max_proportion保留在2,您可能会获得2个香蕉或2个橙子,而另一个则不会。

无论您想要的输出是什么,我的建议都是一样的:检查是否有足够的结果,如有必要,再次致电get_relative_quantities,减少max_results(以获取剩余的元素)或增加max_proportion(丢弃初始结果并接受越来越多的每个项目)。根据需要多次执行此操作以达到所需数量或耗尽可能性。 (这与iterative deepening

背后的原理相同

答案 1 :(得分:0)

首先,建立一个具有比例数量的元素的项目列表:

items = {"bananas":12, "oranges":12, "apples":11, "pears":2}

choices = []
[choices.extend([k] * v) for k, v in items.items()]

接下来,设置最终结果,每个结果的最小数量(每个可能的项目之一):

selected = list(items.keys())

最后,对于您要选择的其他项目,请从按比例重复的项目列表中选择一个随机项目:

import random as rnd
[selected.append(rnd.choice(choices)) for i in xrange(10 - len(items))]

所有这些片段组合在一起:

import random as rnd

items = {"bananas":12, "oranges":12, "apples":11, "pears":2}

choices = []
[choices.extend([k] * v) for k, v in items.items()]

selected = list(items.keys())
[selected.append(rnd.choice(choices)) for i in xrange(10 - len(items))]

运行的输出:

>>> pp.pprint(selected)
['pears',
 'bananas',
 'oranges',
 'apples',
 'bananas',
 'bananas',
 'oranges',
 'apples',
 'apples',
 'apples']

答案 2 :(得分:0)

您可以使用d'Hondt method(或Jefferson方法)来执行此操作。

import heapq, collections, itertools

def fruit_divided(fruit, weight, max_proportion):
    for div in range(1, min(weight, max_proportion) + 1):
        yield (- weight / div, fruit)

def pick(items, max_results, max_proportion):
        fruits = heapq.merge(*(fruit_divided(fruit, frequency, max_proportion)
                               for fruit, frequency in items.items()))
        fruits = itertools.islice(fruits, max_results)
        return collections.Counter(fruit for _, fruit in fruits)

示例运行:

>>> items = {"bananas":12, "oranges":12, "apples":11, "pears":2}
>>> max_results = 10
>>> max_proportion = 3
>>> print(pick(items, max_results, max_proportion))
Counter({'oranges': 3, 'bananas': 3, 'apples': 3, 'pears': 1})

如果只能选择少于max_results个水果,则会返回最高数量。

>>> print(pick(items, max_results, max_proportion))
Counter({'oranges': 3, 'bananas': 3, 'apples': 3, 'pears': 2})