Pandas数据分析 -- Pandas进阶语法全攻略:从数据处理到复杂分析实战 - 教程

Pandas进阶语法全攻略:从数据处理到复杂分析实战

在数据分析领域,Pandas 是Python生态中不可或缺的核心工具。基础语法掌握后,进阶功能能大幅提升数据处理效率,覆盖从数据读写、清洗、合并到聚合分析的全流程。本文结合实战代码,系统拆解 Pandas 进阶核心语法,适合有基础的同学查漏补缺、深化应用。

一、文件读取与存储:多源数据无缝对接

Pandas 支持多种数据格式的读写操作,其中 MySQL 数据库和 JSON 文件是实际开发中高频使用的场景,以下是具体实现方案。

1.1 读写 MySQL 数据库

需提前安装依赖包 pymysql(用于数据库连接),核心通过 read_sql() 读取数据,to_sql() 写入数据。

import pandas as pd
import pymysql
from sqlalchemy import create_engine
# 1. 数据库连接配置(SQLAlchemy 引擎,支持多线程)
engine = create_engine('mysql+pymysql://用户名:密码@主机IP:端口/数据库名?charset=utf8mb4')
# 2. 读取 MySQL 表数据
# 方式1:读取整个表
df_read = pd.read_sql('表名', con=engine)
# 方式2:执行SQL查询语句读取
sql = "SELECT id, name, salary FROM employee WHERE department='技术部'"
df_sql = pd.read_sql(sql, con=engine)
# 3. 写入 MySQL 表
# if_exists 参数:replace(覆盖)、append(追加)、fail(存在则报错)
df_write = pd.DataFrame({
'name': ['张三', '李四'],
'department': ['销售部', '技术部'],
'salary': [15000, 20000]
})
df_write.to_sql(
name='employee',  # 目标表名
con=engine,
if_exists='append',
index=False  # 不写入索引列
)

1.2 读写 JSON 文件

JSON 作为轻量级数据格式,广泛用于接口数据交互,Pandas 提供简洁的读写方法。

import pandas as pd
# 1. 读取 JSON 文件
# 方式1:标准 JSON 格式(每行一个 JSON 对象)
df_json = pd.read_json('data.json', lines=True)
# 方式2:读取嵌套 JSON(需指定 orient 参数)
df_nested = pd.read_json('nested_data.json', orient='records')
# 2. 写入 JSON 文件
# orient 参数:records(每行一个对象,推荐)、index(以索引为键)、columns(以列为键)
df = pd.DataFrame({
'product': ['手机', '电脑', '平板'],
'price': [3999, 5999, 2999],
'stock': [100, 50, 80]
})
# 写入标准 JSON(便于后续读取)
df.to_json('output.json', orient='records', force_ascii=False, indent=2)
# force_ascii=False:保留中文;indent=2:格式化输出,增强可读性

二、DataFrame 数据增删改查:数据操作核心

DataFrame 是 Pandas 核心数据结构,掌握其增删改查操作是数据处理的基础。

2.1 新增数据

import pandas as pd
df = pd.DataFrame({
'name': ['张三', '李四'],
'age': [25, 30],
'salary': [15000, 20000]
})
# 1. 新增列(直接赋值)
df['department'] = ['销售部', '技术部']  # 新增部门列
df['bonus'] = df['salary'] * 0.1  # 基于现有列计算新增
# 2. 新增行(append 已过时,推荐 _append 或 concat)
new_row = pd.DataFrame({'name': ['王五'], 'age': [28], 'salary': [18000], 'department': ['运营部'], 'bonus': 1800})
df = df._append(new_row, ignore_index=True)  # ignore_index:重置索引
# 3. 批量新增行(concat 效率更高)
new_rows = pd.DataFrame({
'name': ['赵六', '孙七'],
'age': [32, 27],
'salary': [22000, 16000],
'department': ['技术部', '销售部'],
'bonus': [2200, 1600]
})
df = pd.concat([df, new_rows], ignore_index=True)

2.2 删除数据

