Python introduced random.choices in v3.6, and now I'm trying to use a similar functionality in Django.
My objective is selecting a Django object "randomly" based on weights.
Solution Draft
My example class:
class WeightedElement(models.Model):
weight = models.PositiveIntegerField()
With O(1) set as an objective, I found the stochastic acceptance version of Fitness proportionate selection that looks very promising, and did:
from random import random
class ChoicesManager(models.Manager):
def get_random(self, get_weighted_by: str) -> models.Model:
queryset = super(ChoicesManager, self).get_queryset()
number_of_rows = queryset.count()
max_weight_object = queryset.order_by(get_weighted_by).last()
max_weight = getattr(max_weight_object, get_weighted_by)
while True:
random_object = queryset[int(random() * number_of_rows)]
random_object_weight = getattr(random_object, get_weighted_by)
if random() < random_object_weight / max_weight:
break
return random_object
Safety checks
Does it work (right distribution)?
I created four objects:
<QuerySet [(1, 4), (2, 3), (3, 12), (4, 1)]>
That should give this distribution:
{1: 2000, 2: 1500, 3: 6000, 4: 500}
And I got:
{1: 1926, 2: 1526, 3: 6044, 4: 504}
Does it perform?
I did timeit giving me:
0,0013149236337946007per number obtained
Populating the DB with ten thousand random weights from zero to one hundred, I got:
0,005099541511962798per number obtained
My doubts
- Is a manager the right place for doing this?
- Is there a way to reduce DB queries?
Aucun commentaire:
Enregistrer un commentaire