lundi 4 septembre 2017

Python random.choices in a Django Model

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,0013149236337946007 per number obtained

Populating the DB with ten thousand random weights from zero to one hundred, I got:

  • 0,005099541511962798 per 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