Python 商务数据分析—— NumPy 学习笔记Ⅱ
一、数组索引与切片
1.1 索引方式对比
索引类型 | 一维数组示例 | 二维数组示例 | 说明 |
下标索引 | a[2] | a[0,1] 或 a[0][1] | 正 / 负下标均可 |
切片索引 | a[1:5:2] | a[1:3,1:2] | 支持步长,冒号表示全选 |
花式索引 | a[[1,2,5]] | a[[1,0],1] | 按索引数组取值 |
布尔索引 | a[a>3] | a[a>3] | 返回满足条件的元素 |
1.2 索引方法完整示例
# 3x3数组arr = np.arange(9).reshape(3, 3)print("原始数组arr:\n", arr)# 输出:# [[0 1 2]# [3 4 5]# [6 7 8]]# 1. 下标索引print("arr[1,1]:", arr[1,1]) # 输出: 4print("arr[-1,-1]:", arr[-1,-1]) # 输出: 8# 2. 切片索引print("arr[1:3,1:2]:\n", arr[1:3,1:2])# 输出:# [[4]# [7]]print("arr[:,:]:\n", arr[:,:]) # 全选,输出整个数组print("arr[:-1,:-1]:\n", arr[:-1,:-1])# 输出:# [[0 1]# [3 4]]# 3. 花式索引print("arr[[1,0],1]:", arr[[1,0],1]) # 输出: [4 1]print("arr[[0,2],[0,2]]:", arr[[0,2],[0,2]]) # 输出: [0 8]# 4. 布尔索引print("arr[arr>5]:", arr[arr>5]) # 输出: [6 7 8]print("arr[(arr>3)&(arr<7)]:", arr[(arr>3)&(arr<7)]) # 输出: [4 5 6]
二、数组元素操作
2.1 值替换方式
方法 | 语法 | 示例 |
索引替换 | a[1]=0 | 直接修改指定位置 |
条件替换 | a[a<3]=1 | 批量修改满足条件的元素 |
where 函数 | np.where(cond, x, y) | 条件为真取 x,否则取 y |
2.2 代码演示
np.random.seed(42)arr = np.random.randint(0, 10, size=(3, 4))print("原始数组arr:\n", arr)# 输出:# [[6 3 7 4]# [7 4 3 7]# [6 9 2 6]]# 1. 索引替换arr[1] = [1, 2, 3, 4] # 替换第2行print("索引替换后:\n", arr)# 输出:# [[6 3 7 4]# [1 2 3 4]# [6 9 2 6]]# 2. 条件替换arr[arr < 5] = 0 # 小于5的元素替换为0print("条件替换后:\n", arr)# 输出:# [[6 0 7 0]# [0 0 0 0]# [6 9 0 6]]# 3. where函数替换arr_where = np.where(arr > 5, 1, -1) # 大于5的元素为1,否则为-1print("where替换后:\n", arr_where)# 输出:# [[ 1 -1 1 -1]# [-1 -1 -1 -1]# [ 1 1 -1 1]]
三、广播机制
3.1 广播原则
- 从后缘维度 (trailing dimension) 开始比较。
- 若维度长度相等或其中一方为 1,则可广播。
- 长度为 1 的维度会被扩展至另一数组的长度。
3.2 典型案例
# 案例1:标量与数组运算a1 = np.random.randint(0, 5, size=(3, 5))print(a1 * 2.345) # 标量自动广播到所有元素# 案例2:不同形状数组运算a = np.arange(3).reshape(3, 1)b = np.arange(3)print(a + b)# 等价于:# [[0,1,2],# [0,1,2],# [0,1,2]] +# [[0,0,0],# [1,1,1],# [2,2,2]]# 输出:# [[0,1,2],# [1,2,3],# [2,3,4]]
3.3 广播兼容性判断
数组 A 形状 | 数组 B 形状 | 是否可广播 | 广播后形状 |
(3,8,2) | (8,3) | 否 | - |
(3,8,2) | (8,1) | 是 | (3,8,2) |
(3,1,8) | (8,1) | 是 | (3,8,8) |
四、数组形状操作
4.1 叠加操作
方向 | 函数 | 示例 |
垂直叠加 | np.vstack() / np.concatenate(axis=0) | v3 = np.vstack([v1,v2]) |
水平叠加 | np.hstack() / np.concatenate(axis=1) | h3 = np.hstack([h1,h2]) |
一维拼接 | np.concatenate(axis=None) | h5 = np.concatenate([h1,h2], axis=None) |
代码示例
arr1 = np.array([[1, 2, 3], [4, 5, 6]])arr2 = np.array([[7, 8, 9], [10, 11, 12]])arr3 = np.array([[1], [2]])# 1. 垂直叠加arr_vstack = np.vstack((arr1, arr2))print("垂直叠加vstack:\n", arr_vstack)# 输出:# [[ 1 2 3]# [ 4 5 6]# [ 7 8 9]# [10 11 12]]# 2. 水平叠加arr_hstack = np.hstack((arr1, arr3))print("水平叠加hstack:\n", arr_hstack)# 输出:# [[1 2 3 1]# [4 5 6 2]]# 3. concatenatearr_concat_v = np.concatenate((arr1, arr2), axis=0) # 垂直叠加arr_concat_h = np.concatenate((arr1, arr3), axis=1) # 水平叠加print("concatenate垂直叠加:\n", arr_concat_v)print("concatenate水平叠加:\n", arr_concat_h)# 4. 一维拼接arr_flat = np.concatenate((arr1, arr2), axis=None)print("一维拼接:\n", arr_flat) # 输出: [ 1 2 3 4 5 6 7 8 9 10 11 12]
4.2 切割操作
方向 | 函数 | 示例 |
水平切割 | np.hsplit() / np.split(axis=1) | np.hsplit(hs1,2) |
垂直切割 | np.vsplit() / np.split(axis=0) | np.vsplit(vs1,4) |
代码示例
arr = np.arange(16).reshape(4, 4)print("原始数组:\n", arr)# 输出:# [[ 0 1 2 3]# [ 4 5 6 7]# [ 8 9 10 11]# [12 13 14 15]]# 1. 水平切割arr_hsplit1 = np.hsplit(arr, 2) # 平均分成2份arr_hsplit2 = np.hsplit(arr, (1, 3)) # 在第1和第3列后切割print("水平切割hsplit1:\n", arr_hsplit1)print("水平切割hsplit2:\n", arr_hsplit2)# 输出:# [array([[0, 1],# [4, 5],# [8, 9],# [12, 13]]),# array([[ 2, 3],# [ 6, 7],# [10, 11],# [14, 15]])]# [array([[0],# [4],# [8],# [12]]),# array([[1, 2],# [5, 6],# [9, 10],# [13, 14]]),# array([[ 3],# [ 7],# [11],# [15]])]# 2. 垂直切割arr_vsplit1 = np.vsplit(arr, 4) # 平均分成4份arr_vsplit2 = np.vsplit(arr, (1, 3)) # 在第1和第3行后切割print("垂直切割vsplit1:\n", arr_vsplit1)print("垂直切割vsplit2:\n", arr_vsplit2)# 输出:# [array([[0, 1, 2, 3]]),# array([[4, 5, 6, 7]]),# array([[ 8, 9, 10, 11]]),# array([[12, 13, 14, 15]])]# [array([[0, 1, 2, 3]]),# array([[4, 5, 6, 7],# [8, 9, 10, 11]]),# array([[12, 13, 14, 15]])]# 3. split指定axisarr_split_col = np.split(arr, 2, axis=1) # 水平切割arr_split_row = np.split(arr, 2, axis=0) # 垂直切割print("split水平切割:\n", arr_split_col)print("split垂直切割:\n", arr_split_row)
4.3 矩阵转置
t1 = np.random.randint(0, 10, size=(3, 4))print("原始矩阵t1:\n", t1)# 输出:# [[1 5 9 8]# [0 0 6 7]# [2 4 1 9]]# 方法1: ndarray.Tprint("转置t1.T:\n", t1.T)# 输出:# [[1 0 2]# [5 0 4]# [9 6 1]# [8 7 9]]# 方法2: transpose()t2 = t1.transpose()t2[0, 0] = 99 # 修改转置矩阵会影响原矩阵print("修改转置矩阵后原矩阵t1:\n", t1)# 输出:# [[99 5 9 8]# [ 0 0 6 7]# [ 2 4 1 9]]
五、Axis 深度理解
5.1 Axis 本质
- axis=0:沿行方向操作,相当于处理每一列。
- axis=1:沿列方向操作,相当于处理每一行。
- 三维数组:axis=2表示处理每个矩阵的元素。
5.2 操作示例
# 案例1:求和arr = np.array([[0, 1], [2, 3], [4, 5]])print("原始数组arr:\n", arr)# 输出:# [[0 1]# [2 3]# [4 5]]sum_axis0 = np.sum(arr, axis=0) # 按列求和sum_axis1 = np.sum(arr, axis=1) # 按行求和print("axis=0按列求和:", sum_axis0) # 输出: [6 9]print("axis=1按行求和:", sum_axis1) # 输出: [1 5 9]# 案例2:求最大值np.random.seed(100)arr_rand = np.random.randint(1, 10, size=(3, 5))print("随机数组arr_rand:\n", arr_rand)# 输出:# [[9 9 4 8 8]# [1 5 3 6 3]# [3 3 2 1 9]]max_axis0 = np.max(arr_rand, axis=0) # 按列求最大值max_axis1 = np.max(arr_rand, axis=1) # 按行求最大值print("axis=0按列最大值:", max_axis0) # 输出: [9 9 4 8 9]print("axis=1按行最大值:", max_axis1) # 输出: [9 6 9]# 案例3:删除元素arr_delete = np.delete(arr_rand, 0, axis=0) # 删除第1行print("删除行后:\n", arr_delete)# 输出:# [[1 5 3 6 3]# [3 3 2 1 9]]arr_delete_col = np.delete(arr_rand, 0, axis=1) # 删除第1列print("删除列后:\n", arr_delete_col)# 输出:# [[9 4 8 8]# [5 3 6 3]# [3 2 1 9]]
六、实战练习
6.1 基础函数
函数 / 方法 | 功能 | 示例语法 |
np.array() | 从序列创建数组 | np.array([1,2,3]) |
np.arange() | 生成等差数列 | np.arange(0, 10, 2) |
np.zeros/ones/full/eye | 生成特殊数组 | np.zeros((3,3)) |
np.reshape() | 修改数组形状 | arr.reshape(2,6) |
np.flatten() | 展平数组(拷贝) | arr.flatten() |
np.ravel() | 展平数组(视图) | arr.ravel() |
np.vstack/hstack | 垂直 / 水平叠加 | np.vstack((a,b)) |
np.hsplit/vsplit | 水平 / 垂直切割 | np.hsplit(a, 2) |
np.where() | 条件替换 | np.where(arr>5, 1, 0) |
np.sum/max/min | 按 axis 计算 | np.sum(arr, axis=1) |
6.2 综合案例:销售数据处理
# 生成三维销售数据[季度, 产品, 地区]sales_data = np.random.randint(100, 500, size=(3, 2, 4))print("销售数据形状:", sales_data.shape) # 输出: (3, 2, 4)# 1. 按产品求和(axis=0)product_total = np.sum(sales_data, axis=0)print("各产品总销量:\n", product_total)# 输出形状: (2, 4),表示2个产品在4个地区的总销量# 2. 按季度求和(axis=(1,2))quarter_total = np.sum(sales_data, axis=(1, 2))print("各季度总销量:", quarter_total)# 输出形状: (3,),表示3个季度的总销量# 3. 按地区求最大值(axis=2)max_region = np.max(sales_data, axis=2)print("各季度各产品在地区的最大销量:\n", max_region)# 输出形状: (3, 2),表示3个季度、2个产品在各地区的最大销量# 4. 数据清洗:将小于150的值替换为150sales_data_clean = np.where(sales_data < 150, 150, sales_data)print("清洗后销售数据:\n", sales_data_clean)