以大多数元素位于列表开头的方式选择元素

时间:2017-11-14 01:02:37

标签: python

我有一个值列表,我希望以这样的方式选择n值,大多数元素都来自列表的开头,随着它在列表中的进一步减少(如图所示)下方链接)。

np.random.seed(0)
a = pd.Series(range(100))
np.random.shuffle(a)
a.values

array([26, 86,  2, 55, 75, 93, 16, 73, 54, 95, 53, 92, 78, 13,  7, 30, 22,
       24, 33,  8, 43, 62,  3, 71, 45, 48,  6, 99, 82, 76, 60, 80, 90, 68,
       51, 27, 18, 56, 63, 74,  1, 61, 42, 41,  4, 15, 17, 40, 38,  5, 91,
       59,  0, 34, 28, 50, 11, 35, 23, 52, 10, 31, 66, 57, 79, 85, 32, 84,
       14, 89, 19, 29, 49, 97, 98, 69, 20, 94, 72, 77, 25, 37, 81, 46, 39,
       65, 58, 12, 88, 70, 87, 36, 21, 83,  9, 96, 67, 64, 47, 44])

选择这些数字的好方法是什么?

http://www.bydatabedriven.com/wp-content/uploads/2012/12/Screen-Shot-2012-12-03-at-8.12.36-PM.png

作为一个例子,如果n = 10,那么返回的值可能是(从列表的开头选择的更多数字与列表末尾的那些值相比):

26,2,16,92,8,45,61,99,94 39

2 个答案:

答案 0 :(得分:3)

您可以使用np.random.choice并直接或使用pd.Series.sample传递适当形状的权重,因为您正在使用pandas。例如:

In [59]: s = pd.Series(range(100))

In [60]: chosen = s.sample(10**6, replace=True, weights=1.0/(1+np.arange(len(s)))) #typecast the weight to float

In [61]: chosen.hist(bins=50).get_figure().savefig("out.png")

给了我

sample histogram

您可以根据心脏的内容调整权重功能。这里我基本上使用了1 / i,因此第4个元素的选择可能性比第一个要低4倍。您可以将该表达式赋予某种能力,**2使第4个元素被选中的可能性降低16倍,或**0.5使第4个元素的一半可能被选为第一个元素。完全取决于你找到一个你满意的行为。

另请注意,我在这里使用replace=True,因为我想选择大量的值来使情节看起来更好。如果您不希望两次选择相同的元素,请使用replace=False

答案 1 :(得分:0)

通过重新发明轮子来解决

以下是从第一原则开始的方法。

  1. random.random()返回0到1之间的随机数。这意味着当random.random() < x变得接近0时,表达式x变得更少。

    < / LI>
  2. 对于数组中的每个元素,比如说array[i],让我们定义元素被选中的几率

  3. <强> odds_pick(i) = (1 - i / len(array)) * DAMP   这里,DAMP是一个介于0和1之间的数字,用于减少拾取数字的几率。因此,

    • 当i = 0(第一个元素)时,被挑选元素的几率只是= DAMP
    • 对于最后一个元素,它是DAMP / len(array)
    • 对于其他人来说,赔率介于这两个极端之间,随着i变大,它们会减少。

    最后,为了得到这个合适的样本,迭代数组,并检查是否random.random() < odds_pick(i)。如果是这样,请选择元素。

    由于我们定义odds_pick(i)的方式,随着i的增加,它会变得越来越接近0,因此random.random() < odds_pick(i)将越来越不真实。最终,这意味着我们最终会从前端而不是从末端更频繁地挑选元素。

    <小时/>

    代码:

    import random
    def sample_with_bias(arr, n, damping_factor=.3):
        res = []
        indexes_picked = []
        n_picked = 0
        while n_picked < n:
            for i, x in enumerate(arr):
                odds_pick = damping_factor * (1 - i * 1. / len(arr))
                if i not in indexes_picked and random.random() < odds_pick:
                    print(odds_pick)
                    n_picked += 1
                    indexes_picked.append(i)
                    res.append(x)
        return res
    

    请注意,在数组上有多次传递以覆盖仅在一次传递中无法对n个唯一元素进行采样的极端情况。

    让我们进行一些实验:

    def run_experiment(arr, damping_factor, num_pick=10, num_runs=100):
        all_samples = []
        for i in range(num_runs):
            all_samples.extend(sample_with_bias(arr, num_pick, damping_factor=damping_factor))
        dist = Counter(all_samples)
        dist = sorted(list(dist.items()), key=lambda k: k[0])
        k, v = zip(*dist)
        plt.bar(k, v)
        plt.title("Damping Factor = {0}".format(damping_factor))
        plt.show()
    

    for df in [0.10, 0.50, 0.99]:
        np.random.seed(0)
        a = pd.Series(range(100))
        run_experiment(a, damping_factor=df)
    

    <小时/>

    结果

    • 对于大量的阻尼,几乎是均匀的,在开始时仍然偏向元素: enter image description here

    • 让我们看看减少阻尼时会发生什么:

    enter image description here

    • 几乎没有阻尼,只选择前面的元素:

    enter image description here