1

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

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

现在,我想max_results从我的 37 ( total_frequency) 个项目中挑选出 10 个 ( ),但与它们的频率成正比(例如,任何项目中最多 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_proportion10 分中有 3 分)。我该如何解决这个问题?

4

3 回答 3

0

这将取决于哪种策略更适合您的需求。假设 your max_resultsis10和 your max_proportionis 2。应该退回什么?第一次迭代将得到2每个。

  • 如果你放弃你的结果并重做所有事情,增加到max_proportion3梨的数量将下降到1(即结果将像你的例子一样);
  • 如果您保留结果并进行新的迭代,使用max_results = 2and max_proportion = 1,您将增加一个香蕉和一个橙子;
    • 如果max_proportion保持在2,您可能会得到 2 个香蕉或 2 个橙子,而其他一个都没有。

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

于 2012-12-27T04:48:33.040 回答
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']
于 2012-12-27T05:45:21.843 回答
0

您可以使用d'Hondt 方法(或 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})
于 2017-08-10T23:55:51.080 回答