python在机器学习领域的有很多经典的第三方库,本文主要是介绍Nump、Pandas、Matplotlib、Seaborn、与Sklearn五个库的基础使用

一 Numpy库
Python 科学计算的基石和性能引擎。
1.1 核心概念:ndarray
NumPy的核心是同质数据的多维数组(所有元素类型相同),相比Python列表具有:
-
内存效率高:连续内存存储
-
运算速度快:向量化操作,无需Python循环
-
功能丰富:广播、矩阵运算、随机数等
python
import numpy as np
# 基础属性
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.ndim) # 维度数:2
print(arr.shape) # 形状:(2, 3)
print(arr.dtype) # 数据类型:int64
print(arr.size) # 元素总数:6
1.2 数组创建方法
python
# 从Python结构转换
np.array([1, 2, 3]) # 列表转数组
np.array([(1, 2), (3, 4)], dtype=float) # 指定类型
# 内置创建函数
np.zeros((3, 4)) # 3×4零矩阵
np.ones((2, 3, 4)) # 2×3×4全1数组(三维)
np.eye(3) # 3×3单位矩阵
np.full((2, 2), 7) # 填充特定值
# 序列生成
np.arange(0, 10, 2) # [0, 2, 4, 6, 8],步长为2
np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1],5个等间距点
# 随机数组
np.random.rand(3, 3) # 0-1均匀分布
np.random.randn(3, 3) # 标准正态分布
np.random.randint(1, 10, (3,3)) # 随机整数
np.random.seed(42) # 设置随机种子保证可复现
1.3数据类型(dtype)
python
# 常见类型
np.array([1, 2, 3], dtype=np.int32) # 32位整数
np.array([1.0, 2.0], dtype=np.float64) # 64位浮点(默认)
np.array([True, False], dtype=np.bool_) # 布尔型
# 类型转换
arr.astype(np.float32) # 转换数据类型
1.4 索引与切片
python
arr = np.arange(10).reshape(2, 5) # 2×5数组
# 基础索引
arr[0, 1] # 0行1列元素:1
arr[0][1] # 同上(但不建议,效率较低)
# 切片(重要:切片是视图view,非副本)
arr[:, 1:3] # 所有行,1-2列
arr[::-1] # 行逆序
# 布尔索引(筛选)
arr[arr > 5] # 所有大于5的元素(一维结果)
arr[(arr > 2) & (arr < 8)] # 多条件(&|, ~分别代表与或非)
# 花式索引(整数数组索引)
arr[[0, 1], [2, 3]] # 取(0,2)和(1,3)元素 → [2, 8]
arr[:, [0, 2, 4]] # 取第0,2,4列
# 获取副本(避免修改原数组)
arr[0:2, 0:2].copy()
1.5 数组运算与广播
向量化运算是NumPy的核心优势:
python
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 元素级运算
a + b # [5, 7, 9]
a * b # [4, 10, 18](逐元素乘,非矩阵乘)
a ** 2 # [1, 4, 9]
np.sqrt(a) # 开方
# 矩阵乘法
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
A @ B # 或 np.dot(A, B),矩阵乘法
# 广播机制(Broadcasting)
# 标量广播
a + 10 # [11, 12, 13]
# 维度广播(自动扩展较小数组)
matrix = np.ones((3, 3))
vector = np.arange(3) # [0, 1, 2]
matrix + vector # 每行加vector,结果为[[1,2,3], [1,2,3], [1,2,3]]
# 广播规则:从后向前比较维度,相等或其中一个为1时可广播
1.6 常用数学与统计函数
python
arr = np.random.randn(4, 5)
# 基础统计
arr.sum() # 总和
arr.mean() # 均值
arr.std() # 标准差
arr.var() # 方差
arr.min() # 最小值
arr.max() # 最大值
arr.argmin() # 最小值索引(扁平化后)
arr.argmax() # 最大值索引
# 轴参数(axis)关键概念
arr.sum(axis=0) # 按列求和(压缩行,结果形状(5,))
arr.sum(axis=1) # 按行求和(压缩列,结果形状(4,))
arr.mean(axis=0, keepdims=True) # 保持维度,结果形状(1,5)
# 其他数学函数
np.exp(arr) # 指数
np.log(arr) # 自然对数
np.sin(arr) # 三角函数
np.ceil(arr) # 向上取整
np.floor(arr) # 向下取整
np.clip(arr, -1, 1) # 限制在[-1,1]区间
1.7 数组形状操作
python
arr = np.arange(12) # [0..11]
# 重塑(reshape,返回视图或副本)
arr.reshape(3, 4) # 3×4数组
arr.reshape(3, -1) # -1自动计算该维度大小
arr.reshape(2, 2, 3) # 三维数组
# 转置与换轴
arr.T # 转置(二维)
arr.transpose(1, 0, 2) # 指定轴顺序
arr.swapaxes(0, 1) # 交换两个轴
# 扁平化
arr.flatten() # 返回副本(一维)
arr.ravel() # 返回视图(高效,修改会影响原数组)
# 增删维度
arr[np.newaxis, :] # 增加轴,形状(1, 12)
arr[:, np.newaxis] # 形状(12, 1)
np.squeeze(arr) # 移除长度为1的轴
1.8 数组合并与分割
python
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
# 合并
np.vstack((a, b)) # 垂直堆叠(行增加)
np.hstack((a, b.T)) # 水平堆叠(列增加)
np.concatenate([a, b], axis=0) # 指定轴连接
np.stack([a, a], axis=0) # 在新轴上堆叠,结果(2,2,2)
# 分割
np.split(arr, 3, axis=0) # 等分为3份
np.array_split(arr, 3) # 不等分(更常用)
np.hsplit(arr, 2) # 水平分割
np.vsplit(arr, 2) # 垂直分割
1.9 线性代数(np.linalg)
python
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
# 核心运算
np.linalg.inv(A) # 逆矩阵
np.linalg.det(A) # 行列式
np.linalg.matrix_rank(A) # 矩阵秩
np.linalg.solve(A, b) # 解线性方程组 Ax = b
# 分解与特征
eigenvalues, eigenvectors = np.linalg.eig(A) # 特征值与特征向量
U, S, Vh = np.linalg.svd(A) # 奇异值分解
# 范数
np.linalg.norm(A) # Frobenius范数
np.linalg.norm(A, ord=2) # 谱范数(最大奇异值)
1.10 实用技巧与最佳实践
python
# 1. 预分配内存(避免动态扩展)
result = np.empty((1000, 1000))
for i in range(1000):
result[i] = some_computation()
# 2. 布尔掩码赋值
arr[arr < 0] = 0 # 将所有负数置零(clip的替代)
# 3. 结构化数组(类似数据库记录)
dt = np.dtype([('name', 'U10'), ('age', 'i4'), ('score', 'f4')])
students = np.array([('Alice', 20, 85.5), ('Bob', 21, 92.0)], dtype=dt)
students['name'] # 访问字段
# 4. 内存布局
arr = np.array([[1, 2], [3, 4]], order='C') # C风格(行优先,默认)
arr = np.array([[1, 2], [3, 4]], order='F') # Fortran风格(列优先)
# 5. 视图vs副本
view = arr[::2] # 步长切片,总是视图
copy = arr[::2].copy() # 显式复制
# 6. 性能优化:避免Python循环,使用向量化
# 慢
result = [np.sin(x) for x in arr]
# 快
result = np.sin(arr)
1.11 实用建议
-
忘掉循环,拥抱向量化:养成直接用数组运算思考的习惯,这是用好 NumPy 的关键。
-
理解维度 :时刻清楚你的
arr.shape,这是进行复杂操作和广播的基础。 -
关注数据类型 :创建数组时,用
dtype参数(如np.float32)可节省大量内存。 -
作为基石:NumPy 数组是 Pandas(数据分析)、Scikit-learn(机器学习)等所有主流数据科学库的底层数据结构。
python
import pandas as pd
df = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
values = df.values # 转为NumPy数组(视图或副本视情况而定)
二 Pandas库
定位 :表格数据操作的事实标准,R 语言 DataFrame 的 Python 实现。
核心概念 :DataFrame(二维标签化表格)和 Series(一维标签化数组),自带行列索引。
关键能力:
-
数据 IO:读写 CSV/Excel/SQL/JSON,自动类型推断
-
数据清洗:缺失值处理(fillna/dropna)、重复值删除、异常值筛选、类型转换
-
数据变换:透视表(pivot)、长宽格式转换(melt)、分组聚合(groupby)、合并连接(merge/join)
-
时间序列:日期解析、重采样(resample)、移动窗口计算
典型场景:数据探索性分析(EDA)、特征工程、金融时间序列处理、日志分析。
2.1 核心数据结构
Series(一维带标签数组)
python
import pandas as pd
import numpy as np
# 创建
s = pd.Series([1, 3, 5, np.nan, 6, 8], index=['a', 'b', 'c', 'd', 'e', 'f'])
# 自动对齐:基于标签运算,而非位置
s['b'] # 3
DataFrame(二维表格)
python
# 从字典创建(最常用)
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'salary': [5000, 6000, 7000]
}, index=['a', 'b', 'c'])
# 基础属性
df.shape # (3, 3) - 行列数
df.columns # Index(['name', 'age', 'salary'], dtype='object')
df.index # 行标签
df.dtypes # 各列数据类型
df.info() # 详细信息(非空值数量、内存等)
df.describe() # 统计摘要(数值列)
2.2 数据读取与导出
python
# 读取(自动推断类型、处理表头)
df = pd.read_csv('data.csv', encoding='utf-8',
sep=',', # 分隔符
header=0, # 第几行作为列名
index_col=0, # 指定某列为索引
parse_dates=['date'], # 解析日期
na_values=['NA', 'NULL']) # 识别为NaN的值
# 读取多个sheet
all_sheets = pd.read_excel('example.xlsx', sheet_name=None)
for sheet, data in all_sheets.items():
print(f"Sheet Name: {sheet}")
print(data)
# 其他格式
pd.read_excel('data.xlsx', sheet_name='Sheet1')
pd.read_sql('SELECT * FROM table', conn)
pd.read_json('data.json')
# 导出
df.to_csv('output.csv', index=False, encoding='utf-8-sig') # index=False不保存行索引
df.to_excel('output.xlsx', sheet_name='Sheet1')
# 导出多个sheet
df1 = pd.DataFrame({'姓名': ['张三', '李四'], '年龄': [23, 25]})
df2 = pd.DataFrame({'姓名': ['张三', '李四'], '身高': [170, 171]})
with pd.ExcelWriter('multi_sheets.xlsx', engine='xlsxwriter') as writer:
df1.to_excel(writer, sheet_name='Sheet1', index=False)
df2.to_excel(writer, sheet_name='Sheet2', index=False)
2.3 索引与选择(核心)
python
# 列选择(返回Series或DataFrame)
df['name'] # 单列(Series)
df[['name', 'age']] # 多列(DataFrame)
df.name # 属性访问(仅当列名是有效标识符时)
# loc(基于标签/名称)
df.loc['a'] # 选择'a'行
df.loc[:, 'name'] # 选择'name'列
df.loc['a':'b', ['name', 'age']] # 切片(包含'b')
df.loc[df['age'] > 25] # 布尔索引
# iloc(基于整数位置,左闭右开)
df.iloc[0] # 第1行
df.iloc[0:2, 0:2] # 前2行,前2列
df.iloc[[0, 2], [1, 2]] # 特定行列
# at/iat(快速标量访问,比loc/iloc快)
df.at['a', 'name'] # 单个值
df.iat[0, 0]
# 布尔索引(筛选)
df[df['age'] > 25]
df[(df['age'] > 25) & (df['salary'] < 7000)] # & | ~ 代替 and or not
df.query('age > 25 and salary < 7000') # 字符串查询(更直观)
df[df['name'].isin(['Alice', 'Bob'])]
df[df['name'].str.contains('li')] # 字符串匹配
2.4 数据清洗
缺失值处理
python
# 检测
df.isnull() # 或 isna()
df.notnull()
df.isnull().sum() # 每列缺失值数量
# 处理
df.dropna() # 删除含NaN的行
df.dropna(subset=['age']) # 仅当age为NaN时删除
df.dropna(how='all') # 仅删除全为NaN的行
df.dropna(axis=1) # 删除含NaN的列
df.fillna(0) # 填充0
df['age'].fillna(df['age'].mean()) # 用均值填充
df.fillna(method='ffill') # 前向填充(用前值)
df.fillna(method='bfill') # 后向填充
重复值与类型转换
python
# 重复值
df.duplicated() # 标记重复行(除第一次外)
df.duplicated(keep=False) # 标记所有重复
df.drop_duplicates() # 删除重复行(保留第一个)
df.drop_duplicates(subset=['name'], keep='last')
# 类型转换
df['age'] = df['age'].astype(int)
df['salary'] = df['salary'].astype(float)
df['category'] = df['category'].astype('category') # 类别型(省内存)
pd.to_numeric(df['col'], errors='coerce') # 无法转换设为NaN
pd.to_datetime(df['date']) # 转为日期时间
异常值与替换
python
# 替换
df['gender'].replace({'M': 'Male', 'F': 'Female'})
df.replace(np.nan, 0)
df['age'].clip(0, 100) # 限制在0-100之间
# 重命名
df.rename(columns={'old_name': 'new_name'})
df.rename(index={'a': 'row_a'})
df.columns = ['col1', 'col2', 'col3'] # 直接赋值
2.5 数据变换
应用函数
python
# apply(沿轴应用函数)
df['age'].apply(lambda x: x + 1) # Series
df.apply(lambda row: row['age'] * 2, axis=1) # 行操作(axis=1)
df.applymap(str) # 逐元素(DataFrame专用,已弃用,用map替代)
# map(Series专用,逐元素映射)
df['gender'].map({'M': 0, 'F': 1})
# apply返回多列
def process(row):
return pd.Series([row['a']+row['b'], row['a']*row['b']])
df[['sum', 'product']] = df.apply(process, axis=1)
新增/删除列
python
# 新增
df['new_col'] = df['age'] * 12
df.insert(1, 'middle_name', 'Unknown') # 在位置1插入
# 删除
del df['temp_col']
df.drop('temp_col', axis=1) # 返回新DataFrame(原df不变)
df.drop(['col1', 'col2'], axis=1, inplace=True) # 原地修改
排序与排名
python
df.sort_values('age', ascending=False)
df.sort_values(['age', 'salary'], ascending=[True, False])
df.sort_index()
df['rank'] = df['salary'].rank(method='dense', ascending=False) # 排名
2.6 . 数据合并与重塑
合并(Merge/Join)
python
# merge(类似SQL join)
pd.merge(df1, df2, on='key', how='inner') # 内连接
pd.merge(df1, df2, left_on='lkey', right_on='rkey', how='left')
pd.merge(df1, df2, on=['key1', 'key2'], how='outer')
# join(基于索引,更便捷)
df1.join(df2, how='left', lsuffix='_left', rsuffix='_right')
# concat(轴向连接)
pd.concat([df1, df2], axis=0) # 纵向拼接(增加行)
pd.concat([df1, df2], axis=1) # 横向拼接(增加列)
pd.concat([df1, df2], ignore_index=True) # 重置索引
重塑(Pivot/Melt)
python
# pivot(长转宽)
df_pivot = df.pivot(index='date', columns='category', values='sales')
# pivot_table(透视表,支持聚合)
df.pivot_table(values='sales', index='category',
columns='region', aggfunc='sum', fill_value=0)
# melt(宽转长)
df_melt = pd.melt(df, id_vars=['id'], value_vars=['A', 'B'],
var_name='variable', value_name='value')
# stack/unstack(层级索引转换)
df.stack() # 列转行(长格式)
df.unstack() # 行转列(宽格式)
2.7 分组聚合(GroupBy)
python
# 基础分组
grouped = df.groupby('department')
grouped.mean() # 数值列均值
grouped.agg({'salary': 'mean', 'age': 'max'}) # 多聚合
# 多列分组
df.groupby(['dept', 'gender'])['salary'].agg(['mean', 'sum', 'count'])
# 自定义聚合
df.groupby('dept').agg({
'salary': lambda x: x.max() - x.min(), # 极差
'age': 'mean'
})
# transform(保持原索引,常用于标准化)
df['avg_salary'] = df.groupby('dept')['salary'].transform('mean')
# apply(自定义复杂操作)
def top_n(group, n=3):
return group.nlargest(n, 'salary')
df.groupby('dept').apply(top_n)
# 过滤
df.groupby('dept').filter(lambda x: x['salary'].mean() > 5000)
2.8 时间序列处理
python
# 时间索引
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
# 时间范围生成
pd.date_range(start='2024-01-01', periods=10, freq='D') # 日
pd.date_range(start='2024-01-01', periods=12, freq='M') # 月末
pd.date_range(start='2024-01-01', periods=4, freq='Q') # 季末
# 重采样(Resample)
df.resample('M').mean() # 月均值
df.resample('W').sum() # 周求和
df.resample('D').ffill() # 日填充
# 移动窗口
df['rolling_mean'] = df['value'].rolling(window=7).mean() # 7日移动平均
df['exp_mean'] = df['value'].ewm(span=7).mean() # 指数加权平均
# 时间提取
df.index.year
df.index.month
df.index.dayofweek
df.index.to_period('M') # 转为时期
2.9 高级技巧
多重索引(MultiIndex)
python
# 创建
arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]
df_multi = pd.DataFrame({'value': [10, 20, 30, 40]},
index=pd.MultiIndex.from_arrays(arrays, names=('number', 'color')))
# 访问
df_multi.loc[1] # 第一层
df_multi.loc[(1, 'red')] # 具体值
df_multi.xs('red', level='color') # cross section
内存优化
python
# 降型(Downcast)
df['int_col'] = pd.to_numeric(df['int_col'], downcast='integer') # int64→int32/int8
df['float_col'] = pd.to_numeric(df['float_col'], downcast='float')
# 类别型
df['category_col'] = df['category_col'].astype('category') # 字符串→类别(省内存)
迭代(避免,尽量用向量化)
python
# 慢(逐行Python循环)
for index, row in df.iterrows():
print(row['name'])
# 快(向量化)
df['new_col'] = df['col1'] + df['col2']
# itertuples(比iterrows快)
for row in df.itertuples():
print(row.name)
字符串处理(矢量化)
python
df['name'].str.lower()
df['name'].str.contains('Alice')
df['name'].str.split(' ').str[0] # 提取姓
df['name'].str.replace('Mr.', 'Ms.')
df['name'].str.len()
df['name'].str.extract(r'(\d+)') # 正则提取
1.10与 NumPy/可视化协作
python
# 转NumPy(底层数据)
arr = df.values # 或 to_numpy()
arr = df['col'].to_numpy()
# 快速绘图(基于Matplotlib)
df['age'].plot(kind='hist', bins=20)
df.plot(x='date', y='value', kind='line')
df.boxplot(column='salary', by='department')
# 相关性
df.corr() # 相关系数矩阵
df.corr()['target'].sort_values(ascending=False) # 与目标变量相关性
1.11 最佳实践
python
# 链式操作:使用 df.pipe() 或括号换行保持代码可读性
result = (df
.query('age > 25')
.assign(age_month=lambda x: x['age']*12)
.groupby('dept')
.agg({'salary': 'mean'}))
# 避免SettingWithCopyWarning:使用 .loc 或 .copy() 明确赋值
# 错误
df[df['age'] > 25]['salary'] = 0
# 正确
df.loc[df['age'] > 25, 'salary'] = 0
# 方法链中赋值:用 assign() 新增列
df = df.assign(age_double=lambda x: x['age'] * 2)
三 Matplotlib
定位 :Python 可视化的底层基础设施,提供类似 MATLAB 的绘图接口。
核心概念 :Figure(画布)和 Axes(坐标系/子图),面向对象与 pyplot 状态机双接口。
关键能力:
-
基础图表:折线图、散点图、柱状图、直方图、饼图、箱线图
-
精细控制:坐标轴刻度、图例、标注、颜色映射(colormap)、子图布局(subplot)
-
多后端支持:Jupyter 内嵌、独立窗口、保存为 PNG/PDF/SVG 矢量图
-
可扩展性:几乎所有高级可视化库(Seaborn、Plotly 等)都基于它构建
典型场景:出版级论文插图、需要像素级控制的复杂图表、快速数据探查。
3.1 Matplotlib 核心架构:三层理解
理解其架构,能让你从"调用函数"变为"驾驭框架"。
-
脚本层(
pyplot模块) :我们最常接触的plt.plot()、plt.xlabel()就属于这一层。它提供一套类似MATLAB的命令式API ,适合快速绘图和交互式环境,它会自动管理当前的图形(Figure)和坐标轴(Axes)。 -
艺术家层(
Artist对象) :这是Matplotlib的核心抽象层 。一切可见元素(Figure画布、Axes坐标轴、Line2D线条、Text文本)都是一个"艺术家"对象。通过直接操作这些对象,你可以实现像素级的精细控制。 -
后端层(Backend) :这是渲染引擎 ,负责将抽象的艺术家对象输出为屏幕上的像素(如
TkAgg、Qt5Agg)或文件(如PDF、PNG)。用户通常无需直接干预。
关键思想 :当你在脚本层调用 plt.plot() 时,底层其实是在当前 Axes 对象上创建了一个 Line2D 艺术家对象,并由后端渲染出来。
import matplotlib.pyplot as plt
import numpy as np
# 1. 数据准备 (通常从Pandas DataFrame来)
x = np.linspace(0, 10, 100)
categories = ['A', 'B', 'C', 'D']
values = [25, 40, 30, 35]
# 2. 创建画布和坐标轴 (面向对象风格,更清晰,推荐)
fig, ax = plt.subplots(figsize=(8, 5)) # fig是画布,ax是坐标轴
# 3. 绘制图表(在坐标轴ax上绘制)
ax.plot(x, np.sin(x), label='sin(x)', color='steelblue', linewidth=2) # 折线图
ax.scatter(x[::10], np.sin(x[::10]), color='darkorange', zorder=5) # 散点图(叠加)
# 4. 定制化图表(设置标题、标签、图例等)
ax.set_xlabel('X Axis Label', fontsize=12)
ax.set_ylabel('Y Axis Label', fontsize=12)
ax.set_title('A Professional Plot Example', fontsize=14, fontweight='bold')
ax.legend(frameon=True) # 显示图例
ax.grid(True, linestyle='--', alpha=0.6) # 显示网格
# 5. 保存与展示
fig.tight_layout() # 自动调整子图参数,使之填充整个图像区域
fig.savefig('professional_plot.png', dpi=300, bbox_inches='tight') # 保存高分辨率图片
plt.show()
3.2 基础图表类型(Plot Types)
|------|---------------------|-------------------------|----------------------------------------------------------|
| 图表类型 | 核心方法 | 典型数据分析应用场景 | 关键参数/技巧 |
| 折线图 | ax.plot(x, y) | 时间序列趋势(股价、销量)、连续变量关系。 | marker(标记点)、linestyle(线型)、linewidth(线宽)。 |
| 散点图 | ax.scatter(x, y) | 两个变量间的相关性、数据分布、聚类发现。 | s(点大小)、c(颜色,可赋值为数组以表示第三维)、alpha(透明度)。 |
| 柱状图 | ax.bar(x, height) | 分类数据对比(不同产品销量)、离散数据分布。 | 用 bar 表示并列,用 barh 绘制水平条形图。注意 x 为分类位置。 |
| 直方图 | ax.hist(data, bins) | 单一连续变量的分布情况(年龄分布、成绩分布)。 | bins(箱子数/边界)是关键,决定分布形状的呈现。density=True 可显示概率密度。 |
| 箱线图 | ax.boxplot(data) | 展示数据分布、中位数、异常值,多组数据对比。 | 一眼看出数据的四分位距(IQR)和离群点。 |
| 饼图 | ax.pie(sizes) | 显示各部分占整体的比例(市场份额、预算分配)。 | autopct='%1.1f%%'(显示百分比)、explode(部分突出)、startangle(起始角度)。 |
3.2.1 线图(Line Plot)
fig, ax = plt.subplots()
ax.plot(x, y,
color='red', # 颜色:'r', '#FF0000', (1,0,0)
linestyle='--', # 线型:'-', '--', '-.', ':'
linewidth=2, # 线宽
marker='o', # 标记:'o', 's', '^', 'D'
markersize=8,
label='Series A')
3.2.2散点图(Scatter Plot)
# 基础散点
ax.scatter(x, y, c=colors, s=sizes, alpha=0.6, cmap='viridis')
# 气泡图(三维信息:x, y, size)
ax.scatter(x, y, s=df['population']*0.1, c=df['growth_rate'],
cmap='RdYlGn', edgecolors='black', linewidth=0.5)
plt.colorbar(label='Growth Rate')
3.2.3 3 柱状图(Bar Chart)
# 垂直柱状图
ax.bar(categories, values, width=0.6, color='steelblue', edgecolor='black')
# 水平柱状图(适合类别多的情况)
ax.barh(categories, values, height=0.5)
# 分组柱状图
x = np.arange(len(categories))
width = 0.35
ax.bar(x - width/2, men_means, width, label='Men')
ax.bar(x + width/2, women_means, width, label='Women')
ax.set_xticks(x)
ax.set_xticklabels(categories)
3.2.4 直方图与密度图(Histogram / KDE)
# 直方图
ax.hist(data, bins=30, density=True, alpha=0.7, color='blue', edgecolor='black')
# 叠加核密度估计(需 scipy)
from scipy.stats import gaussian_kde
kde = gaussian_kde(data)
x_range = np.linspace(data.min(), data.max(), 100)
ax.plot(x_range, kde(x_range), 'r-', linewidth=2)
3.2.5 饼图与环形图(Pie Chart)
# 基础饼图
wedges, texts, autotexts = ax.pie(sizes,
labels=labels,
autopct='%1.1f%%',
startangle=90,
explode=[0, 0.1, 0]) # 突出显示第二块
# 环形图(Donut)
centre_circle = plt.Circle((0,0), 0.70, fc='white')
ax.add_artist(centre_circle)
3.2.6 箱线图与小提琴图(Boxplot / Violin)
# 箱线图
bp = ax.boxplot([data1, data2, data3],
labels=['A', 'B', 'C'],
patch_artist=True, # 填充颜色
notch=True) # 凹槽显示中位数置信区间
# 美化箱线图
colors = ['pink', 'lightblue', 'lightgreen']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)
3.3 图形元素精细控制
3.3.1 标题与标签
ax.set_title('Main Title', fontsize=16, fontweight='bold', pad=20)
ax.set_xlabel('X Axis', fontsize=12)
ax.set_ylabel('Y Axis', fontsize=12)
ax.text(0.5, 0.5, 'Annotation', transform=ax.transAxes, # 相对坐标
ha='center', fontsize=10, bbox=dict(boxstyle='round', facecolor='wheat'))
3.3.2 坐标轴控制
# 范围与刻度
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)
ax.set_xticks(np.arange(0, 11, 2))
ax.set_xticklabels(['Zero', 'Two', 'Four', 'Six', 'Eight', 'Ten'])
# 对数坐标
ax.set_xscale('log')
ax.set_yscale('log')
# 双 Y 轴(次要坐标轴)
ax2 = ax.twinx()
ax2.plot(x, y2, color='red')
ax2.set_ylabel('Secondary Axis', color='red')
ax2.tick_params(axis='y', labelcolor='red')
3.3.3 图例(Legend)
ax.legend(loc='upper left', # 位置:best, upper right, lower left 等
frameon=True, # 边框
fancybox=True, # 圆角
shadow=True,
ncol=2) # 多列显示
# 自定义图例(不依赖 label 参数)
from matplotlib.lines import Line2D
custom_lines = [Line2D([0], [0], color='blue', lw=2),
Line2D([0], [0], color='red', lw=2, linestyle='--')]
ax.legend(custom_lines, ['Line 1', 'Line 2'])
3.3.4注释(Annotation)
# 指向特定点的注释
ax.annotate('Peak Value',
xy=(peak_x, peak_y), # 被注释点
xytext=(peak_x+1, peak_y+0.5), # 文本位置
arrowprops=dict(arrowstyle='->', color='red', lw=1.5),
fontsize=10, color='red')
# 区域阴影
ax.axvspan(xmin=2, xmax=4, alpha=0.2, color='gray', label='Critical Zone')
ax.axhline(y=0.5, color='k', linestyle='--', linewidth=0.5) # 水平参考线
3.4 颜色与样式系统
3.4.1 颜色映射(Colormap)
# 内置 colormap:viridis, plasma, inferno, magma, cividis(感知均匀)
# 定性:tab10, tab20, Set1, Paired
# 连续:Blues, Greens, Oranges
# 发散:RdBu, coolwarm, Spectral
scatter = ax.scatter(x, y, c=z, cmap='viridis')
cbar = plt.colorbar(scatter, ax=ax)
cbar.set_label('Intensity', rotation=270, labelpad=15)
3.4.2 样式表(Style Sheets)
# 内置样式
plt.style.use('seaborn-v0_8-whitegrid') # 白色网格背景
plt.style.use('ggplot') # R 风格
plt.style.use('fivethirtyeight') # 网站风格
# 临时样式
with plt.style.context('dark_background'):
plt.plot(x, y)
3.4.3 自定义 RC 参数(全局配置)
plt.rcParams['font.size'] = 12
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['axes.grid'] = True
plt.rcParams['grid.alpha'] = 0.3
3.5. 子图与复杂布局(Layout)
3.5.1 基础子图(Subplot)
# 2×2 子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 遍历绘制
for i, ax in enumerate(axes.flat):
ax.plot(x, y)
ax.set_title(f'Subplot {i+1}')
plt.tight_layout() # 自动调整间距
3.5.2 不规则布局(GridSpec)
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig)
ax1 = fig.add_subplot(gs[0, :]) # 第一行占满
ax2 = fig.add_subplot(gs[1:, 0]) # 第二三行第一列
ax3 = fig.add_subplot(gs[1, 1:]) # 第二行后两列
ax4 = fig.add_subplot(gs[2, 1:]) # 第三行后两列
3.5.3 嵌套子图(Inset Axes)
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
# 在主图中添加小图
ax_inset = inset_axes(ax, width="30%", height="30%", loc='upper right')
ax_inset.plot(x, y)
ax_inset.set_xlim(2, 4) # 放大局部区域
ax_inset.set_ylim(-0.5, 0.5)
3.6. 高级图表类型
3.6.1 误差条与填充区域
# 误差条
ax.errorbar(x, y, yerr=error, fmt='o', capsize=5, capthick=2, elinewidth=1)
# 填充区域(置信区间)
ax.fill_between(x, y-std, y+std, alpha=0.3, label='±1 Std Dev')
ax.fill_between(x, 0, y, where=(y > 0), interpolate=True, alpha=0.3)
3.6.2 堆叠图与面积图
# 堆叠面积图
ax.stackplot(years, layer1, layer2, layer3,
labels=['Layer 1', 'Layer 2', 'Layer 3'],
colors=['#1f77b4', '#ff7f0e', '#2ca02c'],
alpha=0.8)
3.6.3 极坐标图(Polar)
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5]) # 径向刻度
3.6.4 3D 绘图(需 mpl_toolkits)
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 3D 散点
ax.scatter(x, y, z, c=z, cmap='viridis', s=50)
# 3D 曲面
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)
ax.plot_surface(X, Y, Z, cmap='coolwarm', alpha=0.8)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
3.7. 数据分析师必备技巧
3.7.1 中文显示解决方案
# 方法1:全局设置(推荐)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 方法2:局部设置
plt.text(0.5, 0.5, '中文', fontproperties='SimHei', fontsize=14)
3.7.2 时间序列绘图(与 Pandas 集成)
# Pandas 时间序列自动格式化
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
df['value'].plot(figsize=(12, 6)) # 自动处理时间刻度
plt.gcf().autofmt_xdate() # 自动旋转日期标签
3.7.3 保存高分辨率图像
plt.savefig('figure.png',
dpi=300, # 分辨率(出版级通常300+)
bbox_inches='tight', # 去除白边
facecolor='white', # 背景色
format='png') # 或 'pdf', 'svg', 'eps'(矢量图)
# 透明背景
plt.savefig('figure.png', transparent=True)
3.7.4 性能优化(大数据集)
# 降采样(避免百万级点卡死)
mask = np.arange(0, len(x), step=100) # 每100个点取1个
ax.plot(x[mask], y[mask])
# 使用 Datashader(超大数据集)或简化绘图
ax.plot(x, y, rasterized=True) # 栅格化(矢量输出时减小文件大小)
3.8 完整实战示例模板
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
# 创建数据
np.random.seed(42)
df = pd.DataFrame({
'月份': ['1月', '2月', '3月', '4月', '5月'],
'销售额': [120, 150, 180, 140, 200],
'利润': [20, 30, 45, 25, 50]
})
# 创建图形(OO 风格)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 左图:柱状图
bars = ax1.bar(df['月份'], df['销售额'], color='steelblue', edgecolor='black')
ax1.set_title('月度销售额', fontsize=14, fontweight='bold')
ax1.set_ylabel('金额(万元)')
for bar in bars:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{height}', ha='center', va='bottom')
# 右图:双轴图(销售额 vs 利润率)
ax2_twin = ax2.twinx()
line1 = ax2.plot(df['月份'], df['销售额'], 'o-', color='blue', label='销售额')
line2 = ax2_twin.plot(df['月份'], df['利润'], 's--', color='red', label='利润')
ax2.set_title('销售额与利润趋势', fontsize=14, fontweight='bold')
ax2.set_ylabel('销售额', color='blue')
ax2_twin.set_ylabel('利润', color='red')
ax2.tick_params(axis='y', labelcolor='blue')
ax2_twin.tick_params(axis='y', labelcolor='red')
# 合并图例
lines = line1 + line2
labels = [l.get_label() for l in lines]
ax2.legend(lines, labels, loc='upper left')
plt.tight_layout()
plt.savefig('report_chart.png', dpi=300, bbox_inches='tight')
plt.show()