布尔掩码切片的意外结果
Unexpected result from boolean mask slicing
我对下面示例中 numpy
数组切片的工作方式感到困惑。我不知道切片究竟是如何工作的,希望得到解释。
import numpy as np
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
])
m = [False,True,True,False]
# Test 1 - Expected behaviour
print(arr[m])
Out:
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
# Test 2 - Expected behaviour
print(arr[m,:])
Out:
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
# Test 3 - Expected behaviour
print(arr[:,m])
Out:
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
### What's going on here? ###
# Test 4
print(arr[m,m])
Out:
array([ 6, 11]) # <--- diagonal components. I expected [[6,7],[10,11]].
我发现 arr[:,m][m]
可以达到我想要的结果。但我仍然很好奇这是如何工作的。
这就是 numpy 数组的索引工作方式。通常,如果您有特定的 "slices" 行和列,您想要 select 您只需执行:
import numpy as np
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
])
# You want to check out rows 2-3 cols 2-3
print(arr[2:4,2:4])
Out:
[[11 12]
[15 16]]
现在假设您想要 select 特定行和列索引的任意组合,例如您想要 row0-col2 和 row2-col3
print(arr[[0, 2], [2, 3]])
Out:
[ 3 12]
你做的和上面的一样。 [m,m]
等同于:
[m,m] == [[False,True,True,False], [False,True,True,False]]
这相当于说你想要 row1-col1 和 row2-col2
print(arr[[1, 2], [1, 2]])
Out:
[ 6 11]
我不知道为什么,但这是 numpy
处理一维 boolean
数组元组切片的方式:
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
])
m1 = [True, False, True]
m2 = [False, False, True, True]
# Pseudocode for what NumPy does
#def arr[m1,m2]:
# intm1 = np.transpose(np.argwhere(m1)) # [True, False, True] -> [0,2]
# intm2 = np.transpose(np.argwhere(m2)) # [False, False, True, True] -> [2,3]
# return arr[intm1,intm2] # arr[[0,2],[2,3]]
print(arr[m1,m2]) # --> [3 12]
我所期待的是对数组的非连续段进行切片行为;选择行和列的交集,可以通过以下方式实现:
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
])
m1 = [True, False, True]
m2 = [False, False, True, True]
def row_col_select(arr, *ms):
n = arr.ndim
assert(len(ms) == n)
# Accumulate a full boolean mask which will have the shape of `arr`
accum_mask = np.reshape(True, (1,) * n)
for i in range(n):
shape = tuple([1]*i + [arr.shape[i]] + [1]*(n-i-1))
m = np.reshape(ms[i], shape)
accum_mask = np.logical_and(accum_mask, m)
# Select `arr` according to full boolean mask
# The boolean mask is the multiplication of the boolean arrays across each corresponding dimension. E.g. for m1 and m2 above it is:
# m1: | m2: False False True True
# |
# True | [[False False True True]
# False | [False False False False]
# True | [False False True True]]
return arr[accum_mask]
print(row_col_select(arr,m1,m2)) # --> [ 3 4 11 12]
In [55]: arr = np.array([
...: [1,2,3,4],
...: [5,6,7,8],
...: [9,10,11,12],
...: [13,14,15,16]
...: ])
...: m = [False,True,True,False]
在您的所有示例中,我们可以使用此 m1
而不是布尔列表:
In [58]: m1 = np.where(m)[0]
In [59]: m1
Out[59]: array([1, 2])
如果 m
是一个类似于 arr
的二维数组,那么我们可以将它用于 arr
中的 select 个元素——但它们会被分解;但是当沿一维使用 select 时,等效数组索引更清晰。是的,我们可以以不同的顺序甚至多次使用 np.array([2,1])
或 np.array([2,1,1,2])
到 select 行。但是用 m1
代替 m
不会丢失任何信息或控制。
Select 行或列:
In [60]: arr[m1]
Out[60]:
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
In [61]: arr[:,m1]
Out[61]:
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
使用 2 个数组,我们得到 2 个元素,arr[1,1]
和 arr[2,2]
。
In [62]: arr[m1, m1]
Out[62]: array([ 6, 11])
请注意,在 MATLAB 中我们必须使用 sub2ind
来做同样的事情。在 numpy
中容易的事情在 MATLAB 中有点难;对于块,它是另一种方式。
为了得到一个块,我们必须创建一个列数组来广播第一行:
In [63]: arr[m1[:,None], m1]
Out[63]:
array([[ 6, 7],
[10, 11]])
如果这太难记了,np.ix_
可以帮我们做:
In [64]: np.ix_(m1,m1)
Out[64]:
(array([[1],
[2]]),
array([[1, 2]]))
[63] 与 [62] 做同样的事情;不同之处在于 2 个阵列的广播方式不同。这与在这些添加中所做的广播相同:
In [65]: m1+m1
Out[65]: array([2, 4])
In [66]: m1[:,None]+m1
Out[66]:
array([[2, 3],
[3, 4]])
这种索引行为是完全一致的——前提是我们不从其他语言导入期望。
我使用了m1
,因为布尔数组不广播,如下所示:
In [67]: np.array(m)
Out[67]: array([False, True, True, False])
In [68]: np.array(m)[:,None]
Out[68]:
array([[False],
[ True],
[ True],
[False]])
In [69]: arr[np.array(m)[:,None], np.array(m)]
...
IndexError: too many indices for array
实际上 'column' 布尔值也不起作用:
In [70]: arr[np.array(m)[:,None]]
...
IndexError: boolean index did not match indexed array along dimension 1; dimension is 4 but corresponding boolean dimension is 1
我们可以使用 logical_and
针对行布尔值广播列布尔值:
In [72]: mb = np.array(m)
In [73]: mb[:,None]&mb
Out[73]:
array([[False, False, False, False],
[False, True, True, False],
[False, True, True, False],
[False, False, False, False]])
In [74]: arr[_]
Out[74]: array([ 6, 7, 10, 11]) # 1d result
你引用的是这种情况:"If obj.ndim == x.ndim, x[obj] returns a 1-dimensional array filled with the elements of x corresponding to the True values of obj"
你的其他引述:
*"Advanced indexing always returns a copy of the data (contrast with basic slicing that returns a view)."*
表示如果arr1 = arr[m,:]
,arr1
是一个副本,对arr1
的任何修改都不会影响arr
。但是我可以使用 arr[m,:]=10
来修改 arr
。副本的替代方法是 view
,如在基本索引中一样,arr2=arr[0::2,:]
。 arr2
的修改也会修改 arr
。
您可以使用矩阵乘法创建二维蒙版。
import numpy as np
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
])
m = [False,True,True,False]
mask2d = np.array([m]).T * m
print(arr[mask2d])
输出:
[ 6 7 10 11]
或者,您可以使用矩阵格式输出。
print(np.ma.masked_array(arr, ~mask2d))
我对下面示例中 numpy
数组切片的工作方式感到困惑。我不知道切片究竟是如何工作的,希望得到解释。
import numpy as np
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
])
m = [False,True,True,False]
# Test 1 - Expected behaviour
print(arr[m])
Out:
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
# Test 2 - Expected behaviour
print(arr[m,:])
Out:
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
# Test 3 - Expected behaviour
print(arr[:,m])
Out:
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
### What's going on here? ###
# Test 4
print(arr[m,m])
Out:
array([ 6, 11]) # <--- diagonal components. I expected [[6,7],[10,11]].
我发现 arr[:,m][m]
可以达到我想要的结果。但我仍然很好奇这是如何工作的。
这就是 numpy 数组的索引工作方式。通常,如果您有特定的 "slices" 行和列,您想要 select 您只需执行:
import numpy as np
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
])
# You want to check out rows 2-3 cols 2-3
print(arr[2:4,2:4])
Out:
[[11 12]
[15 16]]
现在假设您想要 select 特定行和列索引的任意组合,例如您想要 row0-col2 和 row2-col3
print(arr[[0, 2], [2, 3]])
Out:
[ 3 12]
你做的和上面的一样。 [m,m]
等同于:
[m,m] == [[False,True,True,False], [False,True,True,False]]
这相当于说你想要 row1-col1 和 row2-col2
print(arr[[1, 2], [1, 2]])
Out:
[ 6 11]
我不知道为什么,但这是 numpy
处理一维 boolean
数组元组切片的方式:
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
])
m1 = [True, False, True]
m2 = [False, False, True, True]
# Pseudocode for what NumPy does
#def arr[m1,m2]:
# intm1 = np.transpose(np.argwhere(m1)) # [True, False, True] -> [0,2]
# intm2 = np.transpose(np.argwhere(m2)) # [False, False, True, True] -> [2,3]
# return arr[intm1,intm2] # arr[[0,2],[2,3]]
print(arr[m1,m2]) # --> [3 12]
我所期待的是对数组的非连续段进行切片行为;选择行和列的交集,可以通过以下方式实现:
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
])
m1 = [True, False, True]
m2 = [False, False, True, True]
def row_col_select(arr, *ms):
n = arr.ndim
assert(len(ms) == n)
# Accumulate a full boolean mask which will have the shape of `arr`
accum_mask = np.reshape(True, (1,) * n)
for i in range(n):
shape = tuple([1]*i + [arr.shape[i]] + [1]*(n-i-1))
m = np.reshape(ms[i], shape)
accum_mask = np.logical_and(accum_mask, m)
# Select `arr` according to full boolean mask
# The boolean mask is the multiplication of the boolean arrays across each corresponding dimension. E.g. for m1 and m2 above it is:
# m1: | m2: False False True True
# |
# True | [[False False True True]
# False | [False False False False]
# True | [False False True True]]
return arr[accum_mask]
print(row_col_select(arr,m1,m2)) # --> [ 3 4 11 12]
In [55]: arr = np.array([
...: [1,2,3,4],
...: [5,6,7,8],
...: [9,10,11,12],
...: [13,14,15,16]
...: ])
...: m = [False,True,True,False]
在您的所有示例中,我们可以使用此 m1
而不是布尔列表:
In [58]: m1 = np.where(m)[0]
In [59]: m1
Out[59]: array([1, 2])
如果 m
是一个类似于 arr
的二维数组,那么我们可以将它用于 arr
中的 select 个元素——但它们会被分解;但是当沿一维使用 select 时,等效数组索引更清晰。是的,我们可以以不同的顺序甚至多次使用 np.array([2,1])
或 np.array([2,1,1,2])
到 select 行。但是用 m1
代替 m
不会丢失任何信息或控制。
Select 行或列:
In [60]: arr[m1]
Out[60]:
array([[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
In [61]: arr[:,m1]
Out[61]:
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
使用 2 个数组,我们得到 2 个元素,arr[1,1]
和 arr[2,2]
。
In [62]: arr[m1, m1]
Out[62]: array([ 6, 11])
请注意,在 MATLAB 中我们必须使用 sub2ind
来做同样的事情。在 numpy
中容易的事情在 MATLAB 中有点难;对于块,它是另一种方式。
为了得到一个块,我们必须创建一个列数组来广播第一行:
In [63]: arr[m1[:,None], m1]
Out[63]:
array([[ 6, 7],
[10, 11]])
如果这太难记了,np.ix_
可以帮我们做:
In [64]: np.ix_(m1,m1)
Out[64]:
(array([[1],
[2]]),
array([[1, 2]]))
[63] 与 [62] 做同样的事情;不同之处在于 2 个阵列的广播方式不同。这与在这些添加中所做的广播相同:
In [65]: m1+m1
Out[65]: array([2, 4])
In [66]: m1[:,None]+m1
Out[66]:
array([[2, 3],
[3, 4]])
这种索引行为是完全一致的——前提是我们不从其他语言导入期望。
我使用了m1
,因为布尔数组不广播,如下所示:
In [67]: np.array(m)
Out[67]: array([False, True, True, False])
In [68]: np.array(m)[:,None]
Out[68]:
array([[False],
[ True],
[ True],
[False]])
In [69]: arr[np.array(m)[:,None], np.array(m)]
...
IndexError: too many indices for array
实际上 'column' 布尔值也不起作用:
In [70]: arr[np.array(m)[:,None]]
...
IndexError: boolean index did not match indexed array along dimension 1; dimension is 4 but corresponding boolean dimension is 1
我们可以使用 logical_and
针对行布尔值广播列布尔值:
In [72]: mb = np.array(m)
In [73]: mb[:,None]&mb
Out[73]:
array([[False, False, False, False],
[False, True, True, False],
[False, True, True, False],
[False, False, False, False]])
In [74]: arr[_]
Out[74]: array([ 6, 7, 10, 11]) # 1d result
你引用的是这种情况:"If obj.ndim == x.ndim, x[obj] returns a 1-dimensional array filled with the elements of x corresponding to the True values of obj"
你的其他引述:
*"Advanced indexing always returns a copy of the data (contrast with basic slicing that returns a view)."*
表示如果arr1 = arr[m,:]
,arr1
是一个副本,对arr1
的任何修改都不会影响arr
。但是我可以使用 arr[m,:]=10
来修改 arr
。副本的替代方法是 view
,如在基本索引中一样,arr2=arr[0::2,:]
。 arr2
的修改也会修改 arr
。
您可以使用矩阵乘法创建二维蒙版。
import numpy as np
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
])
m = [False,True,True,False]
mask2d = np.array([m]).T * m
print(arr[mask2d])
输出:
[ 6 7 10 11]
或者,您可以使用矩阵格式输出。
print(np.ma.masked_array(arr, ~mask2d))