2016-03-08 88 views
0

我正在研究一个涉及二进制模式的项目(这里的np.arrays为0和1)。 我想修改它们的一个随机子集并返回一些给定部分的值已被改变的模式的改变版本(如map函数为固定大小的数组的随机子集) ex:take模式[0 0 1 0 1]和速率0.2,返回[[0 1 1 0 1] [1 0 1 0 1]]随机化阵列的一部分

它通过使用辅助阵列和符合条件迭代似乎是可能的,但有一个“干净”的方式做到这一点?

在此先感谢!

+2

显示你有 – JBernardo

+1

代码所以,如果你有一个数组A的二进制值,数组中的每个索引都有一个概率P?例如,你的模式[0 0 1 0 1]可以变为[1 1 1 1 1],即使这样做不可能吗? – Carser

回答

0

map函数也适用于布尔数组。您可以在子样本逻辑添加到您的功能,像这样:

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. 

或者你可以重写一个地图功能,像这样:

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

有两件事:首先,我有时会得到'[False False True False True]'的输出,其中没有元素被改变。其次,这不允许将'False'转换为'True'。后者可能不是一个问题,因为OP不清楚这是否可能。 –

+0

在第一个选项中,平均为20%,这意味着可能会出现多于或少于20%的元素发生变化的情况。第二个选项总是给你20%的准确率(或者你要求的任何比率)。在40%的情况下,你看到'[False False True False True]'是因为'True'被设置为'True'(没有改变)。 OP可以在lambda中设置他想要的任何函数,例如这个:'f = lambda x:not(x)'。 – Bastiaan

+0

更正:'f = lambda a:np.logical_not(x)' – Bastiaan

0
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)]) 

使用np.tile()以产生从所述种子行的矩阵。

np.random.binomial()用您的请求速率生成二项掩码矩阵。

运用面具与XOR二进制运算^


编辑:

基于@Jared Goguen评论,如果你想改变位的20%,可以说明以口罩选择要随机更改的元素:

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

应该指出,这并不改变0.2的条目的比例,而是每个元素有0.2的机会改变。平均重新排列将会有0.2个元素的比例发生变化,但是每个重新排列可能在没有元素发生变化并且所有元素发生变化的地方都有。这个实现完美地模拟了每个元素具有*独立*转换概率的结构,但是如果知道具有两个1的5个阵列将以三个1转换为5个阵列,则它失败。 –

+1

感谢您指出。另一种情况将用马尔可夫链建模,不是吗? – xvan

+1

对于马尔可夫链,转换矩阵由每个状态转换到其他状态的概率组成。根据定义,在这个过程中状态向量的总和是不变的,所以我真的没有看到使用马尔可夫链对此进行建模的方法。可能有办法使用类似的过程,但转换矩阵不会是随机的。 –

0

所以,已经有一个答案提供了序列,其中每个元素都有一个随机转移概率。但是,您似乎可能想要改变一些确切的元素部分。例如,[1, 0, 0, 1, 0]可以更改为[1, 1, 0, 1, 0][0, 0, 0, 1, 0],但不是[1, 1, 1, 1, 0]

基于xvan的回答,前提是使用按位异或运算符^。当一位与0异或时,它的值不会改变。当一个位与1相异时,它会翻转。从你的问题来看,你似乎想改变序列中的len(seq)*rate位数。首先创建mask其中包含len(seq)*rate的数字1。为了得到一个改变的序列,用洗牌版本mask对原始序列进行异或。

这里有一个简单的,低效率的实现:

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] 

我真的不知道很多关于NumPy的,所以也许更有经验的人可以使这个效率,但这种方法似乎固体。

编辑:这里有一个版本的情况下约30%的速度:

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 

看来,这个更新版本扩展得很好的seq的大小和count值。在seqmask中使用dtype=bool会使时间进一步缩短50%。