# 1. 删除列(drop 方法,axis=1 表示列)
df = df.drop(columns=['bonus'], axis=1)  # 按列名删除
df = df.drop(df.columns[2], axis=1)  # 按列索引删除(删除第3列)
# 2. 删除行(axis=0 表示行,默认)
df = df.drop(index=[0, 2], axis=0)  # 按索引删除
df = df[df['age'] > 28]  # 按条件删除(保留年龄>28的行)

2.3 修改数据

# 1. 按条件修改单个值
df.loc[df['name'] == '李四', 'salary'] = 21000  # 李四薪资改为21000
# 2. 批量修改列值
df['age'] = df['age'] + 1  # 所有人年龄+1
# 3. 替换值(replace 方法)
df['department'] = df['department'].replace('销售部', '市场部')  # 销售部改为市场部

2.4 查询数据

# 1. 基础查询(按列、按条件)
df['name']  # 查询单个列(返回 Series)
df[['name', 'salary']]  # 查询多个列(返回 DataFrame)
df[df['salary'] > 18000]  # 条件查询(薪资>18000)
# 2. 高级查询(loc/iloc 精准定位)
df.loc[df['department'] == '技术部', ['name', 'salary']]  # 按标签查询(部门=技术部的姓名和薪资)
df.iloc[1:3, [0, 2]]  # 按索引查询(第2-3行,第1、3列)
# 3. 多条件查询(& 且、| 或,注意括号)
df[(df['age'] > 28) & (df['salary'] > 20000)]  # 年龄>28 且 薪资>20000

三、缺失值处理:数据清洗关键步骤

真实数据常存在缺失值(NaN),处理不当会影响分析结果。Pandas 提供完整的缺失值处理方案,核心分为「检测」「删除」「填充」三类操作。

3.1 检测缺失值

import pandas as pd
import numpy as np
# 创建示例数据
df = pd.DataFrame({
'A': [1, 2, np.nan, 4],
'B': [5, np.nan, np.nan, 8],
'C': [9, 10, 11, 12]
})
# 1. 检查每个值是否为缺失值(返回布尔矩阵)
print(df.isnull())
# 2. 统计每列缺失值数量(常用)
print(df.isnull().sum())
# 3. 统计每行缺失值数量
print(df.isnull().sum(axis=1))
# 4. 统计总缺失值数量
print(df.isnull().sum().sum())

3.2 删除缺失值

# 1. 删除包含任何缺失值的行(默认 axis=0)
df_drop_any = df.dropna()
# 2. 只删除全为缺失值的行
df_drop_all = df.dropna(how='all')
# 3. 删除包含缺失值的列(axis=1)
df_drop_col = df.dropna(axis=1)
# 4. 保留至少有2个非缺失值的行(thresh 阈值)
df_drop_thresh = df.dropna(thresh=2)
# 5. 删除特定列包含缺失值的行(subset 指定列)
df_drop_subset = df.dropna(subset=['A', 'B'])

3.3 填充缺失值

# 1. 固定值填充
df_fill_0 = df.fillna(0)  # 用0填充
# 2. 统计值填充(适合数值型数据)
df_fill_mean = df.fillna(df.mean())  # 均值填充
df_fill_median = df.fillna(df.median())  # 中位数填充(抗异常值)
df_fill_mode = df.fillna(df.mode().iloc[0])  # 众数填充(适合分类数据)
# 3. 前后值填充(适合时间序列)
df_fill_ffill = df.fillna(method='ffill')  # 前向填充(用前一个值)
df_fill_bfill = df.fillna(method='bfill')  # 后向填充(用后一个值)
# 4. 限制填充数量
df_fill_limit = df.fillna(method='ffill', limit=1)  # 每行最多填充1个缺失值
# 5. 不同列用不同值填充(灵活配置)
df_fill_dict = df.fillna({'A': 0, 'B': df['B'].mean()})

3.4 高级处理技巧

