[PYTHON] Pondération aléatoire des choix même sous numpy v1.6

Numpy avec GAE / py

Dans l'environnement standard App Engine de Google Cloud Platform (GCP) avec python Il existe différentes restrictions, mais vous pouvez utiliser numpy: + 1:

Cependant, la version est fixée à 1.6.1: scream: (La dernière en date du 31 août 2017 est la 1.13.1?)

Par conséquent, diverses fonctions peuvent ne pas être disponibles, mais cette fois j'ai eu des problèmes car je ne pouvais pas utiliser np.random.choice.

numpy.random.choice

C'est une fonction pratique d'extraction aléatoire ajoutée en 1.7 et au-dessus. numpy.random.choice

Fonction basique

Fondamentalement, il s'agit d'une méthode qui reçoit un tableau ou une longueur de tableau et renvoie un élément de tableau ou un index par extraction de nombres aléatoires. Spécifiez le nombre à extraire aléatoirement dans le deuxième argument (ou taille). La taille est 1 lorsqu'elle n'est pas spécifiée et la valeur de retour est un élément au lieu d'un tableau.

#Renvoie l'index avec la longueur du tableau
>>> np.random.choice(5)
3
#Si vous spécifiez la taille, il renverra l'index sous forme de tableau
>>> np.random.choice(5, 3)
array([3, 2, 4])

#Spécifiez un tableau et renvoyez l'élément
>>> np.random.choice(['alpha', 'beta', 'gamma'])
'alpha'
#Si vous spécifiez la taille, il récupérera l'élément du tableau et le retournera.
>>> np.random.choice(['alpha', 'beta', 'gamma'], 2)
array(['alpha', 'beta'],
      dtype='|S5')

En fait, la taille et les tableaux prennent également en charge plusieurs dimensions. Il semble difficile d'amener cela à 1,6 également. ..

Cependant, s'il ne s'agit que d'une fonction standard, il semble qu'elle puisse être reproduite en utilisant pleinement np.random.randint. Puisque randint peut spécifier la plage et la taille du tableau, on peut dire que la partie de sélection d'index de choix est commune.

Fonctionnalité facultative: contrôle en double

Par défaut, les doublons sont autorisés pour le contenu sélectionné au hasard avec une taille spécifiée.

#La valeur par défaut est l'autorisation de duplication, vous pouvez donc choisir la même valeur.
>>> np.random.choice(5, 3)
array([0, 0, 0])
>>> np.random.choice(['alpha', 'beta', 'gamma'], 2)
array(['gamma', 'gamma'],
      dtype='|S5')

# replace=Vous pouvez empêcher la duplication avec False.
>>> np.random.choice(5, 3, replace=False)
array([0, 1, 4])
>>> np.random.choice(['alpha', 'beta', 'gamma'], 2, replace=False)
array(['gamma', 'alpha'],
      dtype='|S5')

Une erreur se produit si la taille est supérieure à la longueur de tableau sélectionnée.

Cette fois, j'ai été troublé car il semblait que cette interdiction de duplication n'était pas mise en œuvre s'il était np.random de 1,6 ou moins. De plus, si vous bouclez le randint pour qu'il ne se chevauche pas, cela prendra beaucoup de temps si vous n'êtes pas chanceux. (Je ne connais pas le nombre pseudo aléatoire, alors peut-être que ce n'est pas le cas ...)

Donc, cette fois, j'utiliserai un échantillon du module aléatoire de python. random.sample peut être extrait du tableau sans duplication et avec une spécification de taille. Je ne pense pas que ce soit exactement la même chose car la méthode de génération de nombres aléatoires peut être différente, mais ...

Fonctionnalités optionnelles: pondération

Il peut être pondéré en passant un tableau avec le nom p et la même longueur que la cible d'extraction. p est une abréviation de probabilité, et il semble que le total devrait être 1. Par défaut, il est uniformément réparti.

#Il peut être biaisé par pondération.
>>> np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0])
array([3, 2, 3])
>>> np.random.choice(['alpha', 'beta', 'gamma'], 2, p=[0.1, 0.1, 0.8])
array(['gamma', 'gamma'],
      dtype='|S5')

#Non couvert dans cet article, mais peut être utilisé avec replace
>>> np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0], replace=False)
array([2, 3, 0])
>>> np.random.choice(['alpha', 'beta', 'gamma'], 2, p=[0.1, 0.1, 0.8], replace=False)
array(['gamma', 'alpha'],
      dtype='|S5')

Quelqu'un demandait cette pondération dans stackoverflow, alors j'ai emprunté la réponse à ce thread.

How do I “randomly” select numbers with a specified bias toward a particular number

Ce n'est pas bien compris, mais il semble que p soit converti en un tableau cumulatif et que des nombres aléatoires uniformément distribués y soient placés pour créer les valeurs réelles des nombres aléatoires.

Exemple d'implémentation

C'est une branche conditionnelle plutôt merdique, mais je pense qu'elle ressemblera à ceci une fois implémentée. Puisque l'aléatoire de python est utilisé pour l'extraction sans duplication, il peut y avoir une dégradation des performances et un biais de nombre aléatoire. S'il vous plaît laissez-moi savoir s'il y a une meilleure implémentation. (En fait, il semble y avoir une API considérable même dans la série 1.6)

La meilleure chose à faire est d'utiliser numpy 1.7 ou supérieur.

def numpy_choice(a, size=1, replace=True, p=None):
    # 1.Si c'est 6, il n'y a pas de choix, alors reproduisez-le.remplacer permet la duplication.p est pondéré
    #Tableau de plage si entier, sinon tableau.
    values = np.arange(a) if isinstance(a, int) else np.asarray(a)
    if p:
        # TODO:Vous devez vérifier la longueur de p et la longueur de a.
        #En outre, il n'est pas pris en charge en combinaison avec remplacer maintenant. ..(Je n'en ai pas besoin)
        choiced = weighted_choice(values, p, size)
    else:
        length = len(values)
        if replace or size > length:
            #S'il y a duplication, utilisez randint
            idx = np.random.randint(0, length, size)
        else:
            #Aucune duplication n'est aléatoire en python.Utiliser un échantillon
            idx = random.sample(np.arange(length), size)
        choiced = values[idx]
    if size == 1 and len(choiced) == 1:
        #Lorsque la taille 1, l'élément est retourné
        return choiced[0]
    return choiced


def weighted_choice(values, p, size=1):
    #Choix pondéré.Il reste un débordement de pile.
    values = np.asarray(values)

    cdf = np.cumsum(np.asarray(p))
    cdf /= cdf[-1]

    uniform_samples = np.random.sample(size)
    idx = cdf.searchsorted(uniform_samples, side='right')
    sample = values[idx]

    return sample

Tâche

Recommended Posts

Pondération aléatoire des choix même sous numpy v1.6
À propos de tout numpy
Fonction de réglage NumPy
Les zéros NumPy peuvent être définis même avec une taille de 0