在将这一简单的Octave代码行转换为Python时,我想知道是否有一种更快/更干净的方法:
给出两个矩阵,八度命令使用布尔矩阵Y
对矩阵R
的行进行平均,以标记感兴趣的值:
load (‘mydata.mat’)
row1_mean = mean( Y(1, R(1, :) ) )
又好又快又容易。尝试在Python中做到这一点要简洁得多,但到目前为止,我可以像这样到达那里:
import numpy as np
from scipy import io as spio
myDict = spio.loadmat(‘mydata.mat’)
Y_mat = myDict['Y']
R_mat = myDict['R']
maskR = ~R_mat.astype(bool)[0][:] # row as boolean so we can invert it
maskR = maskR.astype(int) # turn it back to 1s & 0s
maskedY = np.ma.masked_array(Y_mat[0][:], mask=maskR) # mask row of Y with R matrix
row1_mean = maskedY.mean() # get the mean
我可能会缺少更好的方法。
特别是,是否有一种更简单的方法可以将1s和0s矩阵求逆?
也许也有一种更直接的方法来获取数组切片的均值(我知道axis
),但要考虑到屏蔽数组?
答案 0 :(得分:2)
如果我了解您要正确执行的操作,这是一种更好的方法:
row1_mean = Y_mat[0][R_mat[0].astype(bool)].mean()
那就是如果您只想要单行的均值。您可以像这样计算每一行的均值:
means = np.nanmean(np.where(mask, arr, np.nan), axis=1)
# if every value in a given row is masked, the mean will be calculated as nan. Change those to zeros
means[np.isnan(means)] = 0
作为将来的注释,您实际上可以用布尔数组对Numpy数组建立索引(我想就像八度一样)。这是一个简单的示例:
import numpy as np
arr = np.arange(10*5).reshape(10,5)
mask = np.random.randint(0, 2, (10, 5), dtype=bool)
print('original array\n%s\n' % arr)
print('boolean masked array\n%s\n' % arr[mask])
输出:
original array
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]
[25 26 27 28 29]
[30 31 32 33 34]
[35 36 37 38 39]
[40 41 42 43 44]
[45 46 47 48 49]]
boolean masked array
[ 1 2 3 4 7 8 10 11 12 14 15 19 26 27 29 33 38 39 44 45 46]
如您所见,布尔索引将使2D数组变平(出于Paul Panzer在评论中解释的原因)。这就是为什么我在上面的第二个答案中使用np.where
的原因。
答案 1 :(得分:1)
如果您想使用屏蔽数组,这是一种简化的方法:
import numpy as np
# create some mock data
R_mat = np.arange(16).reshape(4, 4)
Y_mat = np.random.randint(0, 2, (4, 4))
R_mat
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
Y_mat
# array([[0, 1, 0, 1],
# [0, 1, 1, 0],
# [0, 1, 0, 1],
# [0, 0, 1, 0]])
# compute all row means or all column means at once
# use Y_mat==0 to invert and convert to bool in one go
row_means = np.ma.MaskedArray(R_mat, Y_mat==0).mean(axis=1)
col_means = np.ma.MaskedArray(R_mat, Y_mat==0).mean(axis=0)
row_means
# masked_array(data=[2.0, 5.5, 10.0, 14.0],
# mask=[False, False, False, False],
# fill_value=1e+20)
col_means
# masked_array(data=[--, 5.0, 10.0, 7.0],
# mask=[ True, False, False, False],
# fill_value=1e+20)
# or take just one row or column and get the mean
np.ma.MaskedArray(R_mat, Y_mat==0)[2].mean()
# 10.0
np.ma.MaskedArray(R_mat, Y_mat==0)[:, 0].mean()
# masked
如果出于某些原因要避免使用屏蔽数组:
nrow, ncol = R_mat.shape
I, J = np.where(Y_mat)
row_means = np.bincount(I, R_mat[I, J], nrow) / np.bincount(I, None, nrow)
J, I = np.where(Y_mat.T)
col_means = np.bincount(J, R_mat[I, J], ncol) / np.bincount(J, None, ncol)
# __main__:1: RuntimeWarning: invalid value encountered in true_divide
row_means
# array([ 2. , 5.5, 10. , 14. ])
col_means
# array([nan, 5., 10., 7.])