# 1. 条件填充(根据其他列逻辑填充)
df_conditional = df.copy()
df_conditional.loc[df_conditional['A'].isnull(), 'A'] = df_conditional['C'].mean()
# 2. 分组填充(按分组统计量填充)
df_group_fill = df.copy()
df_group_fill['A'] = df_group_fill.groupby('B')['A'].transform(lambda x: x.fillna(x.mean()))
# 3. KNN填充(基于相似性填充,需安装 sklearn)
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=2)  # 取2个最近邻的均值
df_knn = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
# 4. 标记缺失值(保留缺失信息用于分析)
df_indicator = df.copy()
df_indicator['A_missing'] = df_indicator['A'].isnull()

3.5 实战案例(泰坦尼克号数据)

# 加载数据
titanic = pd.read_csv('titanic.csv')
# 查看缺失值情况
print(titanic.isnull().sum())
# 策略性处理
titanic['Age'].fillna(titanic['Age'].median(), inplace=True)  # 年龄用中位数填充
titanic['Embarked'].fillna(titanic['Embarked'].mode()[0], inplace=True)  # 登船口用众数填充
titanic.drop('Cabin', axis=1, inplace=True)  # 客舱列缺失过多,直接删除
# 验证处理结果
print(titanic.isnull().sum())

3.6 关键注意事项

  1. 填充前需理解数据业务含义,避免无意义填充(如用均值填充分类数据)。
  2. 数值型数据优先用中位数(抗异常值),分类数据用众数。
  3. 时间序列数据适合前向/后向填充或插值。
  4. 谨慎删除数据,避免信息丢失,优先尝试填充。

四、数据合并:Concat 与 Merge 详解

数据合并是多源数据整合的核心操作,Pandas 提供 concat()(拼接)和 merge()(关联)两种核心方法,适用场景不同。

4.1 Concat:数据堆叠(基于索引/列名)

核心用途:沿轴方向(行/列)拼接多个结构相似的 DataFrame,类似「堆叠」操作。

import pandas as pd
# 创建示例数据
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}, index=['X', 'Y', 'Z'])
df2 = pd.DataFrame({'A': [7, 8, 9], 'B': [10, 11, 12]}, index=['U', 'V', 'W'])
df3 = pd.DataFrame({'C': [13, 14, 15], 'D': [16, 17, 18]}, index=['X', 'Y', 'Z'])
# 1. 纵向拼接(默认 axis=0,行堆叠)
result_v = pd.concat([df1, df2])
# 2. 横向拼接(axis=1,列堆叠)
result_h = pd.concat([df1, df3], axis=1)
# 3. 忽略索引(重置为连续索引)
result_ignore = pd.concat([df1, df2], ignore_index=True)
# 4. 连接方式(outer 外连接默认,inner 内连接)
df4 = pd.DataFrame({'A': [1, 2], 'B': [4, 5]}, index=['X', 'Y'])
df5 = pd.DataFrame({'A': [3, 4], 'B': [6, 7]}, index=['Y', 'Z'])
result_outer = pd.concat([df4, df5], join='outer')  # 保留所有索引
result_inner = pd.concat([df4, df5], join='inner')  # 只保留共同索引

4.2 Merge:数据库风格关联(基于关键字段)

核心用途:基于一个或多个关键字段(key)关联多个 DataFrame,类似 SQL 的 JOIN 操作,灵活性更高。

import pandas as pd
# 创建示例数据
left = pd.DataFrame({
'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']
})
right = pd.DataFrame({
'key': ['K0', 'K1', 'K2', 'K4'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']
})
# 1. 内连接(默认 how='inner',只保留共同 key)
result_inner = pd.merge(left, right, on='key')
# 2. 左连接(how='left',保留左表所有数据)
result_left = pd.merge(left, right, on='key', how='left')
# 3. 右连接(how='right',保留右表所有数据)
result_right = pd.merge(left, right, on='key', how='right')
# 4. 外连接(how='outer',保留所有数据)
result_outer = pd.merge(left, right, on='key', how='outer')
# 5. 多键合并(基于多个字段关联)
left2 = pd.DataFrame({
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']
})
right2 = pd.DataFrame({
'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']
})
result_multi = pd.merge(left2, right2, on=['key1', 'key2'], how='inner')

4.3 核心区别对比

特性concat()merge()
合并方式基于索引或列名基于关键字段
灵活性较低(仅堆叠)较高(支持复杂关联)
内存效率较高较低
适用场景简单数据堆叠、时间序列拼接关系型数据关联、复杂匹配
语法复杂度简单相对复杂

