【问题标题】:Assigning probabilities to items in python在python中为项目分配概率
【发布时间】:2016-09-19 21:35:12
【问题描述】:

我知道问题标题含糊不清。我很抱歉。我有一个具有键的哈希图:值<string>:<list of lists>。对于给定的列表,列表中的每个项目都有相应的被选中概率。例如,哈希图中的一项可能看起来像

"NP":[['A', 'B'], ['C', 'D', 'E'], ['F']]

我需要随机选择右侧的列表之一。每个列表都有它自己的概率。这是将在地图中生成上述项目的输入字符串。

3 NP A B
1 NP C D E
1 NP F

因为NP A B旁边有数字3NP C D E旁边有1NP F旁边有1,所以概率比是3:1: 1,所以[A, B] 有 3/5 的概率被选中,[C, D, E] 有 1/5 的概率被选中,[F 的概率相同。

我的问题是,我如何模拟这些概率?

在引入这些数字之前,这很容易,因为我可以计算列表的长度(在上面的示例中为 3),然后选择一个介于 0 和 len(list) - 1 之间的随机数和 random.randint(),然后选择列表中的那个索引。要模拟伯努利随机变量,我知道可以检查if random.randint() < p。但这仅在您有 2 个案例时才有效。我无法明确编写 if 语句来检查,因为列表可能包含 n 元素。

【问题讨论】:

  • 使用numpy.random.choice 很简单。如果您不想使用 numpy,则必须实现自己的离散采样算法。查看 wiki 以获取一些想法。 alias-algorithm 是一个很好但更难实现的算法,但您也可以使用二分搜索(甚至线性搜索)来实现。
  • 是的,很遗憾我不能使用用户需要安装的 numpy 或库
  • 参见 Eli Bendersky 的 Weighted random generation in Python
  • @StevenRumbalski 所以在那篇文章中我传入的weights 列表在我的示例中是[3, 1, 1][60, 20, 20],其中每个元素都是从[(3/5)*100, (1/5)*100, (1/5)*100] 计算出来的

标签: python probability


【解决方案1】:

所以我解决这个问题的方法是创建一个从0total probability 的稀疏表。在你的情况下,那是

0 -> 0
3 -> 1
4 -> 2

然后选择一个介于 0 和 4 之间的 int,并选择最大值 >= 所选值(换句话说,1 映射到 0,2 映射到 0,3 映射到 1)。此映射中的“值”对应于原始字典中的子列表。这不应该占用任何额外的库。

【讨论】:

    【解决方案2】:

    如果您的总重量仍然很小,以下是一种粗略的方法可能就足够了:

    >>> NP = [['A', 'B'], ['C', 'D', 'E'], ['F']]
    >>> weights = (3,1,1)
    >>> indx_list = [idx for idx,w in zip(range(len(NP)), weights) for _ in range(w)]
    >>> indx_list
    [0, 0, 0, 1, 2]
    >>> import random
    >>> random.choice([0, 0, 0, 1, 2])
    1
    >>> sample = [random.choice([idx for idx,w in zip(range(len(NP)), weights) for _ in range(w)]) for _ in range(1000)]
    >>> from collections import Counter
    >>> counts = Counter(sample)
    >>> counts
    Counter({0: 600, 2: 213, 1: 187})
    

    【讨论】:

      【解决方案3】:

      这是一个使用线性搜索的简单原型。唯一的依赖是 random.random() 来获得 [0,1) 内的浮点数。

      尽管采用了未经优化的方法,但在我的 PC 上对 100.000 个样本只需要约 0.25 秒。但请记住,这种性能取决于统计数据/概率向量。也可以通过预排序来改进代码。

      总体思路:查看this

      代码

      import random
      
      """ Discrete-sampling """
      def cum_sum(xs):
          cum_sum = []
          total = 0
          for i in xs:
              total += i
              cum_sum.append(total)
          total_sum = sum(cum_sum)
          return cum_sum, cum_sum[-1]
      
      def discrete_sample(items, probs):
          cum_sum_, max_ = cum_sum(probs)
          random_val = random.random() * max_
          for ind, i in enumerate(items):
              if random_val < cum_sum_[ind]:
                  return i
          return items[-1]  # fail-safe
      
      def sample_from_dict(element, data, data_p):
          data_ = data[element]
          data_p_ = data_p[element]
          selection = discrete_sample(range(len(data_)), data_p_)
          return data_[selection]
      
      """ Data """
      data = {'NP': [['A', 'B'], ['C', 'D', 'E'], ['F']]}
      data_p = {'NP': [3, 1, 1]}
      
      """ Try it """
      samples = []
      for i in range(100000):
          samples.append(sample_from_dict('NP', data, data_p))
      
      counts = [0, 0, 0]
      for i in samples:
          if i == ['A', 'B']:
              counts[0] += 1
          elif i == ['C', 'D', 'E']:
              counts[1] += 1
          elif i == ['F']:
              counts[2] += 1
      
      print(counts)
      

      输出

      [60130, 19867, 20003]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-17
        • 2016-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-27
        相关资源
        最近更新 更多