一、NumPy
NumPy:Numerical Python,第三方数值计算库,底层C实现,提供ndarray多维数组,是pandas、matplotlib底层基础。
导入约定:
import numpy as np
1. 核心对象:ndarray N维数组
ndarray:n‑dimensional array,多维同类型数组。
和Python原生list关键区别
| list | np.ndarray |
| 可以存放不同类型元素 | 全部元素必须相同数据类型 |
| 内存分散,链表式存储 | 连续一块内存存储,计算速度快 |
| 循环Python层面,慢 | 向量化运算,C底层,速度快 |
| 动态变长 | 创建时维度固定 |
lst = [1,2,3,4]
arr = np.array([1,2,3,4])
print(type(arr)) # numpy.ndarray
ndarray重要属性
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
print(arr.itemsize) # 单个元素字节大小
- shape 是元组:一维(N,),二维(rows,cols),三维(z,y,x)。一维数组shape是(4,),不是(4,1);一维不是列向量也不是行向量。
2. 创建数组常用方法
# 1.从list转换
a1 = np.array([1,2,3])
a2 = np.array([[1,2],[3,4]])
# 指定dtype
a = np.array([1,2], dtype=np.float32)
# 2.全0、全1
np.zeros((2,3)) # 2行3列全0
np.ones((3,2)) # 3行2列全1
# 3.指定值填充
np.full((2,2), 10)
# 4.单位矩阵
np.eye(3)
# 5.等差序列 arange (类似range,支持浮点数)
np.arange(0,10,2) # [0 2 4 6 8]
# 6.等分linspace:在start‑end之间取N个点,包含首尾
np.linspace(0, 10, 5) # [ 0. 2.5 5. 7.5 10. ]
# 7.随机数组
np.random.rand(2,3) # [0,1)均匀分布
np.random.randn(3,3) # 标准正态分布
np.random.randint(0,10, size=(2,2)) # 整数随机
数据类型 dtype,常用:int8/int16/int32/int64;float32/float64;bool
arr = np.array([1,2], dtype=np.float64)
arr2 = arr.astype(np.int32) # 类型转换,返回新数组,不修改原数组
坑:astype()不会原地修改,必须接收返回值。
3.索引与切片
(1)索引与切片
一维
arr = np.array([0,1,2,3,4])
print(arr[2])
print(arr[1:4])
二维
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
arr2d[0,1] # 第0行第1列,等价 arr2d[0][1],推荐逗号写法
arr2d[1:3, :] # 行切片1‑2,所有列
arr2d[:, 0] # 所有行,第0列
和list最大坑:numpy切片返回视图view,不是拷贝!共享内存,修改会影响原数组。
a = np.array([1,2,3,4])
b = a[1:3]
b[0] = 99
print(a) # [ 1 99 3 4] 原数组被改动!
list切片是拷贝;numpy切片默认是视图view,共用同一块内存,只为性能。
想要独立拷贝:显式调用 .copy()
b = a[1:3].copy()
b[0]=99
# a不受影响
布尔索引、花式索引:返回 拷贝(新数组,独立内存),修改结果不会影响原数组。
(2)布尔索引
arr[布尔掩码数组]
# 二维:可以分别给行、列传布尔数组
arr[行布尔掩码, 列布尔掩码]
用布尔数组当掩码,取出条件为True对应的元素。
arr = np.array([10, 20, 30, 40, 50])
mask = arr > 25 # 得到布尔掩码数组
print(mask) # [False False True True True]
res = arr[mask]
print(res) # [30 40 50]
多条件组合:不能用and / or / not,要用运算符 & | ~,每个条件必须加括号
# 大于20 并且 小于50
res = arr[(arr > 20) & (arr < 50)]
print(res) # [30 40]
# 取反 ~
res = arr[~(arr>25)]
print(res) # [10 20]
二维数组布尔索引
arr2d = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
mask = arr2d > 4
res = arr2d[mask]
print(res) # [5 6 7 8 9]
# 布尔索引取出结果永远是一维数组!
(3)花式索引
传入整数数组 / 整数列表作为索引,按指定下标取出元素。
一维花式索引
arr[整数序列]
arr = np.array([10,20,30,40,50])
# 传入下标列表
res = arr[[0, 2, 4]]
print(res) # [10 30 50]
# 顺序可以乱序、可以重复取
res = arr[[4,1,1]]
print(res) # [50 20 20]
二维花式索引
两种写法:
arr[行下标数组, 列下标数组]:一一配对取点
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 取 (0,1) 和 (2,0) 两个点
res = arr2d[[0, 2], [1, 0]]
print(res) # [2 7]
两边整数数组长度最好相等;如果不等,会触发广播机制
一边花式索引,另一边切片 arr[整数序列, :] / arr[:, 整数序列]
选取若干整行:传入行列表
# 取第0行、第2行
res = arr2d[[0,2], :]
print(res)
'''
[[1 2 3]
[7 8 9]]
'''
选取若干整列
res = arr2d[:, [0,2]]
print(res)
'''
[[1 3]
[4 6]
[7 9]]
'''
4. 向量化运算
不写 Python for 循环,直接对整个 ndarray 数组做运算符 / 函数运算;运算在底层 C 实现,避开 Python 循环的开销。
Python 原生 for 循环:每一次迭代都在 Python 虚拟机执行,循环量大很慢。
NumPy 向量化:循环下移到 C 层,数组内存连续,CPU 可以批量处理,速度提升几十~上百倍。
核心思想:把操作施加到数组每一个元素,不用手动遍历。
import numpy as np
arr = np.array([1,2,3,4])
# 向量化,没有for循环
res = arr * 2
print(res) # [2 4 6 8]
(1)运算符向量化(逐元素 element‑wise)
+ - * / // % ** > < >= <= == & | 全部逐元素运算。
*:逐元素相乘,不是矩阵乘法。矩阵乘法用 @ / np.dot()。
a = np.array([1,2,3])
b = np.array([4,5,6])
print(a + b) # [5 7 9]
print(a - b) # [-3 -3 -3]
print(a * b) # [ 4 10 18] 逐元素乘
print(a / b)
print(a // b)
print(a ** 2) # 每个元素平方 [1 4 9]
print(a > 2) # [False False True] 返回布尔数组
print(a == 2) # [False True False]
二维同样逐元素:
m1 = np.array([[1,2],[3,4]])
m2 = np.array([[10,20],[30,40]])
print(m1 + m2)
print(m1 * m2) # 逐元素,不是矩阵乘法
(2)广播机制:不同shape数组可以运算,numpy自动扩展维度,不复制数据。
规则:
1. 从尾部维度对齐
2. 维度相等,或者其中一个为1,可以广播
3. 否则报错
一维数组 + 标量
arr = np.array([1,2,3])
arr + 10
二维 (2,3) + 一维 (3,)
m = np.ones((2,3))
v = np.array([10,20,30])
print(m + v)
[[11. 21. 31.]
[11. 21. 31.]]
(2,1) + (1,3),广播结果 (2,3)
a = np.array([[1],[2]]) # shape(2,1)
b = np.array([[10,20,30]]) # shape(1,3)
print(a + b)
[[11 21 31]
[12 22 32]]
广播报错例子:(2,3) 和 (2,4),尾部维度 3 vs4,都不为 1,无法广播。
(3)矩阵乘法
| 写法 | 运算类型 | 说明 | 维度规则 |
|---|---|---|---|
A * B | Hadamard 积(逐元素相乘) | 对应位置元素相乘,不是数学矩阵乘法 | 支持广播,可以维度不同,只要可广播 |
A @ B | 矩阵乘法(推荐) | 等价np.matmul,实现标准数学矩阵乘法 | A(m,k)@B(k,n) |
np.matmul(A,B) | 矩阵乘法 | 和@完全等价 | 同上 |
np.dot(A,B) | 点积 | 二维数组等价矩阵乘法;一维做向量内积;高维行为特殊,不推荐多维矩阵使用 | 一维向量长度相同;二维满足矩阵乘法规则 |
@ / np.matmul() 标准矩阵乘法
import numpy as np
A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])
print("==== A * B 逐元素 ====")
print(A * B)
'''
[[ 5 12]
[21 32]]
'''
print("==== A @ B 矩阵乘法 ====")
print(A @ B)
'''
[[19 22]
[43 50]]
'''
np.dot() 的坑
二维数组:np.dot(A,B)等价A@B
一维数组:np.dot(v1,v2)向量内积(标量)
v1 = np.array([1,2])
v2 = np.array([3,4])
print(np.dot(v1,v2)) # 1*3 +2*4 =11,输出标量
大于 2 维数组:np.dot 和 @行为不一致,工程中禁止用 dot 做多维矩阵运算。
一维数组参与矩阵乘法特殊行为
import numpy as np
M = np.array([[1,2],[3,4]]) # (2,2)
v = np.array([10,20]) # (2,) 一维数组
v_col = v.reshape(-1,1) # (2,1) 二维列向量
res1 = M @ v
res2 = M @ v_col
print("res1 =", res1)
print("res1.shape =", res1.shape)
print("res2 =")
print(res2)
print("res2.shape =", res2.shape)
res1 = [ 50 110 ]
res1.shape = (2,)
res2 =
[[ 50]
[110]]
res2.shape = (2, 1)
(4)数组形状操作 reshape /ravel/flatten
arr = [0 1 2 3 4 5]
1. reshape() 修改形状
数组.reshape(dim0, dim1, dim2...)
reshape前后总元素必须完全相等,否则报错。
优先返回视图(共享内存),不复制数据,但不保证一定是视图;
-1:交给numpy自动计算该维度的大小。
reshape(2,3)
arr2 = arr.reshape(2,3)
print(arr2)
'''
[[0 1 2]
[3 4 5]]
'''
print(arr2.shape) # (2, 3)
6个元素 = 2行 ×3列。
reshape(-1,2),固定列数=2,-1自动计算行数
arr3 = arr.reshape(-1,2)
print(arr3)
'''
[[0 1]
[2 3]
[4 5]]
'''
print(arr3.shape) # (3, 2)
- reshape(-1, 1) → 列向量 (N,1)
- reshape(1, -1) → 行向量 (1,N)
reshape是视图不绝对:如果内存布局不连续,reshape会返回拷贝,修改新数组不一定影响原数组。
2. ravel() 展平为一维
将多维数组拉平成一维;优先返回视图,内存不允许时才做拷贝。
arr2d = np.array([[1,2],[3,4]])
r = arr2d.ravel()
print(r) # [1 2 3 4]
print(r.shape) # (4,)
# ravel是视图,修改会影响原数组
r[0] = 999
print(arr2d)
'''
[[999 2]
[ 3 4]]
'''
np.ravel(arr2d) 和 arr2d.ravel()效果一样。
3. flatten() 展平,永远返回拷贝
不管什么情况,一定复制一份全新数据,和原数组完全隔离。
arr2d = np.array([[1,2],[3,4]])
f = arr2d.flatten()
print(f) # [1 2 3 4]
f[0] = 999
print(arr2d)
'''
[[1 2]
[3 4]]
'''
3. .T 转置
交换数组的各个维度顺序,二维就是行↔列互换。
m = np.arange(6).reshape(2,3)
print(m)
'''
[[0 1 2]
[3 4 5]]
'''
print(m.shape) # (2, 3)
mt = m.T
print(mt)
'''
[[0 3]
[1 4]
[2 5]]
'''
print(mt.shape) # (3, 2)
二维矩阵转置常用.T;高维数组推荐np.transpose()。
5. np.newaxis 增加维度(广播高频)
作用:,不改变数据,等价reshape。
v = np.array([10,20])
print(v) # [10 20]
print(v.shape) # (2,)
v[:, np.newaxis]:增加列维度|(N,1)|构造列向量,用于广播
- : 代表保留原来的0轴(2个元素)
- np.newaxis 在后面插入新维度
v_col = v[:, np.newaxis]
print(v_col)
'''
[[10]
[20]]
'''
print(v_col.shape) # (2, 1)
等价写法:v.reshape(-1,1)
v[np.newaxis, :]:增加行维度|(1,N)|构造行向量
- 在最前面插入长度为1的维度
- : 保留原来全部元素
v_row = v[np.newaxis, :]
print(v_row)
# [[10 20]]
print(v_row.shape) # (1, 2)
等价写法:v.reshape(1,-1)
为什么要用newaxis?之前广播例子:(2,1)+(1,3),一维(2,)无法直接参与该广播,通过np.newaxis增加维度,造出(2,1)列向量,开启广播机制。
(5)np.arange
np.arange(start, stop, step, dtype=None)
start:起始值,默认0,包含该值
stop:终止值,不包含(左闭右开 [start, stop))
step:步长,默认1,可以正数、负数、浮点数
dtype:指定数据类型,不写自动推断
只给 stop,start 默认 0,step 默认 1
import numpy as np
arr = np.arange(10)
print(arr)
# [0 1 2 3 4 5 6 7 8 9]
# 0~9,取不到10
start, stop
arr = np.arange(2,10)
print(arr)
# [2 3 4 5 6 7 8 9]
start, stop, step 步长
arr = np.arange(2,10,2)
print(arr)
# [2 4 6 8]
负步长,倒序
arr = np.arange(5,0,-1)
print(arr)
# [5 4 3 2 1]
浮点数步长
arr = np.arange(0, 1, 0.2)
print(arr)
# [0. 0.2 0.4 0.6 0.8]
配合 reshape,生成多维数组,arange永远输出一维,再 reshape 改变形状
arr = np.arange(6).reshape(2,3)
print(arr)
'''
[[0 1 2]
[3 4 5]]
'''
| range | np.arange | |
|---|---|---|
| 返回 | range 迭代器 | ndarray 数组 |
| 支持浮点数 | 不支持 | 支持 |
| 可以直接做向量化运算 | 不支持 | 支持 |
易错坑
1. reshape总元素数量必须一致,否则抛异常;
2. ravel是视图,修改会改动原始数组;flatten拷贝不会;
3. (2,)一维数组,和`(2,1)`列向量打印数字相似,但shape不同,广播、矩阵乘法行为不一样;
4. -1只能在reshape中出现最多一次,不能写reshape(-1,-1),numpy无法推算两个未知数。
5.NumPy 通用函数 ufunc
ufunc:专门做向量化的函数,输入数组,逐元素计算,输出新数组。替代 Python 内置 math 函数,math 只支持单个数字;np 的 ufunc 支持数组。
数学类 ufunc
np.sin(arr)
np.cos(arr)
np.exp(arr) # e^x
np.log(arr) # 自然对数 ln
np.log10(arr)
np.sqrt(arr) # 开平方
np.abs(arr) # 绝对值
np.square(arr) # 平方
示例对比
import math
# math.sin 只能传单个数字
# math.sin(np.array([1,2,3])) 报错
arr = np.array([0, np.pi/2, np.pi])
res = np.sin(arr) # ufunc向量化,直接算全部元素
print(res)
条件 ufunc:np.where
np.where(condition, x, y)
逻辑:条件为 True 取 x,False 取 y,逐元素。
arr = np.array([1, -2, 3, -4])
res = np.where(arr>0, arr, 0)
print(res) # [1 0 3 0]
也可以只传条件:返回满足条件的下标元组,经常配合布尔索引。
常用统计函数
全部默认对整个数组计算,可以指定`axis`轴。
- axis=0:沿着行方向压缩,按列计算
- axis=1:沿着列方向压缩,按行计算`
arr = np.array([[1,2],[3,4]])
arr.sum()
arr.sum(axis=0) # 列求和
arr.sum(axis=1) # 行求和
arr.mean()
arr.max()
arr.min()
arr.argmax() #最大值下标
np.random
import numpy as np
(1)随机种子 seed
作用:固定随机数序列,保证每次运行代码得到完全一样的随机结果,用于复现实验。
np.random.seed(42) # 传入任意整数作为种子
关键点:seed()只需要设置一次;不是每生成一次随机数都调用。
不设置 seed,每次运行随机结果不一样。
(2)生成均匀分布随机数
np.random.rand(d0,d1,d2...)
生成 [0,1) 区间均匀分布浮点数,参数直接传各个维度大小。
# shape(2,3),0~1之间
arr = np.random.rand(2,3)
print(arr.shape) # (2, 3)
np.random.uniform(low, high, size),指定区间的均匀分布,[low, high)
# 生成5个数字,范围 [1,10)
arr = np.random.uniform(low=1, high=10, size=5)
# 2行2列
arr2 = np.random.uniform(0,100,size=(2,2))
(3)正态(高斯)分布
标准正态分布:均值 0,方差 1 N(0,1)
np.random.randn(d0,d1...)
arr = np.random.randn(2,3)
自定义正态分布
loc:均值 μ
scale:标准差 σ
np.random.normal(loc, scale, size)
# 均值=10,标准差=2,shape(3,)
arr = np.random.normal(loc=10, scale=2, size=3)
(4)随机整数
np.random.randint(low, high=None, size)
生成整数,区间 [low, high),左闭右开
# high不写:区间 [0, low)
arr1 = np.random.randint(0,10,size=5) # [0,10) 5个整数
# 二维
arr2 = np.random.randint(1,100,size=(2,3))
(5)随机抽样、打乱
从给定序列中随机采样
np.random.choice(a, size, replace=True, p=None)
a:数据源(整数 / 数组)
size:输出形状
replace=True:有放回抽样;False无放回,不能抽比样本总数更多的数
p:每个元素被抽取的概率,和 a 长度相同
# 从[10,20,30,40]中抽3个,有放回
res1 = np.random.choice([10,20,30,40], size=3)
# 无放回抽样
res2 = np.random.choice([10,20,30,40], size=3, replace=False)
# 指定概率:10取到概率0.8,20为0.2
res3 = np.random.choice([10,20], size=5, p=[0.8,0.2])
np.random.shuffle(arr):原地打乱数组顺序,无返回值,只打乱第一维。
arr = np.array([1,2,3,4,5])
np.random.shuffle(arr)
print(arr) #原数组被打乱
np.random.permutation(arr):返回打乱后的新拷贝,不修改原数组。
arr = np.array([1,2,3,4,5])
res = np.random.permutation(arr)
print(arr) #原数组不变
shuffle 原地修改;permutation 返回新数组。
6.拼接与分割
核心函数:底层主函数 np.concatenate;vstack/hstack 是便捷封装;分割:np.split / np.vsplit / np.hsplit / np.array_split
import numpy as np
a = np.array([[1, 2],
[3, 4]])
b = np.array([[5, 6]]) # shape (1,2)
c = np.array([[7],
[8]]) # shape (2,1)
(1)np.concatenate
np.concatenate( (数组1,数组2,...), axis=0 )
axis=0:竖直方向拼接,增加行,要求除 0 轴以外其余维度完全匹配
axis=1:水平方向拼接,增加列,要求除 1 轴以外其余维度完全匹配
# axis=0 竖直拼接,增加行
res0 = np.concatenate((a, b), axis=0)
print(res0)
'''
[[1 2]
[3 4]
[5 6]]
'''
# axis=1 水平拼接,增加列
res1 = np.concatenate((a, c), axis=1)
print(res1)
'''
[[1 2 7]
[3 4 8]]
'''
拼接时,非拼接维度的 shape 必须完全一致,否则报错。例:a(2,2) 和 b(1,3),axis=0拼接,第二维 2≠3,直接报错。
(2)np.vstack () 竖直堆叠
v → vertical 竖直,增加行
res = np.vstack([a, b])
# 等价 np.concatenate((a,b), axis=0)
np.hstack () 水平堆叠
h → horizontal 水平,增加列
res = np.hstack([a, c])
# 等价 np.concatenate((a,c), axis=1)
小区别:
concatenate 必须指定 axis;
vstack/hstack 自动固定 axis,可读性更好,内部调用 concatenate。
(3)分割
np.split
np.split(arr, 分割份数 / 分割位置列表, axis=0)
axis=0:按行切(竖直分割)
axis=1:按列切(水平分割)
np.split必须等分,总长度必须能被份数整除,否则抛异常。
arr = np.arange(12).reshape(3,4)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
'''
# axis=0,沿行切成3份
parts = np.split(arr, 3, axis=0)
print(parts[0]) # [[0 1 2 3]]
# 按指定位置切,第二个参数传列表
parts2 = np.split(arr, [1], axis=1)
# 在索引1位置切开,分成 [:1] 和 [1:]
np.vsplit/np.hsplit 快捷分割
np.vsplit(arr, n):等价 np.split(arr, n, axis=0),按行切
np.hsplit(arr, n):等价 np.split(arr, n, axis=1),按列切
# 按行切3份
p1 = np.vsplit(arr, 3)
# 按列切2份
p2 = np.hsplit(arr, 2)
np.array_split
np.split要求严格等分;np.array_split可以不等分,不会报错。当维度长度不能整除分割份数时,部分子数组会多一个元素。
arr = np.arange(5)
res = np.array_split(arr, 2)
print(res) # [array([0,1,2]), array([3,4])]
| 函数 | 作用 | axis | 特点 |
|---|---|---|---|
np.concatenate((a,b),axis=0) | 拼接,增加行 | 0 | 底层函数,非拼接维度 shape 必须匹配 |
np.concatenate((a,b),axis=1) | 拼接,增加列 | 1 | 底层函数 |
np.vstack([a,b]) | 竖直堆叠,增加行 | 固定 0 | concatenate 封装 |
np.hstack([a,b]) | 水平堆叠,增加列 | 固定 1 | concatenate 封装 |
np.split(arr,N,axis=0) | 分割数组 | 0/1 | 必须等分,否则报错 |
np.vsplit | 按行分割 | 固定 0 | split 封装 |
np.hsplit | 按列分割 | 固定 1 | split 封装 |
np.array_split | 分割数组 | 0/1 | 支持不等分 |
二、Pandas
Pandas 基于 NumPy,两大核心:Series 一维带索引序列、DataFrame 二维表格(Excel 表结构)
1. Series 一维序列
结构:索引index + 值values;可以自定义标签索引,不只是 0,1,2 数字。
(1)创建 Series
列表创建,默认数字索引0,1,2,3
s1 = pd.Series([10,20,30,40])
print("s1:\n",s1)
'''
0 10
1 20
2 30
3 40
dtype: int64
'''
指定自定义索引
s2 = pd.Series([10,20,30,40], index=["a","b","c","d"])
print("\ns2:\n",s2)
'''
a 10
b 20
c 30
d 40
dtype: int64
'''
字典创建,字典key直接成为index
dic = {"a":100, "b":200, "c":300}
s3 = pd.Series(dic)
print("\ns3:\n",s3)
'''
a 100
b 200
c 300
dtype: int64
'''
(2)Series 属性
print(s2.index) #索引对象 Index(['a', 'b', 'c', 'd'], dtype='object')
print(s2.values) #取出数值,返回numpy数组 [10 20 30 40]
print(s2.dtype) #数据类型 int64
print(s2.shape) #形状 (4,)
(3)Series 取值
print(s2["a"]) #标签索引取值 10
print(s2[0]) #位置索引取值 10
#切片:标签切片闭区间,首尾都取
print(s2["a":"c"])
'''
a 10
b 20
c 30
dtype: int64
'''
(4)Series 运算
支持向量化运算,和 numpy 类似,不需要循环
print(s2 * 2)
'''
a 20
b 40
c 60
d 80
dtype: int64
'''
2. DataFrame 二维表格
类比 Excel 表格:行 index,列 columns;DataFrame 的每一列,就是一个 Series。
(1)创建 DataFrame
#方式1:字典创建,字典key作为列名
data = {
"name":["张三","李四","王五"],
"age":[18,20,22],
"score":[88,92,79]
}
df = pd.DataFrame(data)
print("df:\n",df)
'''
name age score
0 张三 18 88
1 李四 20 92
2 王五 22 79
'''
#方式2:numpy二维数组创建,手动指定列名、行索引
arr = np.array([[18,88],[20,92],[22,79]])
df2 = pd.DataFrame(arr, columns=["age","score"], index=[10,20,30])
print("\ndf2:\n",df2)
'''
age score
10 18 88
20 20 92
30 22 79
'''
(2)DataFrame 常用属性与查看数据
print(df.index) #行索引 RangeIndex(start=0, stop=3, step=1)
print(df.columns) #列索引 Index(['name', 'age', 'score'], dtype='object')
print(df.values) #转为numpy二维数组
print(df.shape) #(3, 3) (行数,列数)
print(df.dtypes) #每一列的数据类型
print(df.head(2)) #查看前2行;不传参数默认前5行
print(df.tail(1)) #查看最后1行
print(df.info()) #表格概览:行数、列名、非空值数量、数据类型
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 name 3 non-null object
1 age 3 non-null int64
2 score 3 non-null int64
dtypes: int64(2), object(1)
memory usage: 200.0+ bytes
'''
print(df.describe()) #只对数值列做统计:count mean std min max四分位数
'''
age score
count 3.000000 3.000000
mean 20.000000 86.333333
std 2.000000 6.506407
min 18.000000 79.000000
25% 19.000000 83.500000
50% 20.000000 88.000000
75% 21.000000 90.000000
max 22.000000 92.000000
'''
3. 索引选取数据
(1)取列
#取单列,返回Series
col_series = df["name"]
print(col_series)
'''
0 张三
1 李四
2 王五
Name: name, dtype: object
'''
#取多列,传入列表,返回DataFrame
col_df = df[["name","age"]]
print(col_df)
'''
name age
0 张三 18
1 李四 20
2 王五 22
'''
坑:df[]直接写,只能取列,不能直接写数字取行;取行用loc、iloc。
(2)loc:按标签索引(行标签、列标签)
df.loc[行标签, 列标签],按标签(index 名字)取值,闭区间,两边都包含 [start, end]
#取单行,行标签0
print(df.loc[0])
'''
name 张三
age 18
score 88
Name: 0, dtype: object
'''
#取多行
print(df.loc[[0,2]])
'''
name age score
0 张三 18 88
2 王五 22 79
'''
#行+列同时指定
print(df.loc[0, "name"]) #张三
#切片,loc[0:2],标签0、1、2全部取出,两边都包含
print(df.loc[0:2, ["name","age"]])
'''
name age
0 张三 18
1 李四 20
2 王五 22
'''
(3)iloc
语法:df.iloc[行位置, 列位置],按数字位置(0 开始的下标)取值,左闭右开 [start, end)
print(df.iloc[0]) #第0行
print(df.iloc[0:2, 0:2]) #行0,1;列0,1,左闭右开
'''
name age
0 张三 18
1 李四 20
'''
(4)布尔索引
#条件,返回布尔掩码
mask = df["age"] >19
print(mask)
'''
0 False
1 True
2 True
Name: age, dtype: bool
'''
#掩码筛选,保留True对应的行
res = df[mask]
print(res)
'''
name age score
1 李四 20 92
2 王五 22 79
'''
#多条件:&代表且,|代表或;每个条件必须加括号!
res2 = df[ (df["age"]>18) & (df["score"]>80) ]
print(res2)
'''
name age score
1 李四 20 92
'''
(5)列操作:新增、修改、删除
#新增一列
df["gender"] = ["男","男","女"]
print(df)
'''
name age score gender
0 张三 18 88 男
1 李四 20 92 男
2 王五 22 79 女
'''
#修改整列
df["age"] = df["age"] + 1
print(df["age"])
# 0 19
# 1 21
# 2 23
#删除列
# axis=1代表列;inplace=True原地修改原df,不生成新对象
df.drop("gender", axis=1, inplace=True)
print(df.columns) #Index(['name', 'age', 'score'], dtype='object')
(5)文件读写
#读写csv文件
df.to_csv("test.csv", index=False) #index=False:不把行索引写入文件
df_read = pd.read_csv("test.csv")
print(df_read)
#读写excel(需要pip install openpyxl)
df.to_excel("test.xlsx", index=False)
df_read_excel = pd.read_excel("test.xlsx")
(6)缺失值处理
#构造带缺失的DataFrame
df_miss = pd.DataFrame({
"a":[1,np.nan,3],
"b":[np.nan,5,6]
})
print(df_miss)
'''
a b
0 1.0 NaN
1 NaN 5.0
2 3.0 6.0
'''
print(df_miss.isna()) #判断每个位置是否缺失,返回布尔表格
print(df_miss.isna().sum()) #统计每一列缺失值的数量
df_drop = df_miss.dropna() #删除存在缺失的行,返回新df
print(df_drop)
df_fill = df_miss.fillna(0) #缺失值填充为0,返回新df
print(df_fill)
'''
a b
0 1.0 0.0
1 0.0 5.0
2 3.0 6.0
'''
(7)统计运算
print(df["age"].sum()) #求和
print(df["age"].mean()) #平均值
print(df["age"].max()) #最大值
print(df["age"].min()) #最小值
print(df["score"].value_counts()) #统计该列每个值出现次数
4.排序聚合
(1) sort_values:按值排序
df.sort_values(by="列名", ascending=True, inplace=False)
by:指定按哪一列 / 多列排序
ascending=True:升序从小到大;False降序从大到小
inplace=False:默认返回新 DataFrame,不修改原数据;True原地修改
# 按score分数升序
df1 = df.sort_values(by="score")
print("按score升序:\n",df1)
'''
name class age score
2 王五 2班 19 79
3 赵六 2班 22 85
0 张三 1班 18 88
4 钱七 1班 21 90
1 李四 1班 20 92
'''
# 多列排序:先按班级,班级相同再按分数降序
df3 = df.sort_values(by=["class","score"], ascending=[True,False])
print("\n多列排序:\n",df3)
'''
name class age score
1 李四 1班 20 92
4 钱七 1班 21 90
0 张三 1班 18 88
3 赵六 2班 22 85
2 王五 2班 19 79
'''
(2)sort_index:按行索引排序
# 先打乱顺序
df_shuffle = df.sample(frac=1, random_state=42)
print("打乱后的df:\n",df_shuffle)
# 恢复索引顺序
df_sort_idx = df_shuffle.sort_index()
print("\nsort_index恢复索引:\n",df_sort_idx)
(3)groupby 分组聚合
groupby 逻辑:拆分 → 应用函数 → 合并结果 按某一列相同的值分组,然后对每组做统计计算。
基础用法
# 按照class班级分组
group_obj = df.groupby("class")
print(group_obj)
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x......>
# groupby返回分组对象,直接print看不到数据,需要调用聚合函数
常用聚合函数
# 对全部数值列做聚合
res = group_obj.mean()
print("按班级分组求均值:\n",res)
'''
age score
class
1班 19.666667 90.000000
2班 20.500000 82.000000
'''
# 其它聚合
print("\n求和:\n", group_obj.sum())
print("\n最大值:\n", group_obj.max())
print("\n计数:\n", group_obj.count())
只对指定一列聚合
#分组后,只取score列,计算平均分
res_score = df.groupby("class")["score"].mean()
print("\n各班平均分:\n",res_score)
'''
class
1班 90.0
2班 82.0
Name: score, dtype: float64
'''
agg ():一次执行多个不同聚合操作,可以同时指定多个统计方式
#对score列,同时计算:均值、最大值、最小值
res_agg = df.groupby("class")["score"].agg(["mean","max","min"])
print("\nagg多聚合:\n",res_agg)
'''
mean max min
class
1班 90.000000 92 88
2班 82.000000 85 79
'''
agg 给列起自定义名字
res_rename = df.groupby("class").agg(
平均分=("score","mean"),
最高分=("score","max"),
人数=("name","count")
)
print("\n自定义聚合列名:\n",res_rename)
'''
平均分 最高分 人数
class
1班 90.000000 92 3
2班 82.000000 85 2
'''
多列分组:按多个字段分组
先按班级,再按其他字段
# 示例:如果有gender性别列,df.groupby(["class","gender"])
groupby 重置索引 reset_index ()
groupby 之后分组字段会变成行索引;reset_index()把它变回普通列。
res = df.groupby("class")["score"].mean()
print(res)
'''
class
1班 90.0
2班 82.0
'''
res = df.groupby("class")["score"].mean().reset_index()
print("\nreset_index之后:\n",res)
'''
class score
0 1班 90.0
1 2班 82.0
'''
高频坑:groupby 结果默认把分组字段作为 index,做后续表格合并时经常需要reset_index()。
5.apply 自定义函数
apply:把自定义函数作用到 Series 的每一个元素,或者 DataFrame 的每一行 / 每一列。
注意:优先用 pandas、numpy 内置向量化运算;循环 / 简单逻辑不要优先 apply,性能不如向量化。
import pandas as pd
df = pd.DataFrame({
"name":["张三","李四","王五","赵六"],
"age":[18,20,19,22],
"score":[88,92,79,85]
})
print(df)
'''
name age score
0 张三 18 88
1 李四 20 92
2 王五 19 79
3 赵六 22 85
'''
(1)Series.apply ():作用于列中每一个元素
简单函数,分数判断等级
def get_level(score):
if score >=90:
return "A"
elif score >=80:
return "B"
else:
return "C"
# 对score列Series使用apply
df["level"] = df["score"].apply(get_level)
print(df)
'''
name age score level
0 张三 18 88 B
1 李四 20 92 A
2 王五 19 79 C
3 赵六 22 85 B
'''
配合 lambda 匿名函数,简短逻辑不用 def
# age +2
df["age_plus2"] = df["age"].apply(lambda x: x+2)
print(df[["age","age_plus2"]])
'''
age age_plus2
0 18 20
1 20 22
2 19 21
3 22 24
'''
(2)DataFrame.apply ():作用于行或者列
参数 axis:
axis=0 默认:按列,函数接收每一列 Series
axis=1:按行,函数接收每一行 Series,最常用,可以同时读取多个列的值
axis=1,按行处理,可以同时访问多列
需求:生成描述字符串,张三,18岁,分数88
def gen_desc(row):
# row代表一行,可以通过列名取各个字段
return f"{row['name']},{row['age']}岁,分数{row['score']}"
df["desc"] = df.apply(gen_desc, axis=1)
print(df["desc"])
'''
0 张三,18岁,分数88
1 李四,20岁,分数92
2 王五,19岁,分数79
3 赵六,22岁,分数85
Name: desc, dtype: object
'''
axis=1 才是遍历行。
(3)apply 传额外参数
函数除了 row 还需要别的参数,用args传元组。
def add_offset(x, offset):
return x + offset
df["score_offset"] = df["score"].apply(add_offset, args=(5,))
print(df[["score","score_offset"]])
'''
score score_offset
0 88 93
1 92 97
2 79 84
3 85 90
'''
applymap(旧版本 pandas,全表每个元素),新版本 pandas 已经废弃applymap,推荐使用 df.map() / df.apply(lambda x:x.map(func))。
apply 性能,Series.apply:遍历列中每一个元素,本质是循环;简单计算优先用向量化运算。
#优先写这个(向量化,速度快)
df["score"] + 10
#不要优先写这个,速度慢
df["score"].apply(lambda x:x+10)
DataFrame.apply(axis=1)遍历行,性能更差,大数据量尽量少用。
适合 apply 场景:逻辑复杂,多分支判断,很难写成向量化表达式。
map () 补充:Series 映射,map专门用于 Series 值映射,经常做字典映射替换。
s = pd.Series([1,2,3])
#字典映射
s2 = s.map({1:"一",2:"二",3:"三"})
print(s2)
'''
0 一
1 二
2 三
dtype: object
'''
| 方法 | 对象 | 作用 |
|---|---|---|
Series.apply() | Series | 对每个元素执行函数,支持复杂 def 函数 |
Series.map() | Series | 元素映射,适合字典映射、简单转换 |
DataFrame.apply(func,axis=1) | DataFrame | 按行,一行作为对象传入函数,可以读取多列 |
三、Matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 解决中文显示,防止中文方框乱码
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False #解决负号显示异常
Matplotlib:Python底层绘图库;pandas、seaborn绘图底层都依赖它。
两套写法:
- plt.xxx:简单快速,脚本、交互式画图用。内部有隐藏的全局状态:当前画布 figure、当前子图 axes
- fig, ax = plt.subplots():显式创建画布对象fig和子图对象ax,一切操作都通过对象调用,没有隐藏全局状态。
1. plt.subplots ()
创建画布 Figure 和坐标轴 Axes 对象。
plt.subplots(nrows=1, ncols=1, figsize=(6,4), dpi=100)
nrows:子图行数;ncols:子图列数
figsize=(w,h):画布宽高,单位英寸
dpi:画布分辨率
返回:fig画布对象,ax坐标轴对象;多子图返回数组axes[i,j]
# 1个子图
fig, ax = plt.subplots(figsize=(6,4))
ax.plot([1,2,3], [2,4,1])
fig.tight_layout()
plt.show()
# 2行2列多个子图
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8,6))
axes[0,0].plot([1,2],[3,1])
axes[1,1].bar(["A","B"],[10,20])
fig.tight_layout()
plt.show()


fig.tight_layout () :自动调整子图间距,解决标题、坐标轴标签重叠。
ig.savefig () 保存画布为图片文件,必须写在 plt.show () 之前,show 会清空画布。
fig.savefig(fname, dpi=150, bbox_inches="tight")
fname:输出文件名,如"out.png"
dpi:输出分辨率
bbox_inches="tight":裁掉多余白边
plt.show () 弹出绘图窗口,渲染图像,执行后内存画布清空。
2.坐标轴设置函数
| 函数 | 说明 |
|---|---|
ax.set_title(text) | 设置子图标题 |
ax.set_xlabel(text) | X 轴名称 |
ax.set_ylabel(text) | Y 轴名称 |
ax.set_xlim(left,right) | X 轴显示范围 |
ax.set_ylim(bottom,top) | Y 轴显示范围 |
ax.set_xticks(list) | X 轴刻度位置 |
ax.set_xticklabels(list) | X 轴刻度文字 |
ax.legend() | 显示图例,绘图时需要指定 label |
ax.grid(True,alpha=0.3) | 绘制网格,alpha 透明度 0~1 |
plt 状态机对应:plt.title()、plt.xlabel()、plt.ylabel()、plt.xlim()、plt.ylim()、plt.legend()、plt.grid()
# 面向对象写法
fig, ax = plt.subplots(figsize=(5, 3))
ax.plot([1, 2, 3], [2, 4, 1], label="样本曲线")
ax.set_title("坐标轴设置示例")
ax.set_xlabel("X轴")
ax.set_ylabel("Y轴")
ax.set_xlim(0, 4)
ax.set_ylim(0, 5)
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()
# plt 状态机写法
plt.plot([1, 2, 3], [2, 4, 1], label="样本曲线")
plt.title("坐标轴设置示例")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.xlim(0, 4)
plt.ylim(0, 5)
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
(3)折线图
ax.plot () /plt.plot () 折线图
函数说明:绘制折线,适合趋势、时序数据。
ax.plot(x, y, color="red", marker="o", linestyle="-",
linewidth=2, markersize=6, label="曲线")
x,y:坐标序列
color:线条颜色;marker点标记;linestyle线条样式
linewidth线宽;markersize标记大小;label图例名称
x = np.arange(1, 6)
y = np.array([2, 4, 1, 5, 3])
# 面向对象
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(x, y, color='red', marker='o', linestyle='-', label='销量')
ax.set_title('销量折线')
ax.set_xlabel('月份')
ax.set_ylabel('销量')
ax.legend()
ax.grid(alpha=0.3)
plt.show()
# plt 状态机
plt.plot(x, y, color='red', marker='o', linestyle='-', label='销量')
plt.title('销量折线')
plt.xlabel('月份')
plt.ylabel('销量')
plt.legend()
plt.grid(alpha=0.3)
plt.show()

(4)ax.bar () /plt.bar () 垂直柱状图
ax.bar(x, height, width=0.6, color="#3498db", alpha=0.8)
x:类别标签;height柱子高度;width柱子宽度;alpha透明度
cate = ["1班","2班","3班"]
val = [88,82,91]
#面向对象
fig, ax = plt.subplots(figsize=(5,4))
ax.bar(cate, val, color=["#3498db","#2ecc71","#e74c3c"])
ax.set_title("各班平均分")
ax.set_ylabel("分数")
ax.set_ylim(70,95)
plt.show()
#plt状态机
plt.bar(cate, val, color=["#3498db","#2ecc71","#e74c3c"])
plt.title("各班平均分")
plt.ylabel("分数")
plt.ylim(70,95)
plt.show()

(5)ax.barh () /plt.barh () 水平柱状图
ax.barh(y, width, height=0.6)
y 轴类别;width柱子横向长度。
cate = ["1班","2班","3班"]
val = [88,82,91]
fig, ax = plt.subplots(figsize=(5,4))
ax.barh(cate, val)
ax.set_xlabel("分数")
plt.show()

(6)其它
x.scatter () /plt.scatter () 散点图
ax.scatter(x, y, s=50, c="orange", alpha=0.7)
s点大小;c点颜色;alpha透明度。
ax.hist () /plt.hist () 直方图
ax.hist(data, bins=20, alpha=0.7, color="green")
data一维数组;bins分组区间数量。
ax.pie () /plt.pie () 饼图
ax.pie(x, labels=labels, autopct="%.1f%%", startangle=90)
x各类别数值;labels标签;autopct格式化百分比;startangle起始旋转角度。
pandas 内置 .plot ()
pandas 封装 matplotlib,直接对 DataFrame/Series 绘图。
df.plot(kind="line/bar/barh/hist", figsize=(6,4))
df = pd.DataFrame({
"math":[88,92,79,85],
"english":[82,85,90,77]
}, index=["张三","李四","王五","赵六"])
df.plot(kind="bar", figsize=(6,4))
plt.title("学生成绩")
plt.show()


10万+

被折叠的 条评论
为什么被折叠?



