二、NumPy数据处理
1、Numpy基础与数组创建
1.1 Numpy基础知识
Numpy(Numerical Python)是Python编程语言里面用于数值计算的基础库,它提供了高性能的多维数组对象 narray 以及一系列用于操作这类数组的工具函数。NumPy于2005年发布,现如今已成为Python数据科学生态系统的核心组件。
💡重要提示:pandas、scipy、scikit-learn等主流数据处理库都是基于NumPy构建的。掌握NumPy就像掌握了数据分析的「内功心法」,后续学习其他库将事半功倍。
NumPy的核心特性包括:
- 向量化运算:无需编写循环即可对整个数组进行数学运算。
- 广播机制:支持不同形状数组之间的运算。
- 高效内存存储:连续内存块存储,访问速度快。
- 丰富的数学函数库:线性代数、傅里叶变换、随机数生成等。
1.2 ndarray 与 Python列表
对于Python列表与Numpy数组两者之间的关系让大多数初学者有些不解,虽然看着并无差别,但是通过特性回来看确实大不相同。
💡NumPy数组
- 元素类型:必须同质
- 运算方式:向量化运算
- 执行速度:使用C语言作为底层,速度快
- 内存占用:紧凑,连续内存块
- 维度支持:原生N维数组
- 功能丰富度:多维运算、矩阵分解、统计聚合
import numpy as np import time # Python列表:循环求和 py_list = list(range(1000000)) start = time.time() py_sum = sum(py_list) print(f"Python列表求和耗时:{time.time() - start:.4f}秒") # NumPy数组:向量化求和 np_arr = np.arange(1000000) start = time.time() np_sum = np_arr.sum() print(f"NumPy数组求和耗时:{time.time() - start:.4f}秒")1.3 创建数组的方式
# NumPy库 import numpy as np # 方式1:从Python列表转换(最常用) a = np.array([1, 2, 3, 4, 5]) print(f"从列表创建:{a}") # [1 2 3 4 5] # 方式2:全0数组 zeros_1d = np.zeros(5) # 一维:5个元素 zeros_2d = np.zeros((3, 4)) # 二维:3行4列 print(f"全0一维数组:{zeros_1d}") # [0. 0. 0. 0. 0.] # 方式3:全1数组 ones_1d = np.ones(5) ones_2d = np.ones((3, 4)) print(f"全1二维数组:\n{ones_2d}") # [[1. 1. 1. 1.] # [1. 1. 1. 1.] # [1. 1. 1. 1.]] # 方式4:指定填充值的数组 full_arr = np.full((2, 3), 7) # 2行3列,全部填充7 print(f"全7数组:\n{full_arr}") # [[7 7 7] # [7 7 7]] # 方式5:范围数组 arr1 = np.arange(0, 10, 2) # 开始0,结束10,步长2 arr2 = np.linspace(0, 1, 5) # 开始0,结束1,均匀切分5份 print(f"arange创建:{arr1}") # [0 2 4 6 8] print(f"linspace创建:{arr2}") # [0. 0.25 0.5 0.75 1. ] # 方式6:随机数组(最常用) rand_uniform = np.random.rand(3, 4) # 0~1均匀分布 rand_int = np.random.randint(0, 10, (3, 4)) # 0~10整数随机 randn = np.random.randn(3, 4) # 标准正态分布(均值0方差1) print(f"随机整数数组:\n{rand_int}")💡注意事项:np.arange()的区间是左闭右开[start, stop),而np.linspace()是左闭右闭[st
art, stop]。
1.4 数组的关键属性
每个NumPy数组都有以下重要属性,理解它们对于数组的操作可谓是必不可少:
a = np.array([[1, 2, 3], [4, 5, 6]]) print(f"形状 (shape):{a.shape}") # (2, 3) - 2行3列 print(f"维度数 (ndim):{a.ndim}") # 2 - 二维数组 print(f"数据类型 (dtype):{a.dtype}") # int64 - 64位整数 print(f"元素总数 (size):{a.size}") # 6 - 共6个元素 print(f"单个元素字节数 (itemsize):{a.itemsize}") # 8 print(f"数组总字节数 (nbytes):{a.nbytes}") # 481.5 数据类型详解
NumPy支持多种数据类型,合理选择可以节省内存:
| dtype | 说明 | 字节数 | 典型用途 |
| int32 | 32位整数 | 4 | 一般整数运算 |
| int64 | 64位整数 | 8 | 大数值统计 |
| float32 | 单精度浮点 | 4 | 内存敏感场景 |
| float | 双精度浮点 | 8 | 精度要求高 |
| bool | 布尔类型 | 1 | 条件判断 |
| boject | Python对象 | 可变 | 混合类型存储 |
# 指定数据类型 arr1 = np.array([1, 2, 3], dtype=np.float32) arr2 = np.array([1, 2, 3], dtype=np.float64) print(f"float32内存:{arr1.nbytes}字节") # 12 print(f"float64内存:{arr2.nbytes}字节") # 242、数组索引与切片
2.1 一维数组索引基础
NumPy数组的索引方式与 Python 列表类似,但功能强大。索引基础规则:
- 正向索引:从0开始,a[0] 是第一个元素
- 负向索引:从-1开始,a[-1] 是最后一个元素
- 切片:a[start:stop:step],遵循左闭右开原则
a = np.arange(10) # 创建 [0 1 2 3 4 5 6 7 8 9] print(f"原数组:{a}") # 正向索引 print(f"a[0] = {a[0]}") # 0 print(f"a[5] = {a[5]}") # 5 # 负向索引 print(f"a[-1] = {a[-1]}") # 9 print(f"a[-3] = {a[-3]}") # 7 # 切片操作 print(f"a[2:7] = {a[2:7]}") # [2 3 4 5 6] - 第3到第7个元素 print(f"a[:5] = {a[:5]}") # [0 1 2 3 4] - 从头到第5个 print(f"a[5:] = {a[5:]}") # [5 6 7 8 9] - 从第6个到末尾 # 步长切片 print(f"a[::2] = {a[::2]}") # [0 2 4 6 8] - 步长2 print(f"a[::3] = {a[::3]}") # [0 3 6 9] - 步长3 print(f"a[::-1] = {a[::-1]}") # [9 8 7 6 5 4 3 2 1 0] - 完全反转2.2 二维数组索引
二维数组的索引需要同时指定行和列,语法为 array[row, col]:
b = np.array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) print(f"原数组:\n{b}") # 单个元素索引 print(f"b[1, 2] = {b[1, 2]}") # 6 - 第2行第3列 # 行索引 print(f"b[0, :] = {b[0, :]}") # [1 2 3] - 第1行所有列 print(f"b[1] = {b[1]}") # [4 5 6] - 第2行(省略写法) # 列索引 print(f"b[:, 1] = {b[:, 1]}") # [2 5 8] - 第2列所有行 # 子矩阵切片 print(f"b[0:2, 0:2] = \n{b[0:2, 0:2]}") # [[1 2] # [4 5]] # 跳行跳列 print(f"b[::2, ::2] = \n{b[::2, ::2]}") # [[1 3] # [7 9]]2.3 布尔索引
布尔索引是 NumPy 里面最强大的特征之一,它允许我们根据条件筛选数组元素:
a = np.array([10, 20, 30, 40, 50]) print(f"原数组:{a}") # 创建布尔掩码 mask = a > 30 print(f"a > 30 的掩码:{mask}") # [False False False True True] # 使用掩码筛选 result = a[mask] print(f"a[a > 30] = {result}") # [40 50] # 一行写法(最常用) print(f"a[a > 25] = {a[a > 25]}") # [30 40 50]💡核心应用场景:在数据分析中,布尔索引用于数据清洗、异常值检测、条件筛选等场景。
实际应用:
# 场景:筛选价格大于150的商品 prices = np.array([99, 199, 299, 399, 149]) print(f"高价商品价格:{prices[prices > 150]}") # [199 299 399] # 场景:筛选成绩及格的学生 scores = np.array([45, 78, 92, 63, 88, 55, 76]) print(f"及格成绩:{scores[scores >= 60]}") # [78 92 63 88 76] # 复合条件 a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) # 找出大于3且小于8的数 result = a[(a > 3) & (a < 8)] print(f"大于3且小于8:{result}") # [4 5 6 7]💡注意:在NumPy中进行复合条件筛选时,必须使用&(按位与)而不是and(Python逻辑与),因为NumPy操作的是数组而不是单个值。
2.4 花式索引
花式索引(Fancy Indexing)使用整数数组作为索引,可以实现任意顺序、任意位置的元素选取:
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9] # 使用整数数组指定索引位置 indices = [0, 2, 4, 6] print(f"a[{indices}] = {a[indices]}") # [0 2 4 6] # 不按顺序选取 print(f"a[[3, 0, 5]] = {a[[3, 0, 5]]}") # [3 0 5] # 二维花式索引 b = np.arange(12).reshape(3, 4) print(f"原数组:\n{b}") # 选取特定行 print(f"b[[0, 2], :] = \n{b[[0, 2], :]}") # 选取第1行和第3行,因为下标从0开始所以是0和23、数组运算与聚合
3.1 逐元素运算
NumPy的核心优势之一是支持向量化运算,无需循环即可对数组的每个元素进行数学运算:
a = np.array([1, 2, 3, 4, 5]) # 基本算术运算 print(f"a + 10 = {a + 10}") # [11 12 13 14 15] print(f"a - 5 = {a - 5}") # [-4 -3 -2 -1 0] print(f"a * 2 = {a * 2}") # [2 4 6 8 10] print(f"a / 2 = {a / 2}") # [0.5 1. 1.5 2. 2.5] print(f"a ** 2 = {a ** 2}") # [1 4 9 16 25] print(f"a % 2 = {a % 2}") # [1 0 1 0 1] - 取模 # 比较运算(布尔索引) print(f"a > 3 = {a > 3}") # [False False False True True] print(f"a == 3 = {a == 3}") # [False False True False False]💡向量化优势:使用向量化运算比Python循环快10-100倍,因为NumPy在底层使用了SIMD指令集优化。
3.2 数组间运算
两个形状相同的数组可以进行逐元素运算:
a = np.array([1, 2, 3, 4]) b = np.array([10, 20, 30, 40]) print(f"a + b = {a + b}") # [11 22 33 44] print(f"a * b = {a * b}") # [10 40 90 160] print(f"b - a = {b - a}") # [9 18 27 36]3.3 广播机制(Boardcasting)
广播 NumPy 最强大的特征之一,它允许不同形状的数组进行运算:
# 一维数组与二维数组广播 c = np.array([ [1, 2, 3], [4, 5, 6] ]) d = np.array([10, 20, 30]) print(f"c + d = \n{c + d}") # [[11 22 33] # [14 25 36]] # 标量与数组广播 print(f"c * 2 = \n{c * 2}") # [[2 4 6] # [8 10 12]]💡广播规则:当两个数组的形状不同时,NumPy会从后向前比较维度,如果维度相同或其中一个为1,则可以广播。
3.4 聚合函数
NumPy 提供了丰富的聚合函数,用于统计计算:
arr = np.array([1, 2, 3, 4, 5]) # 基本统计 print(f"总和 sum = {arr.sum()}") # 15 print(f"均值 mean = {arr.mean()}") # 3.0 print(f"最大值 max = {arr.max()}") # 5 print(f"最小值 min = {arr.min()}") # 1 print(f"标准差 std = {arr.std():.4f}") # 1.4142 print(f"方差 var = {arr.var():.4f}") # 2.0000 # 累积函数 print(f"累加 cumsum = {arr.cumsum()}") # [1 3 6 10 15] print(f"累乘 cumprod= {arr.cumprod()}") # [1 2 6 24 120]3.5 多维数组聚合(轴向)
对于多维数组,可以通过指定 axis 参数沿特定轴进行聚合:
m = np.arange(12).reshape(3, 4) print(f"原数组:\n{m}") # 按列聚合(axis=0) print(f"每列求和 (axis=0):{m.sum(axis=0)}") #[12 15 18 21] # 按行聚合(axis=1) print(f"每行求和 (axis=1):{m.sum(axis=1)}") # [ 6 22 38] # 其他轴向聚合 print(f"每列最大值:{m.max(axis=0)}") # [ 8 9 10 11] print(f"每行平均值:{m.mean(axis=1)}") # [1.5 5.5 9.5]💡参数提示:axis=1表示行,axis=0表示列