4.4 最佳实践

  1. 优先用 concat():数据结构相同、需简单堆叠(如时间序列拼接、特征矩阵合并)。
  2. 优先用 merge():需基于关键字段匹配(如客户表与订单表关联)。
  3. 性能优化:大数据集用 concat()merge() 可设置 sort=False 提高速度,或用索引合并(left_index=True, right_index=True)。

五、分组聚合与过滤:数据筛查核心

分组聚合(Group Aggregation)和分组过滤(Group Filtering)是数据分析的核心手段,遵循「split-apply-combine」逻辑:拆分数据→应用操作→合并结果。

5.1 分组聚合(Group Aggregation)

对分组后的数据执行统计计算(如均值、总和),核心语法 groupby() + agg()

import pandas as pd
# 创建示例数据
df = pd.DataFrame({
"部门": ["销售", "销售", "技术", "技术", "运营", "运营"],
"姓名": ["张三", "李四", "王五", "赵六", "孙七", "周八"],
"月薪": [15000, 18000, 20000, 25000, 12000, 14000],
"出勤天数": [22, 23, 21, 22, 23, 24]
})
# 1. 单分组+单聚合(按部门统计月薪均值)
dept_salary_mean = df.groupby("部门")["月薪"].mean()
# 2. 单分组+多聚合(按部门统计月薪和出勤天数的多个指标)
dept_agg = df.groupby("部门").agg({
"月薪": ["mean", "sum", "max"],  # 月薪:均值、总和、最大值
"出勤天数": ["mean", "count"]     # 出勤天数:均值、分组人数
})
# 3. 多分组+聚合(按部门+性别分组,统计月薪均值)
df["性别"] = ["男", "女", "男", "男", "女", "女"]
dept_gender_agg = df.groupby(["部门", "性别"])["月薪"].mean()
# 4. 自定义聚合函数(计算月薪中位数与均值的差值)
def median_mean_diff(x):
return x.median() - x.mean()
dept_diff = df.groupby("部门")["月薪"].agg(median_mean_diff)
# 5. 聚合结果重命名(扁平化列名)
dept_agg.columns = ["月薪均值", "月薪总和", "月薪最高", "出勤均值", "出勤人数"]
dept_agg.reset_index(inplace=True)  # 分组列从索引转为普通列

5.2 常用聚合函数速查表

函数功能适用场景
mean()平均值数值型数据(薪资、分数)
sum()总和可累加数据(销售额、时长)
count()非空值数量统计有效数据量
size()分组总数量(含空值)统计分组行数
max()/min()最大值/最小值数值型、日期型数据
median()中位数避免异常值影响的统计
std()/var()标准差/方差数据离散程度分析
nunique()唯一值数量统计分组类别数(如用户数)

5.3 分组过滤(Group Filtering)

根据分组的整体属性筛选行,保留满足条件的整个分组,核心语法 groupby() + filter()

# 1. 按分组大小过滤(保留人数≥2的部门)
df_filter_size = df.groupby("部门").filter(lambda x: len(x) >= 2)
# 2. 按分组统计量过滤(保留平均月薪≥15000的部门)
df_filter_salary = df.groupby("部门").filter(lambda x: x["月薪"].mean() >= 15000)
# 3. 多条件过滤(保留平均月薪≥15000且平均出勤≥22的部门)
df_filter_multi = df.groupby("部门").filter(
lambda x: (x["月薪"].mean() >= 15000) & (x["出勤天数"].mean() >= 22)
)

5.4 分组过滤 vs 普通行过滤

操作筛选依据结果保留示例代码
普通行过滤单条行的属性满足条件的单条行df[df[“月薪”] >= 15000]
分组过滤整个分组的聚合属性满足条件的整个分组df.groupby(“部门”).filter(lambda x: x[“月薪”].mean()>=15000)

六、透视表与交叉表:多维度分析可视化

透视表(pivot_table)和交叉表(crosstab)是分组聚合的「可视化友好版」,适合生成结构化报表,可读性更强。

