Python 如何随机更改numpy数组中某些元素的符号?

Python 如何随机更改numpy数组中某些元素的符号?,python,numpy,random,Python,Numpy,Random,给定一个numpy数组 import numpy as np from numpy.random import random N = 5 x = random(N) 如何将(x中的某些元素)的子集随机乘以-1,以更改数组中某些元素的符号?您可以执行以下操作: import random x = [each*random.choice([-1,1]) for each in x] x[mask] *= -1 一次性: x = [each*random.choice([-1,1]) for e

给定一个numpy数组

import numpy as np
from numpy.random import random
N = 5
x = random(N)
如何将(
x
中的某些元素)的子集随机乘以
-1
,以更改数组中某些元素的符号?

您可以执行以下操作:

import random
x = [each*random.choice([-1,1]) for each in x]
x[mask] *= -1
一次性:

x = [each*random.choice([-1,1]) for each in random(N)]
其中,
random(N)
是生成
N
随机数的随机数运算器,即它可以是
numpy.random.random
,如问题示例所示。

或者:

x = np.where(random(N) > 0.5, -x, x)

(您可以使用任何适合您的其他随机规则替换
random(N)>0.5

假设您有一个布尔掩码,指示要翻转的元素。然后你可以做:

import random
x = [each*random.choice([-1,1]) for each in x]
x[mask] *= -1
此方法也适用于奇特的索引:

x[index] *= -1 
index = np.random.choice(N, size=N // 2, replace=False)
您还可以非常有效地使用:

np.negative(x, where=mask, out=x)
这种方法可能是最有效的

生成掩码很简单。您可以对一个简单的条件进行编码,如

mask = np.random.random(N) >= 0.66
或者,您可以使用选择一个随机索引:

x[index] *= -1 
index = np.random.choice(N, size=N // 2, replace=False)
最后,您可以使用一些真正的黑客来使用XOR实现这一点。其思想是IEEE 754将符号位编码为数字的最高位。您可以通过使用浮点值的整数表示翻转该位来翻转符号。当然,这只适用于浮动

您可以根据浮点的大小调整整数的大小,或者简单地使用
np.uint8
和位
0x80
。指数将根据浮动的大小进行缩放

x.view(np.uint8)[index * x.itemsize] ^= 0x80
这假设了小的尾端字节顺序。对于big-endian,请使用偏移:

x.view(np.uint8)[(index + 1) * x.itemsize - 1] ^= 0x80
计时

以下是我在功率适中的笔记本电脑上运行的一些基准测试:

import numpy as np
from timeit import repeat

def where(x, mask):
    x = np.where(mask, -x, x)

def mask_(x, mask):
    x[mask] *= -1

def index(x, mask):
    x[np.flatnonzero(mask)] *= -1 

def negat(x, mask):
    np.negative(x, where=mask, out=x)

def xor__(x, mask):
    x.view(np.uint8)[np.flatnonzero(mask) * x.itemsize] ^= 0x80

for E in range(2, 7):
    N = 10**E
    x = np.random.random(N)

    for P in (0.1, 0.5, 0.9):
        mask = np.random.random(N) < P

        print(f'E = {E}, P = {P}:')

        for func in where, mask_, index, negat, xor__:
            B = 10**(7 - E)
            t = min(repeat(lambda: func(x, mask), number=B)) / B
            print(f'{func.__name__}: {t:.3g}')
结论:对于小型阵列(10^6个元素),
np.negative
通常是最快的方法。对于10^3-10^4元素附近的最佳点,
np.其中
占主导地位。在比较时间时,请记住方法
索引
异或
依赖于索引数组。如果这是您的输入,请减去调用
np.flatnonzero
所需的时间

在所有情况下,由
p
确定的翻转元素比例对结果影响不大

作为参考,我还对使用
np.random.choice
创建索引与使用掩码之间的差异进行了计时。这些时间有点近似,因为两个操作的结果并不完全相同:

def thresh(n, p):
    return np.flatnonzero(np.random.random(n) < p)

def choice(n, p):
    return np.random.choice(n, size=round(n * p), replace=False)

for E in range(2, 7):
    N = 10**E
    for P in (0.1, 0.5, 0.9):
        print(f'E = {E}, P = {P}:')
        for func in thresh, choice:
            B = 10**(7 - E)
            t = min(repeat(lambda: func(N, P), number=B)) / B
            print(f'{func.__name__}: {t:.3g}')

对随机数组设置阈值并调用
np.flatnonzero
总是比使用
np.random.choice
快约2倍。前者允许您精确复制遮罩,而后者允许您设置翻转元素的精确数量。

我喜欢这样。如何将其更改为独立的生成器,而不是
x
的第二步转换?太糟糕了,
random。random
不会给出负数elements@develarist
x=random(N)*2-1
?(不完全相同,但可能足够好?@HeapOverflow。我已经修改了我的措辞,将花哨的索引从ufunc路径中排除。还添加了一个新的顶部方法,我测试了一点,除了
np.negative
1之外,所有这些都比Julien的慢。@HeapOverflow。数组大小有多大?从根本上说,它们都具有相同的复杂性,因此我想处理布尔索引的开销比轻松地遍历数组并否定整个过程要大。我已经更新了我的措辞。感谢您运行了一百万个元素的基准测试,请参阅。@HeapOverflow。我已经更新了时间安排、计算,并为不同的索引生成方法添加了基准。享受吧!