目录
1. NumPy简介
NumPy(Numerical Python)是Python科学计算的基础库,提供了一个强大的N维数组对象和一系列操作这些数组的函数。它是几乎所有高级数据分析工具(如Pandas、SciPy、Scikit-learn等)的基础。
1.1 NumPy的核心优势
-
高性能数组计算:NumPy的数组操作在底层用C语言实现,比Python原生列表快得多
-
丰富的数学函数:提供大量数学函数,支持向量化操作
-
广播功能:不同形状数组之间的运算机制
-
线性代数运算:内置矩阵运算、傅里叶变换等高级功能
-
内存效率:NumPy数组比Python列表占用更少内存
1.2 安装NumPy
使用pip安装NumPy:
pip install numpy
导入NumPy(通常使用np作为别名):
import numpy as np
2. NumPy数组基础
2.1 创建数组
NumPy的核心是ndarray(N-dimensional array,N维数组)对象。
从Python列表创建
# 一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # 输出: [1 2 3 4 5]
# 二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# 输出:
# [[1 2 3]
# [4 5 6]]
使用内置函数创建特殊数组
# 创建全零数组
zeros = np.zeros(5) # 一维5个0
zeros_2d = np.zeros((2, 3)) # 2行3列的全零矩阵
# 创建全1数组
ones = np.ones((3, 2)) # 3行2列的全1矩阵
# 创建单位矩阵
eye = np.eye(3) # 3x3单位矩阵
# 创建未初始化的数组(内容为内存中的随机值)
empty = np.empty((2, 2))
# 创建等差数组
arange = np.arange(0, 10, 2) # 从0开始到10(不含),步长为2
# 输出: [0 2 4 6 8]
# 创建等间隔数组
linspace = np.linspace(0, 1, 5) # 0到1之间等间隔的5个数
# 输出: [0. 0.25 0.5 0.75 1. ]
# 创建随机数组
random_arr = np.random.random((2, 2)) # 2x2的随机数数组,值在[0,1)之间
2.2 数组属性
NumPy数组有许多有用的属性:
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.ndim) # 数组维度:2
print(arr.shape) # 数组形状:(2, 3)
print(arr.size) # 数组元素总数:6
print(arr.dtype) # 数组元素类型:int32或int64(取决于系统)
print(arr.itemsize) # 每个元素占用的字节数:4(对于int32)
print(arr.nbytes) # 数组总字节数:24(6个元素×4字节)
2.3 数据类型
NumPy支持多种数据类型,可以在创建数组时指定:
# 指定数据类型
arr_float = np.array([1, 2, 3], dtype=np.float64)
arr_complex = np.array([1, 2, 3], dtype=np.complex128)
# 常见数据类型:
# np.int8, np.int16, np.int32, np.int64
# np.uint8, np.uint16, np.uint32, np.uint64
# np.float16, np.float32, np.float64
# np.complex64, np.complex128
# np.bool_
# np.object_ (Python对象)
# np.string_ (固定长度的字符串)
# np.unicode_ (固定长度的Unicode)
3. 数组索引与切片
3.1 一维数组索引
一维数组的索引与Python列表类似:
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr[3]) # 第4个元素:3
print(arr[-1]) # 最后一个元素:9
print(arr[2:5]) # 切片:[2, 3, 4]
print(arr[:5]) # 前5个元素:[0, 1, 2, 3, 4]
print(arr[5:]) # 从第6个开始:[5, 6, 7, 8, 9]
print(arr[::2]) # 步长为2:[0, 2, 4, 6, 8]
print(arr[::-1]) # 反转数组:[9, 8, 7, ..., 0]
3.2 多维数组索引
多维数组的索引使用逗号分隔的索引元组:
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 获取单个元素
print(arr[0, 1]) # 第1行第2列:2
# 获取行
print(arr[1]) # 第2行:[4, 5, 6]
print(arr[1, :]) # 同上
# 获取列
print(arr[:, 1]) # 第2列:[2, 5, 8]
# 子数组
print(arr[:2, 1:]) # 前2行,第2列及以后
# 输出:
# [[2 3]
# [5 6]]
3.3 布尔索引
可以使用布尔数组作为索引:
arr = np.array([1, 2, 3, 4, 5])
# 创建布尔条件
mask = arr > 2
print(mask) # [False False True True True]
# 使用布尔数组索引
print(arr[mask]) # [3, 4, 5]
# 等价于
print(arr[arr > 2])
# 更复杂的条件
print(arr[(arr > 1) & (arr < 4)]) # [2, 3]
print(arr[(arr < 2) | (arr > 4)]) # [1, 5]
3.4 花式索引(Fancy Indexing)
使用整数数组进行索引:
arr = np.array([10, 20, 30, 40, 50])
# 使用整数列表索引
indices = [1, 3, 4]
print(arr[indices]) # [20, 40, 50]
# 二维数组示例
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 选择特定行
print(arr2d[[0, 2]]) # 第1行和第3行
# 输出:
# [[1 2 3]
# [7 8 9]]
# 选择特定行和列
print(arr2d[[0, 2], [0, 1]]) # (0,0)和(2,1)位置的元素
# 输出: [1 8]
4. 数组操作
4.1 形状操作
改变数组形状
arr = np.arange(12)
# reshape方法不改变原数组,返回新数组
arr_2d = arr.reshape(3, 4)
print(arr_2d)
# 输出:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# resize方法会改变原数组
arr.resize(4, 3)
print(arr)
# 输出:
# [[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]
# 展平数组
flattened = arr_2d.flatten() # 返回新数组
raveled = arr_2d.ravel() # 返回视图(修改会影响原数组)
转置数组
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.T)
# 输出:
# [[1 4]
# [2 5]
# [3 6]]
4.2 数组连接与分割
连接数组
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
# 垂直堆叠(沿第一个轴)
print(np.vstack((a, b)))
# 输出:
# [[1 2]
# [3 4]
# [5 6]]
# 水平堆叠(沿第二个轴)
c = np.array([[7], [8]])
print(np.hstack((a, c)))
# 输出:
# [[1 2 7]
# [3 4 8]]
# concatenate函数更通用
print(np.concatenate((a, b), axis=0)) # 等同于vstack
print(np.concatenate((a, c), axis=1)) # 等同于hstack
分割数组
arr = np.arange(12).reshape(3, 4)
print(arr)
# 输出:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# 水平分割(沿列分割)
print(np.hsplit(arr, 2)) # 分成2个数组
# 输出:
# [array([[0, 1],
# [4, 5],
# [8, 9]]),
# array([[ 2, 3],
# [ 6, 7],
# [10, 11]])]
# 垂直分割(沿行分割)
print(np.vsplit(arr, 3)) # 分成3个数组
# 输出:
# [array([[0, 1, 2, 3]]),
# array([[4, 5, 6, 7]]),
# array([[ 8, 9, 10, 11]])]
# split函数更通用
print(np.split(arr, 2, axis=1)) # 等同于hsplit
print(np.split(arr, 3, axis=0)) # 等同于vsplit
4.3 数组排序
arr = np.array([3, 1, 4, 2, 5])
# 返回排序后的新数组
print(np.sort(arr)) # [1, 2, 3, 4, 5]
# 原数组不变
print(arr) # [3, 1, 4, 2, 5]
# 原地排序
arr.sort()
print(arr) # [1, 2, 3, 4, 5]
# 获取排序索引
arr = np.array([3, 1, 4, 2, 5])
indices = np.argsort(arr)
print(indices) # [1, 3, 0, 2, 4]
print(arr[indices]) # [1, 2, 3, 4, 5]
# 多维数组排序
arr2d = np.array([[3, 1, 4], [2, 5, 0]])
print(np.sort(arr2d, axis=1)) # 每行排序
# 输出:
# [[1 3 4]
# [0 2 5]]
print(np.sort(arr2d, axis=0)) # 每列排序
# 输出:
# [[2 1 0]
# [3 5 4]]
5. 数组运算
5.1 基本数学运算
NumPy数组支持逐元素运算:
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 加法
print(a + b) # [5, 7, 9]
print(np.add(a, b)) # 同上
# 减法
print(a - b) # [-3, -3, -3]
print(np.subtract(a, b)) # 同上
# 乘法(逐元素相乘,不是矩阵乘法)
print(a * b) # [4, 10, 18]
print(np.multiply(a, b)) # 同上
# 除法
print(b / a) # [4., 2.5, 2.]
print(np.divide(b, a)) # 同上
# 幂运算
print(a ** 2) # [1, 4, 9]
print(np.power(a, 2)) # 同上
# 取模
print(b % a) # [0, 1, 0]
print(np.mod(b, a)) # 同上
5.2 广播机制
广播是NumPy对不同形状数组进行算术运算的方式:
# 标量与数组运算
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr + 10)
# 输出:
# [[11 12 13]
# [14 15 16]]
# 不同形状数组运算
a = np.array([[1, 2, 3]])
b = np.array([[4], [5]])
print(a + b)
# 输出:
# [[5 6 7]
# [6 7 8]]
广播规则:
-
如果两个数组的维度数不同,将形状较小的数组前面补1
-
如果两个数组在某个维度上的长度不同,且其中一个为1,则该维度扩展为另一个的长度
-
如果两个数组在任何维度上的长度都不相同且都不为1,则报错
5.3 矩阵运算
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# 矩阵乘法
print(np.dot(a, b))
# 输出:
# [[19 22]
# [43 50]]
# Python 3.5+可以使用@运算符
print(a @ b) # 同上
# 点积(一维数组)
v = np.array([1, 2, 3])
w = np.array([4, 5, 6])
print(np.dot(v, w)) # 32 (1*4 + 2*5 + 3*6)
# 转置
print(a.T)
# 输出:
# [[1 3]
# [2 4]]
# 逆矩阵
from numpy.linalg import inv
print(inv(a))
# 输出:
# [[-2. 1. ]
# [ 1.5 -0.5]]
# 行列式
from numpy.linalg import det
print(det(a)) # -2.0
# 特征值和特征向量
from numpy.linalg import eig
eigenvalues, eigenvectors = eig(a)
print(eigenvalues) # [-0.37228132 5.37228132]
print(eigenvectors) # [[-0.82456484 -0.41597356]
# [ 0.56576746 -0.90937671]]
5.4 统计运算
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 求和
print(np.sum(arr)) # 21 (所有元素和)
print(np.sum(arr, axis=0)) # [5, 7, 9] (每列的和)
print(np.sum(arr, axis=1)) # [6, 15] (每行的和)
# 平均值
print(np.mean(arr)) # 3.5
print(np.mean(arr, axis=0)) # [2.5, 3.5, 4.5]
# 标准差
print(np.std(arr)) # 1.707825127659933
# 方差
print(np.var(arr)) # 2.9166666666666665
# 最大值/最小值
print(np.max(arr)) # 6
print(np.min(arr)) # 1
# 最大值/最小值的索引
print(np.argmax(arr)) # 5 (扁平化后的索引)
print(np.argmin(arr, axis=0)) # [0, 0, 0] (每列最小值的行索引)
# 中位数
print(np.median(arr)) # 3.5
# 百分位数
print(np.percentile(arr, 50)) # 3.5 (等同于中位数)
print(np.percentile(arr, 25)) # 2.25 (第一四分位数)
# 累积和
print(np.cumsum(arr)) # [ 1 3 6 10 15 21]
6. 高级数组操作
6.1 通用函数(ufunc)
通用函数是对ndarray逐元素操作的函数:
arr = np.array([0, np.pi/2, np.pi])
# 三角函数
print(np.sin(arr))
# 输出: [0.0000000e+00 1.0000000e+00 1.2246468e-16]
# 指数和对数
print(np.exp(arr))
# 输出: [ 1. 4.81047738 23.14069263]
print(np.log(arr + 1))
# 输出: [0. 0.88137359 1.42138568]
# 绝对值
arr2 = np.array([-1, -2, 3])
print(np.abs(arr2)) # [1, 2, 3]
# 比较函数
a = np.array([1, 2, 3])
b = np.array([2, 2, 2])
print(np.maximum(a, b)) # [2, 2, 3]
print(np.minimum(a, b)) # [1, 2, 2]
6.2 向量化函数
将Python函数向量化,使其可以处理数组:
def my_func(x):
return x ** 2 + 2 * x + 1
# 向量化函数
vec_func = np.vectorize(my_func)
arr = np.array([1, 2, 3, 4])
print(vec_func(arr)) # [ 4 9 16 25]
# 更高效的方法是直接使用数组运算
print(my_func(arr)) # 同上,更高效
6.3 条件逻辑
x = np.array([1, 2, 3, 4, 5])
y = np.array([5, 4, 3, 2, 1])
# 逐元素比较
print(x > y) # [False False False True True]
# where函数
print(np.where(x > y, x, y)) # [5, 4, 3, 4, 5]
# 更复杂的条件
condition = (x > 2) & (y < 3)
print(condition) # [False False True True False]
print(np.where(condition, x * y, x + y)) # [6, 6, 9, 8, 6]
6.4 集合操作
a = np.array([1, 2, 3, 2, 4, 1])
b = np.array([3, 4, 5, 6])
# 唯一值
print(np.unique(a)) # [1, 2, 3, 4]
# 交集
print(np.intersect1d(a, b)) # [3, 4]
# 并集
print(np.union1d(a, b)) # [1, 2, 3, 4, 5, 6]
# 差集(在a中但不在b中)
print(np.setdiff1d(a, b)) # [1, 2]
# 对称差集(仅在a或仅在b中)
print(np.setxor1d(a, b)) # [1, 2, 5, 6]
7. 文件输入输出
7.1 文本文件
# 保存数组到文本文件
arr = np.arange(10).reshape(2, 5)
np.savetxt('array.txt', arr, delimiter=',')
# 从文本文件加载数组
loaded_arr = np.loadtxt('array.txt', delimiter=',')
print(loaded_arr)
7.2 二进制文件
# 保存为.npy格式(单个数组)
np.save('array.npy', arr)
# 加载.npy文件
loaded_arr = np.load('array.npy')
print(loaded_arr)
# 保存多个数组为.npz格式
arr2 = np.array([1, 2, 3])
np.savez('arrays.npz', arr1=arr, arr2=arr2)
# 加载.npz文件
loaded = np.load('arrays.npz')
print(loaded['arr1'])
print(loaded['arr2'])
8. 性能优化技巧
8.1 避免不必要的复制
# 视图(不复制数据)
arr = np.arange(10)
view = arr[3:7] # 创建视图
view[0] = 100 # 修改视图会影响原数组
print(arr) # [0, 1, 2, 100, 4, 5, 6, 7, 8, 9]
# 复制数组
copy = arr[3:7].copy()
copy[0] = 0 # 修改副本不会影响原数组
print(arr) # 不变
8.2 使用向量化操作
避免Python循环,使用NumPy内置函数:
# 不推荐:使用Python循环
arr = np.random.rand(1000000)
result = np.empty_like(arr)
for i in range(len(arr)):
result[i] = arr[i] * 2
# 推荐:使用向量化操作
result = arr * 2 # 快得多
8.3 使用NumPy内置函数
# 不推荐:使用Python的sum
sum_result = sum(arr) # 慢
# 推荐:使用NumPy的sum
sum_result = np.sum(arr) # 快
8.4 预分配数组
# 不推荐:动态扩展数组
result = np.array([])
for i in range(100):
result = np.append(result, i) # 每次都会创建新数组
# 推荐:预分配数组
result = np.empty(100)
for i in range(100):
result[i] = i
9. 实际应用示例
9.1 图像处理
NumPy数组可以表示图像:
from PIL import Image
import matplotlib.pyplot as plt
# 加载图像为NumPy数组
img = np.array(Image.open('example.jpg'))
print(img.shape) # (height, width, channels)
# 转换为灰度图像
gray = np.mean(img, axis=2).astype(np.uint8)
# 调整亮度
brightened = np.clip(img * 1.5, 0, 255).astype(np.uint8)
# 显示图像
plt.imshow(brightened)
plt.show()
9.2 数据分析
# 模拟学生成绩数据
scores = np.random.randint(0, 100, size=(100, 5)) # 100名学生,5门课程
# 计算每门课程的平均分
mean_scores = np.mean(scores, axis=0)
# 找出最高分的学生
max_scores = np.max(scores, axis=1)
top_student = np.argmax(max_scores)
# 统计及格人数(>=60)
passed = np.sum(scores >= 60, axis=0)
print(f"各科平均分: {mean_scores}")
print(f"最高分学生索引: {top_student}")
print(f"各科及格人数: {passed}")
9.3 科学计算
解线性方程组:
# 解方程组:
# 3x + y = 9
# x + 2y = 8
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
# 使用numpy.linalg.solve
x = np.linalg.solve(A, b)
print(x) # [2., 3.]
# 验证
print(np.allclose(np.dot(A, x), b)) # True
10. 总结
NumPy是Python科学计算的基础,提供了强大的多维数组对象和各种操作这些数组的函数。
本教程涵盖了:
-
NumPy数组的创建和基本属性
-
数组索引和切片的各种方法
-
数组的形状操作和连接分割
-
基本的数学运算和统计运算
-
高级操作如广播、通用函数、向量化等
-
文件I/O和性能优化技巧
-
实际应用示例
掌握NumPy是学习Python数据分析和科学计算的重要一步。
通过实践这些操作,能够高效地处理数值数据,为更高级的数据分析、机器学习等任务打下坚实基础。