我正在寻找一种有效的方法来将NumPy数组a
的总和减少给定的数字n
,以使a中的任何值都不低于0,和我可以为pvals
中的不同值指定概率a
。因此,如果我的函数的签名是:
> def removeRandom(a, n, pvals):
> ...
然后应执行以下操作:
> a = np.array([2, 3, 5, 10])
> pvals = np.array([0.1, 0.1, 0.4, 0.4])
> removeRandom(a, 5, pvals)
array([2, 2, 3, 8])
由于删除操作应该是随机的,因此下次输出可能会有所不同:
> removeRandom(a, 5, pvals)
array([1, 3, 4, 7])
我目前有一种方法可以执行删除步骤,然后检查a
中的任何值是否都低于0,如果是,请重复此步骤,直到a
中的值都没有低于0:
def removeRandom(a, n, pvals=None):
if n < np.sum(a):
# remove a total of n at random indexes, taking the pvals into account
aranged = np.arange(a.size)
randomIndexes = np.random.choice(aranged, n, p=pvals)
np.subtract.at(a, randomIndexes, 1)
while(a[a < 0].size > 0):
# what's the sum of all cells below 0?
sumBelowZero = np.abs(np.sum(a[a < 0]))
# set them to 0
a[a < 0] = 0
# rinse and repeat the process
randomIndexes = np.random.choice(aranged, n, p=pvals)
np.subtract.at(a, randomIndexes, 1)
return a
else:
return np.zeros_like(a)
该循环显然不是很优雅,而且如果函数至少将一个值降到0以下,则该函数有可能陷入该循环。随着n
接近{ {1}}。
posted here是解决此问题的一种非常优雅的方法,但它不允许设置概率:
np.sum(a)
由于这里也使用def removeRandom(a, n):
c = np.cumsum(np.r_[0, a])
if n < c[-1]:
r = np.random.choice(np.arange(c[-1]) + 1, n, replace = False)
d = np.sum(r[:,None] <= c[None,:], axis=0)
return np.diff(c-d)
else:
return np.zeros_like(a)
并接受概率,因此我一直在寻找一种利用该方法的方法(显然没有成功)–可以做到吗?
当然,我也希望有其他解决方案。
答案 0 :(得分:2)
这让我有些头疼,想把我的头缠起来,但是我想我理解你的问题。下面的方法从数组中的随机元素中删除总和。
def remove_random(array, total, probs=None):
if total >= np.sum(array):
return np.zeros_like(array)
if total < 0:
raise ValueError("Cannot remove non-positive amount!")
to_remove = total
while to_remove != 0:
idx = np.random.choice(range(len(array)), p=probs)
removeable = min(array[idx], to_remove)
array[idx] = array[idx] - removeable
to_remove = to_remove - removeable
return array
输出(例如)
>>>a = np.array([2, 3, 5, 10])
>>>pvals = np.array([0.1, 0.1, 0.4, 0.4])
>>>n = 10
>>>print(remove_random(a, n, pvals))
<<<[2 3 5 0]
当total
接近数组的总和时,它将放慢速度(许多值最终为零),但是至少该方法不再卡住了。可以通过例如调用np.random.choice
时仅采用非零元素并将其相关概率标准化。