Kit McCarthy
Kit McCarthy

Reputation: 41

Using a shifted bell curve to create random.choices weights

I'm a beginner to Python, attempting something a bit too complicated for me.

I want to output a list (q), k-items long, where each item is one of five options (contained in list w).

w = [a, b, c, d, e]

I'm using random.choices to create list q.

I want each item's weighting to be governed by a skewed bell curve (or even a rough approximation of one), where the horizontal position of the curve's peak equals p. p can range from 0.1 to 1.

So, if p = 0.55, item c should have the highest probability of appearing in list q, with b and d having the second highest probability, and a and e having the lowest probability. If p = 0.1, a should be the most likely to appear and e the least likely, and so on.

I've found scipy.stats.skewnorm, which seems like it could work. However, I'm really struggling to adapt it into the form I need.

I'm after a function where 0 ≤ y ≤ 1 and 0 ≤ x ≤ 1.2. I then want this function to create the five weights for random.choices, something like so:

p = 0.7

prob_a = f(0.2, p)

prob_b = f(0.4, p)

prob_c = f(0.6, p)

etc...

q = random.choices(w, weights=[prob_a, prob_b, prob_c, etc...], k=10)

I'd be extremely grateful for any and all advice, direction, or suggestions! I'm not wedded to either random.choices or skewnorm – if there's a simpler way to achieve a similar result, that'd be amazing!

Upvotes: 2

Views: 361

Answers (1)

Sam Mason
Sam Mason

Reputation: 16194

Based on what you've written so far, I think you want to do something like:

from scipy import stats
from random import choices

def weighted_choices(w, mu, sd, *, k=1):
    weights = stats.norm(mu, sd).pdf(range(len(w)))
    return choices(w, weights=weights, k=k)

where mu is the index of w you want to be the most likely, and sd is how tight you want that selection to be. e.g.:

  • weighted_choices('abcde', 1, 0.1) will almost always choose b
  • weighted_choices('abcde', 3, 0.5) will tend to choose d but has reasonable chance of choosing c or e, and much smaller chance of picking a
  • weighted_choices('abcde', 2, 10) is basically uniform

Upvotes: 1

Related Questions