我有一个由大量0和一些非零条目组成的numpy数组,例如像这样(只是一个玩具示例):
myArray = np.array([[ 0. , 0. , 0.79],
[ 0. , 0. , 0. ],
[ 0. , 0. , 0. ],
[ 0. , 0.435 , 0. ]])
现在我想以给定的概率移动每个非零项,这意味着一些条目被移动,一些可能保留在当前位置。某些行不允许包含非零条目,这意味着不允许在那里移动值。我实现了如下:
import numpy as np
# for reproducibility
np.random.seed(2)
myArray = np.array([[ 0. , 0. , 0.79],
[ 0. , 0. , 0. ],
[ 0. , 0. , 0. ],
[ 0. , 0.435 , 0. ]])
# list of rows where numbers are not allowed to be moved to
ignoreRows = [2]
# moving probability
probMove = 0.3
# get non-zero entries
nzEntries = np.nonzero(myArray)
# indices of the non-zero entries as tuples
indNZ = zip(nzEntries[0], nzEntries[1])
# store values
valNZ = [myArray[i] for i in indNZ]
# generating probabilities for moving for each non-zero entry
lProb = np.random.rand(len(nzEntries))
allowedRows = [ind for ind in xrange(myArray.shape[0]) if ind not in ignoreRows] # replace by "range" in python 3.x
allowedCols = [ind for ind in xrange(myArray.shape[1])] # replace by "range" in python 3.x
for indProb, prob in enumerate(lProb):
# only move with a certain probability
if prob <= probMove:
# randomly change position
myArray[np.random.choice(allowedRows), np.random.choice(allowedCols)] = valNZ[indProb]
# set old position to zero
myArray[indNZ[indProb]] = 0.
print myArray
首先,我确定非零项的所有索引和值。然后我为这些条目中的每一个分配一定的概率,以确定是否将移动条目。然后我得到允许的目标行。
在第二步中,我遍历索引列表并根据它们的移动概率移动它们,这是通过从允许的行和列中进行选择,为这些新索引分配相应的值并设置“旧”值来完成的。到0。
上面的代码可以正常工作,但是在这种情况下速度真的很重要,我想知道是否有更有效的方法来做到这一点。
编辑: Hpaulj的回答帮助我摆脱了很好的for循环以及我接受他答案的原因。我收录了他的评论,并在下面发布了一个答案,以防万一其他人偶然发现这个例子,并想知道我最终是如何使用他的答案的。
答案 0 :(得分:2)
您可以使用数组索引元素,因此:
valNZ=myArray[nzEntries]
可以取代zip
和理解。
简化这两项任务:
allowedCols=np.arange(myArray.shape[1]);
allowedRows=np.delete(np.arange(myArray.shape[0]), ignoreRows)
使用:
I=lProb<probMove; valNZ=valNZ[I];indNZ=indNZ[I]
您不需要每次在循环中执行prog<probMove
测试;只需迭代valNZ
和indNZ
。
我认为您可以同时为所有这些random.choice
生成valNZ
:
np.random.choice(np.arange(10), 10, True)
# 10 choices from the range with replacement
有了它,应该可以在没有循环的情况下移动所有点。
我还没有弄清楚细节。
有一种方法可以使您的迭代移动与任何并行移动不同。如果目标选择是另一个值,则迭代方法可以重写,并且可能移动给定值几次。并行代码不会执行顺序移动。你必须决定一个是否正确。
有一个ufunc
方法.at
,它执行无缓冲的操作。它适用于add
之类的操作,但我不知道是否适用于这样的索引移动。
迭代移动的简化版本:
In [106]: arr=np.arange(20).reshape(4,5)
In [107]: I=np.nonzero(arr>10)
In [108]: v=arr[I]
In [109]: rows,cols=np.arange(4),np.arange(5)
In [110]: for i in range(len(v)):
dest=(np.random.choice(rows),np.random.choice(cols))
arr[dest]=v[i]
arr[I[0][i],I[1][i]] = 0
In [111]: arr
Out[111]:
array([[ 0, 18, 2, 14, 11],
[ 5, 16, 7, 13, 19],
[10, 0, 0, 0, 0],
[ 0, 17, 0, 0, 0]])
可能的矢量化版本:
In [117]: dest=(np.random.choice(rows,len(v),True),np.random.choice(cols,len(v),True))
In [118]: dest
Out[118]: (array([1, 1, 3, 1, 3, 2, 3, 0, 0]), array([3, 0, 0, 1, 2, 3, 4, 0, 1]))
In [119]: arr[dest]
Out[119]: array([ 8, 5, 15, 6, 17, 13, 19, 0, 1])
In [120]: arr[I]=0
In [121]: arr[dest]=v
In [122]: arr
Out[122]:
array([[18, 19, 2, 3, 4],
[12, 14, 7, 11, 9],
[10, 0, 0, 16, 0],
[13, 0, 15, 0, 17]])
如果我之后设置了0
,则会有更多的零。
In [124]: arr[dest]=v
In [125]: arr[I]=0
In [126]: arr
Out[126]:
array([[18, 19, 2, 3, 4],
[12, 14, 7, 11, 9],
[10, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0]])
相同dest
,但迭代完成:
In [129]: for i in range(len(v)):
.....: arr[dest[0][i],dest[1][i]] = v[i]
.....: arr[I[0][i],I[1][i]] = 0
In [130]: arr
Out[130]:
array([[18, 19, 2, 3, 4],
[12, 14, 7, 11, 9],
[10, 0, 0, 16, 0],
[ 0, 0, 0, 0, 0]])
由于这种小尺寸和高移动密度,迭代和矢量化解决方案之间的差异很大。对于稀疏数组,它们会更少。
答案 1 :(得分:1)
下面你可以找到我在提到hpaulj的答案和this question的答案后提出的代码。这样,我摆脱了for循环,它改进了很多代码。因此,我接受了hpaulj的回答。也许下面的代码可以帮助处于类似情况的其他人。
import numpy as np
from itertools import compress
# for reproducibility
np.random.seed(2)
myArray = np.array([[ 0. , 0.2 , 0.79],
[ 0. , 0. , 0. ],
[ 0. , 0. , 0. ],
[ 0. , 0.435 , 0. ]])
# list of rows where numbers are not allowed to be moved to
ignoreRows= []
# moving probability
probMove = 0.5
# get non-zero entries
nzEntries = np.nonzero(myArray)
# indices of the non-zero entries as tuples
indNZ = zip(nzEntries[0],nzEntries[1])
# store values
valNZ = myArray[nzEntries]
# generating probabilities for moving for each non-zero entry
lProb = np.random.rand(len(valNZ))
# get the rows/columns where the entries are allowed to be moved
allowedCols = np.arange(myArray.shape[1]);
allowedRows = np.delete(np.arange(myArray.shape[0]), ignoreRows)
# get the entries that are actually moved
I = lProb < probMove
print I
# get the values of the entries that are moved
valNZ = valNZ[I]
# get the indices of the entries that are moved
indNZ = list(compress(indNZ, I))
# get the destination for the entries that are moved
dest = (np.random.choice(allowedRows, len(valNZ), True), np.random.choice(allowedCols, len(valNZ), True))
print myArray
print indNZ
print dest
# set the old indices to 0
myArray[zip(*indNZ)] = 0
# move the values to their respective destination
myArray[dest] = valNZ
print myArray