Je travaille sur un projet impliquant des modèles binaires (ici np.arrays de 0 et 1). Je voudrais modifier un sous-ensemble aléatoire de ceux-ci et retourner plusieurs versions modifiées du modèle où une fraction donnée des valeurs a été modifiée (comme mapper une fonction à un sous-ensemble aléatoire d'un tableau de taille fixe) ex: prendre le schéma [0 0 1 0 1] et noter 0.2, retourner [[0 1 1 0 1] [1 0 1 0 1]]

Cela semble possible en utilisant des tableaux auxiliaires et en itérant avec une condition, mais existe-t-il une façon "propre" de le faire?

Merci d'avance !

0
AFanthomme 8 mars 2016 à 19:44

3 réponses

Meilleure réponse

La fonction map fonctionne également sur les tableaux booléens. Vous pouvez ajouter la logique de sous-échantillon à votre fonction, comme ceci:

import numpy as np
rate = 0.2
f = lambda x: np.random.choice((True, x),1,p=[rate,1-rate])[0]

a = np.array([0,0,1,0,1], dtype='bool')
map(f, a)
# This will output array a with on average 20% of the elements changed to "1"
# it can be slightly more or less than 20%, by chance.

Ou vous pouvez réécrire une fonction de carte, comme ceci:

import numpy as np

def map_bitarray(f, b, rate):
    '''
    maps function f on a random subset of b
    :param f: the function, should take a binary array of size <= len(b)
    :param b: the binary array
    :param rate: the fraction of elements that will be replaced
    :return: the modified binary array
    '''
    c = np.copy(b)
    num_elem = len(c)
    idx = np.random.choice(range(num_elem), num_elem*rate, replace=False)
    c[idx] = f(c[idx])
    return c

f = lambda x: True
b = np.array([0,0,1,0,1], dtype='bool')
map_bitarray(f, b, 0.2)
# This will output array b with exactly 20% of the elements changed to "1"
0
Bastiaan 9 mars 2016 à 18:16
rate=0.2
repeats=5
seed=[0,0,1,0,1]
realizations=np.tile(seed,[repeats,1]) ^ np.random.binomial(1,rate,[repeats,len(seed)])

Utilisez np.tile() pour générer une matrice à partir de la ligne de départ.

np.random.binomial() pour générer une matrice de masque binomial avec votre taux demandé.

Appliquer le masque avec l'opérateur binaire xor ^


ÉDITER:

Sur la base des commentaires de @Jared Goguen, si vous souhaitez modifier 20% des bits, vous pouvez élaborer un masque en choisissant des éléments à modifier de manière aléatoire:

seed=[1,0,1,0,1]

rate=0.2
repeats=10

mask_list=[]

for _ in xrange(repeats):
  y=np.zeros(len(seed),np.int32)
  y[np.random.choice(len(seed),0.2*len(seed))]=1
  mask_list.append(y)

mask = np.vstack(mask_list)
realizations=np.tile(seed,[repeats,1]) ^ mask
0
xvan 8 mars 2016 à 20:10

Donc, il y a déjà une réponse qui fournit des séquences où chaque élément a une probabilité de transition aléatoire. Cependant, il semble que vous souhaitiez que la fraction exacte des éléments change à la place. Par exemple, [1, 0, 0, 1, 0] peut devenir [1, 1, 0, 1, 0] ou [0, 0, 0, 1, 0], mais pas [1, 1, 1, 1, 0].

La prémisse, basée sur la réponse de xvan, utilise l'opérateur xor bit par bit ^. Lorsqu'un bit est xou avec 0, sa valeur ne change pas. Quand un bit est xor'd avec 1, il se retournera. D'après votre question, il semble que vous souhaitiez modifier len(seq)*rate nombre de bits dans la séquence. Créez d'abord mask qui contient len(seq)*rate nombre de 1. Pour obtenir une séquence modifiée, xou la séquence d'origine avec une version mélangée de mask.

Voici une implémentation simple et inefficace:

import numpy as np

def edit_sequence(seq, rate, count):
    length = len(seq)
    change = int(length * rate)
    mask = [0]*(length - change) + [1]*change
    return [seq ^ np.random.permutation(mask) for _ in range(count)]

rate = 0.2
seq = np.array([0, 0, 1, 0, 1])

print edit_sequence(seq, rate, 5) 

# [0, 0, 1, 0, 0]
# [0, 1, 1, 0, 1]
# [1, 0, 1, 0, 1]
# [0, 1, 1, 0, 1]
# [0, 0, 0, 0, 1]

Je ne connais pas vraiment NumPy, donc peut-être que quelqu'un avec plus d'expérience peut rendre cela efficace, mais l'approche semble solide.

Modifier: voici une version qui est environ 30% plus rapide:

def edit_sequence(seq, rate, count):
    mask = np.zeros(len(seq), dtype=int)
    mask[:len(seq)*rate] = 1
    output = []
    for _ in range(count):
        np.random.shuffle(mask)
        output.append(seq ^ mask)
    return output

Il semble que cette version mise à jour évolue très bien avec la taille de seq et la valeur de count. L'utilisation de dtype=bool dans seq et mask permet une autre amélioration de 50% du timing.

0
Jared Goguen 8 mars 2016 à 21:25