NumPy 学习笔记(二)
数组索引:
1、ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样;
由于数组可能是多维的,所以必须为数组的每个维度指定一个切片,使用切片时返回的是一个子数组
2、整数索引:获取相应下标的元素的值
3、布尔数组索引:布尔索引通过布尔运算来获取符合指定条件的元素的数组
4、花式索引:不同的索引之间可以相互搭配,同时也可以和切片一起使用
import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # 切片,返回 arr[:2][1:] 的相应元素 arr_t = arr[:2, 1:] # 输出 arr_t: [[2 3] [5, 6]] print("arr[:2, 1:]: ", arr_t) # 整数索引允许使用相应下标组成的多维数组 # 这里输出 arr[0][1], arr[1][-1], arr[-1][0] arr_t = arr[[0, 1, -1], [1, -1, 0]] print("arr_t: ", arr_t) # 输出 arr_t: [2 6 7] # 布尔索引使用布尔运算选出符合条件的元素 arr_t = arr[arr > 5] print("arr > 5: ", arr_t) arr_t = arr[arr % 2 == 1] # [6 7 8 9] print("arr % 2 == 1: ", arr_t) # [1 3 5 7 9] # 组合索引 # arr[row, col] row指定行,col 指定列,其切片方法与 python 操作一样 arr_t = arr[::2, ::2] print("arr[::2, ::2]: ", arr_t) # 输出 [[1 3] [7 9]] # 对元素值大于 5 的元素组成的子数组进行切片 arr_t = arr[arr > 5][::2] print("arr[arr > 5][::2]: ", arr_t) # 输出 [6 8] # 对行进行切片,同时获取列为 0 的元素,这里为 arr[1][0], arr[2][0] arr_t = arr[1:, 0] print("arr[1:, 0]: ", arr_t) # 输出 [4 7] arr_t = arr[-1, :] print("arr[-1, :]: ", arr_t) # 同理,输出 [7 8 9]
NumPy 广播:
1、广播是 numpy 对不同形状(shape)的数组进行数值计算的方式
2、如果两个数组 a 和 b 形状相同,即满足 a.shape == b.shape,那么 a 与 b 的算术运算在对应位进行
3、广播规则:
①让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐
②输出数组的形状是输入数组形状的各个维度上的最大值
③如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错
④当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值
import numpy as np one = np.arange(1, 26) # 修改数组形状为 5*5 one.shape = (5, 5) print("one: ", one) # 生成 [50...25) 的数组 two = np.arange(50, 25, -1) two.shape = (5, 5) print("two: ", two) print("one + two: ", one + two) arr_one = np.array([[1, 2, 3, 4], [5, 6, 7, 8]]) arr_two = np.array([1, 2, 3, 4]) # 输出 [[2 3 6 8] [6 8 10 12]] print("arr_one + arr_two: ", arr_one + arr_two) arr_two = np.array([[1], [2]]) # 输出 [[2 3 4 5] [7 8 9 10]] print("arr_one + arr_two: ", arr_one + arr_two) arr_two = np.array([2]) # 输出 [[2 4 6 8] [10 12 14 16]] print("arr_one * arr_two: ", arr_one * arr_two)
迭代数组:
1、NumPy 1.6 中引入的迭代器对象 nditer
提供了许多灵活的方式来以系统的方式访问一个或多个数组的所有元素
2、nditer 使用标准 Python 迭代器接口逐个提供每个元素
3、nditer
对象提供了一个 order 参数来控制迭代的顺序,默认 order="K",即保持原有顺序
4、默认情况下,nditer 将输入数组视为只读对象。 要修改数组元素,必须指定读写或只写模式
5、外部循环:若不使用外部循环,则数组的每个元素由迭代器一次提供一个;我们可以将一维最内层循环移动到迭代器外部的代码中,
通过强制'C'和'F'顺序,而得到不同的外部循环大小。 需要我们通过指定迭代器标志来启用此模式
import numpy as np # 产生 [0, 2pi] 的等差数组,其元素个数为 9 arr = np.linspace(0, 2 * np.pi, 9) arr.shape = (3, 3) print("Order is C") # 按行 for o in np.nditer(arr): print(o) print("Order is F") # 按列 for o in np.nditer(arr.copy(order="F")): print(o) # 默认情况下,nditer将输入数组视为只读对象。 要修改数组元素,必须指定读写或只写模式 arr = np.arange(6).reshape(2, 3) for o in np.nditer(arr): o = o*2 print("arr: ", arr) # 没有改变 for o in np.nditer(arr, op_flags=["readwrite"]): # 注意需要使用省略号索引 o[...] = o*2 print("arr: ", arr) # 外部循环 arr = np.linspace(1, 6, 6).reshape(2, 3) # 输出 [1. 2. 3. 4. 5. 6.] 注意和之前的区别 for o in np.nditer(arr, flags=["external_loop"]): print("arr: ", o) time = 0 # 通过强制'C'和'F'顺序,我们得到不同的外部循环大小 for o in np.nditer(arr, flags=["external_loop"], order="F"): print("arr", time, ": ", o) time += 1 # 跟踪索引 # 跟踪索引或多索引与使用外部循环不兼容,因为它需要每个元素具有不同的索引值 it = np.nditer(arr, flags=["multi_index"]) lst = [] while not it.finished: print("value: {}\nmulti_index: <{}>".format(it[0], it.multi_index)) lst.append(it.multi_index[1] - it.multi_index[0]) it.iternext() print("All it.multi_index[1] - it.multi_index[0]: ", lst)
注:Python中的常规赋值只是更改本地或全局变量字典中的引用,而不是修改现有变量。 这意味着简单地分配给 x 不会将值放入数组的元素中,
而是将 x 作为数组元素引用切换为对指定值的引用。 要实际修改数组的元素,x应该用省略号索引