NumPy 基础知识教程:助你轻松上手数据分析
在数据分析、机器学习和科学计算领域,NumPy (Numerical Python) 是一个绕不开的基础库。它提供了高性能的多维数组对象(ndarray
)以及用于处理这些数组的强大工具。毫不夸张地说,NumPy 是 Python 数据科学生态系统的基石,许多其他流行的库(如 Pandas、Scikit-learn、Matplotlib)都建立在 NumPy 之上。
本教程将带你深入了解 NumPy 的核心概念和基本操作,让你能够自信地使用 NumPy 进行数据处理和分析。
1. 为什么选择 NumPy?
在 Python 中,列表(list)也可以用来存储和操作数据,但为什么还要使用 NumPy 呢?主要有以下几个原因:
- 性能: NumPy 的核心是用 C 语言编写的,这使得它在进行数值计算时速度非常快。NumPy 数组在内存中是连续存储的,而 Python 列表中的元素是分散存储的,这使得 NumPy 能够利用现代 CPU 的向量化指令进行并行计算,从而大幅提高运算效率。
- 内存效率: NumPy 数组中的元素类型是统一的,而 Python 列表中的元素可以是任意类型。这使得 NumPy 数组占用的内存空间更小,也更易于管理。
- 功能丰富: NumPy 提供了大量的数学、统计和线性代数函数,可以方便地进行各种数值计算和数据分析操作。
- 易于使用: NumPy 的 API 设计简洁明了,易于学习和使用。
2. 安装 NumPy
如果你使用 Anaconda Python 发行版,那么 NumPy 已经预装好了。如果没有安装,可以使用以下命令通过 pip 安装:
bash
pip install numpy
或者使用 conda 安装:
bash
conda install numpy
安装完成后,可以在 Python 中导入 NumPy 并查看版本:
“`python
import numpy as np
print(np.version)
“`
3. NumPy 数组(ndarray)
NumPy 的核心是 ndarray
(n-dimensional array,多维数组)对象。它是一个由相同类型的元素组成的多维网格。
3.1 创建数组
有多种方法可以创建 NumPy 数组:
- 从列表或元组创建:
“`python
import numpy as np
从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # 输出:[1 2 3 4 5]
从元组创建
arr2 = np.array((6, 7, 8, 9, 10))
print(arr2) # 输出:[ 6 7 8 9 10]
创建二维数组(矩阵)
arr3 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr3)
输出:
[[1 2 3]
[4 5 6]]
“`
- 使用内置函数创建:
“`python
创建全零数组
zeros_arr = np.zeros((3, 4)) # 创建一个3×4的全零数组
print(zeros_arr)
创建全一数组
ones_arr = np.ones((2, 3, 4)) # 创建一个2x3x4的全一数组(三维)
print(ones_arr)
创建单位矩阵
eye_arr = np.eye(5) # 创建一个5×5的单位矩阵
print(eye_arr)
创建指定范围内的数组
range_arr = np.arange(10, 30, 2) # 从10到30(不包括30),步长为2
print(range_arr) # 输出:[10 12 14 16 18 20 22 24 26 28]
创建等差数列
linspace_arr = np.linspace(0, 1, 5) # 从0到1(包括1),生成5个等间距的数
print(linspace_arr) # 输出:[0. 0.25 0.5 0.75 1. ]
创建随机数数组
rand_arr = np.random.rand(2, 3) # 创建一个2×3的随机数数组(0到1之间)
print(rand_arr)
randn_arr = np.random.randn(3, 3) # 创建一个3×3的标准正态分布随机数数组
print(randn_arr)
randint_arr = np.random.randint(1, 100, size=(4,5)) #创建4×5的1到100的随机整数数组
print(randint_arr)
“`
3.2 数组属性
NumPy 数组有一些重要的属性:
ndarray.ndim
: 数组的维度(轴)的数量。ndarray.shape
: 数组的形状,一个表示每个维度大小的元组。ndarray.size
: 数组中元素的总数。ndarray.dtype
: 数组中元素的类型。ndarray.itemsize
: 数组中每个元素占用的字节数。
“`python
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) # 输出:元素类型: int64 (或 int32,取决于系统)
print(“每个元素字节数:”, arr.itemsize) # 输出:每个元素字节数: 8 (或 4)
“`
3.3 数据类型
NumPy 支持多种数据类型,包括:
- 整数:
int8
,int16
,int32
,int64
,uint8
,uint16
,uint32
,uint64
- 浮点数:
float16
,float32
,float64
,float128
- 复数:
complex64
,complex128
,complex256
- 布尔值:
bool
- 字符串:
string_
,unicode_
在创建数组时,可以指定数据类型:
python
arr = np.array([1, 2, 3], dtype=np.float32)
print(arr.dtype) # 输出:float32
也可以使用 astype()
方法转换数据类型:
python
arr = np.array([1, 2, 3])
float_arr = arr.astype(np.float64)
print(float_arr.dtype) # 输出:float64
4. 数组索引和切片
NumPy 数组的索引和切片与 Python 列表类似,但更加强大和灵活。
4.1 一维数组
“`python
arr = np.arange(10)
print(arr) # [0 1 2 3 4 5 6 7 8 9]
获取单个元素
print(arr[3]) # 输出:3
切片
print(arr[2:5]) # 输出:[2 3 4] (从索引2到5,不包括5)
print(arr[:4]) # 输出:[0 1 2 3] (从开头到索引4,不包括4)
print(arr[6:]) # 输出:[6 7 8 9] (从索引6到结尾)
print(arr[::2]) # 输出:[0 2 4 6 8] (从开头到结尾,步长为2)
print(arr[::-1]) # 输出:[9 8 7 6 5 4 3 2 1 0] (反转数组)
使用负数索引
print(arr[-1]) # 输出:9 (最后一个元素)
print(arr[-3:]) # 输出:[7 8 9] (最后三个元素)
“`
4.2 多维数组
“`python
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)
[[1 2 3]
[4 5 6]
[7 8 9]]
获取单个元素
print(arr[1, 2]) # 输出:6 (第2行第3列的元素,索引从0开始)
切片
print(arr[0:2, 1:3]) # 输出:[[2 3] [5 6]] (第1、2行,第2、3列)
print(arr[:, 1]) # 输出:[2 5 8] (所有行的第2列)
print(arr[1, :]) # 输出:[4 5 6] (第2行的所有列)
使用布尔索引
bool_index = arr > 5
print(bool_index)
[[False False False]
[False False True]
[ True True True]]
print(arr[bool_index]) # 输出:[6 7 8 9] (所有大于5的元素)
print(arr[arr > 5]) # 与上一行等价,更简洁的写法
使用整数数组索引
rows = np.array([0, 2])
cols = np.array([1, 2])
print(arr[rows, cols]) # 输出:[2 9] (获取(0,1)和(2,2)位置的元素)
“`
5. 数组运算
NumPy 提供了丰富的数组运算功能,包括基本的算术运算、数学函数、统计函数、线性代数运算等。
5.1 算术运算
NumPy 数组支持标准的加、减、乘、除、幂等运算。这些运算是按元素进行的(element-wise)。
“`python
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
print(a + b) # 输出:[ 6 8 10 12]
print(a – b) # 输出:[-4 -4 -4 -4]
print(a * b) # 输出:[ 5 12 21 32]
print(a / b) # 输出:[0.2 0.33333333 0.42857143 0.5 ]
print(a ** 2) # 输出:[ 1 4 9 16]
数组与标量运算
print(a + 10) # 输出:[11 12 13 14]
print(a * 2) # 输出:[2 4 6 8]
“`
5.2 数学函数
NumPy 提供了许多常用的数学函数,如三角函数、指数函数、对数函数等。这些函数也是按元素进行的。
“`python
arr = np.array([0, np.pi/2, np.pi])
print(np.sin(arr)) # 输出:[0.0000000e+00 1.0000000e+00 1.2246468e-16]
print(np.cos(arr)) # 输出:[ 1.000000e+00 6.123234e-17 -1.000000e+00]
print(np.exp(arr)) # 输出:[ 1. 4.81047738 23.14069263]
print(np.log(arr[1:])) # 输出:[ nan 1.14472989] (注意:log(0) 是未定义的)
“`
5.3 统计函数
NumPy 提供了丰富的统计函数,可以方便地计算数组的均值、方差、标准差、最大值、最小值等。
“`python
arr = np.array([1, 2, 3, 4, 5, 6])
print(np.mean(arr)) # 输出:3.5 (平均值)
print(np.median(arr)) # 输出:3.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.sum(arr)) # 输出:21 (总和)
print(np.prod(arr)) # 输出:720 (乘积)
print(np.cumsum(arr)) # 输出:[ 1 3 6 10 15 21] (累积和)
print(np.cumprod(arr)) # 输出:[ 1 2 6 24 120 720] (累积积)
沿着指定轴计算
arr2 = np.array([[1,2,3], [4,5,6]])
print(np.sum(arr2, axis=0)) # [5 7 9], 沿着列求和(axis=0)
print(np.sum(arr2, axis=1)) # [ 6 15], 沿着行求和(axis=1)
“`
5.4 线性代数运算
NumPy 提供了强大的线性代数运算功能,包括矩阵乘法、转置、求逆、求解线性方程组等。
“`python
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
矩阵乘法
print(np.dot(a, b)) # 或 a @ b
输出:
[[19 22]
[43 50]]
矩阵转置
print(a.T)
输出:
[[1 3]
[2 4]]
矩阵求逆
a_inv = np.linalg.inv(a)
print(a_inv)
输出:
[[-2. 1. ]
[ 1.5 -0.5]]
求解线性方程组 ax = b
b = np.array([5, 11])
x = np.linalg.solve(a, b)
print(x) # 输出:[1. 2.]
计算行列式
print(np.linalg.det(a)) # -2.0
计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(a)
print(“特征值:”, eigenvalues) # 特征值: [-0.37228132 5.37228132]
print(“特征向量:”, eigenvectors)
计算矩阵的秩
print(np.linalg.matrix_rank(a)) # 2
计算奇异值分解
U, S, V = np.linalg.svd(a)
print(“U:”, U)
print(“S:”, S)
print(“V:”, V)
“`
6. 数组形状操作
NumPy 提供了改变数组形状的函数,可以在不改变数据的情况下重新组织数组。
6.1 reshape()
reshape()
方法可以将数组转换为指定的形状。新的形状必须与原数组的元素总数兼容。
“`python
arr = np.arange(12)
print(arr) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
转换为3×4的二维数组
new_arr = arr.reshape(3, 4)
print(new_arr)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
使用 -1 自动计算维度
new_arr2 = arr.reshape(2, -1) # -1 表示该维度的大小由其他维度自动计算
print(new_arr2)
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
“`
6.2 ravel() 和 flatten()
ravel()
和 flatten()
方法都可以将多维数组展平为一维数组。
ravel()
返回一个视图(view),如果修改了视图,原始数组也会被修改。flatten()
返回一个副本(copy),修改副本不会影响原始数组。
“`python
arr = np.array([[1, 2, 3], [4, 5, 6]])
ravel_arr = arr.ravel()
flatten_arr = arr.flatten()
print(ravel_arr) # [1 2 3 4 5 6]
print(flatten_arr) # [1 2 3 4 5 6]
修改 ravel_arr 会影响原始数组
ravel_arr[0] = 10
print(arr)
[[10 2 3]
[ 4 5 6]]
修改 flatten_arr 不会影响原始数组
flatten_arr[0] = 20
print(arr) # 仍然是 [[10 2 3] [ 4 5 6]]
“`
6.3 transpose() 和 swapaxes()
transpose()
方法可以对数组进行转置(交换行和列)。swapaxes()
方法可以交换指定的两个轴。
“`python
arr = np.array([[1, 2, 3], [4, 5, 6]])
转置
transposed_arr = arr.transpose() # 或 arr.T
print(transposed_arr)
[[1 4]
[2 5]
[3 6]]
交换轴
swapped_arr = arr.swapaxes(0, 1)
print(swapped_arr) # 结果与转置相同
[[1 4]
[2 5]
[3 6]]
“`
7. 数组拼接和分割
NumPy 提供了多个函数用于数组的拼接和分割。
7.1 concatenate()
concatenate()
函数可以沿着指定的轴拼接多个数组。
“`python
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
沿着行拼接(axis=0,默认值)
c = np.concatenate((a, b), axis=0)
print(c)
[[1 2]
[3 4]
[5 6]]
沿着列拼接(axis=1)
d = np.concatenate((a, b.T), axis=1) # 注意:b需要转置
print(d)
[[1 2 5]
[3 4 6]]
“`
7.2 stack(), hstack(), vstack()
stack()
:沿着新的轴堆叠数组。hstack()
:水平堆叠数组(沿着列)。vstack()
:垂直堆叠数组(沿着行)。
“`python
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
沿着新的轴堆叠
c = np.stack((a, b))
print(c)
[[1 2 3]
[4 5 6]]
水平堆叠
d = np.hstack((a, b))
print(d) # [1 2 3 4 5 6]
垂直堆叠
e = np.vstack((a, b))
print(e)
[[1 2 3]
[4 5 6]]
“`
7.3 split(), hsplit(), vsplit()
split()
:将数组沿着指定的轴分割成多个子数组。hsplit()
:将数组水平分割(沿着列)。vsplit()
:将数组垂直分割(沿着行)。
“`python
arr = np.arange(12).reshape(3, 4)
沿着行分割
split_arr = np.split(arr, 3, axis=0)
print(split_arr) # 分割成3个子数组
水平分割
hsplit_arr = np.hsplit(arr, 2) # 分割成2个子数组
print(hsplit_arr)
垂直分割
vsplit_arr = np.vsplit(arr, 3) # 分割成3个子数组
print(vsplit_arr)
“`
8. 广播(Broadcasting)
广播是 NumPy 中一种强大的机制,它允许不同形状的数组之间进行算术运算。广播的规则如下:
- 如果两个数组的维度不同,那么维度较小的数组的形状会在其左侧补 1,直到维度相同。
- 如果两个数组在某个维度上的大小相同,或者其中一个数组在该维度上的大小为 1,那么这两个数组在该维度上是兼容的。
- 如果两个数组在所有维度上都是兼容的,那么它们可以一起广播。
- 广播会在缺失或大小为 1 的维度上进行。
“`python
a = np.array([[1, 2, 3], [4, 5, 6]]) # 形状:(2, 3)
b = np.array([10, 20, 30]) # 形状:(3,)
a + b 可以进行广播
c = a + b
print(c)
[[11 22 33]
[14 25 36]]
广播过程:
1. b 的形状变为 (1, 3)
2. b 在第0维上复制,变为 [[10 20 30], [10 20 30]]
3. a 和 b 按元素相加
“`
广播使得代码更简洁、更高效,避免了不必要的循环。
9. 通用函数(ufunc)
NumPy 的通用函数(universal functions,简称 ufunc)是一种对 ndarray
进行逐元素操作的函数。ufunc 有以下特点:
- 逐元素操作: ufunc 会对数组中的每个元素进行相同的操作。
- 向量化: ufunc 是向量化的,这意味着它们可以避免显式的循环,从而提高性能。
- 广播: ufunc 支持广播机制。
NumPy 提供了大量的 ufunc,涵盖了各种数学、统计、逻辑、位运算等操作。前面介绍的许多函数(如 sin()
、cos()
、exp()
、mean()
、std()
等)都是 ufunc。
除了内置的 ufunc,你还可以使用 np.frompyfunc()
创建自定义的 ufunc。
10. 进阶主题
本教程涵盖了 NumPy 的基础知识,但 NumPy 的功能远不止于此。以下是一些进阶主题,供你进一步学习:
- 结构化数组(Structured Arrays): 允许数组中的每个元素包含多个字段,每个字段可以有不同的数据类型。
- 内存映射数组(Memory-Mapped Arrays): 允许你处理非常大的数组,而无需将整个数组加载到内存中。
- 高级索引: 使用花式索引(fancy indexing)和掩码(masking)进行更复杂的数组操作。
- 线性代数: 深入了解 NumPy 的线性代数模块(
numpy.linalg
)。 - 傅里叶变换: 使用 NumPy 的傅里叶变换模块(
numpy.fft
)进行信号处理。 - 随机数生成: 深入了解 NumPy 的随机数模块(
numpy.random
),包括各种概率分布的随机数生成。
结语
NumPy 是 Python 数据分析和科学计算的基石。掌握 NumPy 的基础知识对于高效地进行数据处理和分析至关重要。本教程介绍了 NumPy 的核心概念、基本操作以及一些进阶主题,希望能够帮助你快速入门 NumPy。
要熟练掌握 NumPy,最好的方法是多练习、多实践。建议你结合实际的数据分析项目,不断地使用 NumPy 来解决问题,这样才能更好地理解和掌握 NumPy 的强大功能。