6.1 透视表(pivot_table):重点掌握

核心用途:多维度数值型数据聚合,支持自定义函数、缺失值填充,灵活性高。

import pandas as pd
# 示例数据(沿用部门数据)
df = pd.DataFrame({
"部门": ["销售", "销售", "技术", "技术", "技术", "运营", "运营"],
"姓名": ["张三", "李四", "王五", "赵六", "钱九", "孙七", "周八"],
"性别": ["男", "女", "男", "男", "女", "女", "女"],
"月薪": [15000, 18000, 20000, 25000, 19000, 12000, 14000],
"出勤天数": [22, 23, 21, 22, 24, 23, 24]
})
# 1. 基础透视表(行:部门,列:性别,值:月薪,聚合:均值)
pivot1 = pd.pivot_table(
df,
values="月薪",
index="部门",
columns="性别",
aggfunc="mean",
fill_value=0  # 缺失值填充为0
)
# 2. 显示总计行/列
pivot2 = pd.pivot_table(
df,
values="月薪",
index="部门",
columns="性别",
aggfunc="mean",
fill_value=0,
margins=True,  # 显示总计
margins_name="部门总计"
)
# 3. 多指标聚合(同时统计月薪和出勤天数)
pivot3 = pd.pivot_table(
df,
values=["月薪", "出勤天数"],
index="部门",
columns="性别",
aggfunc={
"月薪": ["mean", "sum"],
"出勤天数": "mean"
},
fill_value=0
)
# 4. 列名扁平化(解决多指标导致的层级列名)
pivot3.columns = ["_".join(col).strip() for col in pivot3.columns.values]
pivot3.reset_index(inplace=True)
# 5. 自定义聚合函数
def my_agg(x):
return x.median()
pivot5 = pd.pivot_table(
df,
values="月薪",
index="部门",
columns="性别",
aggfunc=my_agg,
fill_value=0
)

6.2 交叉表(crosstab):了解即可

核心用途:分类数据的频次统计(默认计数),支持归一化(占比计算),语法简洁。

# 1. 基础交叉表(统计各部门各性别人数)
crosstab1 = pd.crosstab(
index=df["部门"],
columns=df["性别"],
rownames=["部门"],
colnames=["性别"],
margins=True,  # 显示总计
margins_name="总计"
)
# 2. 归一化(计算每行占比)
crosstab2 = pd.crosstab(
df["部门"],
df["性别"],
normalize="index",  # 按行归一化(行占比和为1)
margins=True
).round(2) * 100  # 转为百分比
# 3. 数值列聚合(类似透视表,灵活性较差)
crosstab3 = pd.crosstab(
df["部门"],
df["性别"],
values=df["月薪"],
aggfunc="mean",
fill_value=0,
margins=True
)

6.3 透视表 vs 交叉表:怎么选?

需求场景优先选择原因
分类数据频次/占比统计交叉表(crosstab)语法简洁,默认支持归一化
多指标聚合(均值+总和等)透视表(pivot_table)支持不同指标自定义函数
多维度分组(≥2个维度)透视表(pivot_table)配置更灵活
复杂自定义聚合逻辑透视表(pivot_table)支持任意自定义函数

七、总结与实战建议

Pandas 进阶语法围绕「数据读写→清洗→合并→分析」的全流程展开,核心要点如下:

  1. 数据读写:根据数据源选择 read_sql()/to_sql()(MySQL)或 read_json()/to_json()(JSON)。
  2. 缺失值处理:优先填充(均值/中位数/众数/KNN),谨慎删除,必要时标记缺失值。
  3. 数据合并:简单堆叠用 concat(),关键字段关联用 merge()
  4. 分组分析:聚合用 groupby()+agg(),筛选分组用 filter()
  5. 报表生成:多维度数值分析用透视表,分类频次统计用交叉表。

实战建议:结合真实业务数据(如销售数据、用户行为数据)反复练习,重点关注「语法逻辑」而非死记硬背,遇到问题时查看 Pandas 官方文档或调试代码逐步排查。

posted @ 2025-12-14 20:38  clnchanpin  阅读(9)  评论(0)    收藏  举报