理解 numpy 中的布尔索引

布尔索引 boolean indexing 定义

布尔索引是一种通过使用布尔值(True或False)数组来选择数组中的元素的方法。布尔数组的长度必须与源数组相同,它指示了哪些元素应该被选择或过滤。

通过使用布尔索引,我们可以根据指定条件来选择数组中的元素,这些条件可以是基于元素值的比较、逻辑运算符的组合或其他布尔表达式。

简单来将, 布尔索引就是让 1个 boolean dtype 的adarray 作为另1个adarry 的 filter, 所以 布尔索引更应该是个filter 而不是 index。

语法:

arr_a[arr_b] 其中 arr_a 和 arr_b 都是ndarray 对象, 而且arr_b.dtype 是 bool


"""
introduce of ndarray boolean index:
    1. ndarray[bool_array]     # means select the elements where bool_array is True
    2. bool_array could be a boolean ndarray or a list of boolean values
    3. bool_array should have the same length with the ndarray
    4. bool_array could be generated by comparison operators e.g. >, <, ==, !=
    5. bool_array could be generated by logical operators e.g. &, |, ~
    6. bool_array could be generated by np.logical_and, np.logical_or, np.logical_not
    7. bool_array could be generated by np.all, np.any
"""



1维数组的 boolean indexing

举个例子:

    arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    logger.info(f"arr: {arr}") # [0 1 2 3 4 5 6 7 8 9]
    arr_filter = [True, False, True, False, True, False, True, False, True, False]
    logger.info(f"arr[arr_filter]: {arr[arr_filter]}") # [0 2 4 6 8]

    arr_filter = [True, False, True, False, True, False, True, False, True, False, True]
    # logger.info(f"arr[arr_filter]: {arr[arr_filter]}") # error, because the length of arr_filter is not the same as arr

上面例子中, 首先创建了1个1维数组 arr, dtype = int32

然后构建了另1个 1维数组arr_filter , dtype = bool ,
然后利用 arr[arr_filter] 就可以返回另 一个 adarray 对象, 新的对象只包含了 符合 arr_filter 的元素。
在这个例子中, 就得到了1个偶数的子数组 [0, 2, 4, 6, 8]

值得注意的是, arr_filter 的shape 必须与 arr 一致, 才能另到True/false 和 arr的元素一一对应, 否则会出错



更优雅地编写arr_filter

上的arr_filter 是暴力定义的, 显然不符合实际场景
其实如果有1个公式的, 例如上面例子是求偶数, 是可以有其他方法来定义的.

  1. 直接利用源arr 名字套用公式
  arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    logger.info(f"arr: {arr}") # [0 1 2 3 4 5 6 7 8 9]
    arr_filter = arr % 2 == 0
    logger.info(f"arr_filter: {arr_filter}") # [ True False  True False  True False  True False  True False]

是不是很方便?

  1. 加上括号
    arr_filter = (arr % 2 == 0)
    logger.info(f"arr_filter: {arr_filter}") # [ True False  True False  True False  True False  True False]

注意 return的不是1个tuple, 而是1个ndarray

  1. 可以用logic 运算符 & | 注意不能是&& ||
    arr_filter = (arr % 2 == 0) & (arr > 5)
    logger.info(f"arr_filter: {arr_filter}") # [False False False False False False  True  True  True  True]
  1. 不能用随便用[ ] 中括号
    (arr % 2 ==0) 返回的是1个1 维 的ndarray
    而[ arr % 2 = 0) 返回的是2维 ndarray

    arr_filter_2_dimension = [arr % 2 == 0]
    logger.info(f"arr_filter_2_dimension: {arr_filter_2_dimension}") # [array([ True, False,  True, False,  True, False,  True, False,  True, False])]



使用lambda

如果是1般的python list , 我们可以lambda 去做filter

例如:

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered_list = list(filter(lambda x: x % 2 == 0, my_list))
print(filtered_list) # [2, 4, 6, 8, 10]

还有更简洁的写法:

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered_list = [x for x in my_list if x % 2 == 0]
print(filtered_list)

而这种简洁写法也适用于 ndarray

arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr_filter = [True if x % 2 == 0 else False for x in arr]
logger.info(f"arr_filter: {arr_filter}") # [True, False, True, False, True, False, True, False, True, False]
logger.info(f"arr[arr_filter]: {arr[arr_filter]}") # [0 2 4 6 8]

上面写法还可以进一步简洁

arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr_filter = [x for x in arr if x % 2 == 0]
logger.info(f"arr_filter: {arr_filter}") # [0, 2, 4, 6, 8]



使用np.vectorize()

np.vectorize 可以把1个普通的函数 转化为1个可以 用于ndarrary的函数
如:

arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# np.vectorize(lambda x: x % 2 == 0) returns a function that can be applied to each element of the array
arr_filter = np.vectorize(lambda x: x % 2 == 0)(arr)
logger.info(f"arr_filter: {arr_filter}") # [False False False False False False  True  True  True  True]



浅复制与深复制

正常的切片操作 是浅复制
例如:

	arr_a = np.arange(1, 11)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]    
    arr_a.shape = (2, 5)
    logger.info(f"arr_a: {arr_a}") # [[ 1  2  3  4  5] [ 6  7  8  9 10]]
    arr_a0 = arr_a[0]
    arr_a0[0] = 100
    # arr_a0 is a reference to arr_a[0]
    assert arr_a0[0] == arr_a[0][0] == 100

arr_a0 是 arr_a 中第1个n-1 维元素, 当arr_a0 的元素被修改, 则代表arr_a 本身被修改

用[:] 方法切片也是一样的

    logger.info("deep copy2 ================================")
    arr_a = np.arange(1, 11)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]    
    arr_a.shape = (2, 5)
    logger.info(f"arr_a: {arr_a}") # [[ 1  2  3  4  5] [ 6  7  8  9 10]]
    arr_a0 = arr_a[:, 2:] # [[3 4 5] [8 9 10]]
    # arr_a0 is a reference to arr_a[0]
    arr_a0[0][0] = 100
    assert arr_a0[0][0] == arr_a[0][2] == 100

但是, boolean 索引 build 出来的新adarray 是深复制出来的。 占用另一块内存, 与源数组不相互影响

    arr_a = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    arr_filter = arr_a % 2 == 0
    logger.info(f"arr_filter: {arr_filter}") # [ True False  True False  True False  True False  True False]

    arr_a0 = arr_a[arr_filter]
    arr_a0[0] = 100

    assert arr_a[0] ==0



2维数组的 boolean indexing

2 维数组和多维数组的 boolean indexing 也不难理解, 我们只要构建 filter 的 ndarray对象,并令其shape 与源adarray保持一致

值得注意是, 无论源数组是多少维

filter 之后的新数组都是1维的, 只包含了符合条件的1为数组, 为何不能保持原来的维数? 因为元素数量减少了, 无法保持原来的形状

例子:

def boolean_index_2_dimension():
    logger.info("boolean_index_2_dimension ================================")
    arr_a = np.arange(1, 11)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]    
    arr_a.shape = (2, 5)
    logger.info(f"arr_a: {arr_a}") # [[ 1  2  3  4  5] [ 6  7  8  9 10]]

    arr_filter = arr_a % 2 == 0 # [[False  True False  True False] [ True False  True False  True]]
    logger.info(f"arr_filter: {arr_filter}")
    logger.info(f"arr_a[arr_filter]: {arr_a[arr_filter]}") # [2 4 6 8 10] why not [[2 4] [6 8 10]] because the shape of arr_filter is (2, 5)


def boolean_index_3_dimension():
    logger.info("boolean_index_2_dimension ================================")
    arr_a = np.arange(1, 28)  # 1 to 27
    arr_a.shape = (3, 3, 3)
    logger.info(f"arr_a: {arr_a}") # [[[ 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]]]

    arr_filter = arr_a % 2 == 0 # [[[False  True False] [ True False  True] [False  True False]] [[ True False  True] [False  True False] [ True False  True]] [[False  True False] [ True False  True] [False  True False]]]
    logger.info(f"arr_filter: {arr_filter}")
    logger.info(f"arr_a[arr_filter]: {arr_a[arr_filter]}") # [ 2  4  6  8 10 12 14 16 18 20 22 24 26] why not [[2 4 6] [8 10 12] [14 16 18] [20 22 24 26]] because the shape of arr_filter is (3, 3, 3)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值