1. Pandas 简介与安装 #
1.1 什么是 Pandas? #
Pandas 是 Python 中最流行的数据处理和分析库,它的名字来源于"Panel Data"(面板数据)。Pandas 专门用于处理结构化数据,就像 Excel 表格一样,但功能更强大。
1.2 为什么学习 Pandas? #
- 处理表格数据:可以轻松处理 CSV、Excel 等格式的数据
- 数据清洗:快速处理缺失值、重复值等问题
- 数据分析:进行分组、聚合、统计等操作
- 数据可视化:配合 Matplotlib 等库进行数据可视化
- 广泛应用:数据科学、金融分析、商业分析等领域必备工具
1.3 安装 Pandas #
# 在命令行或终端中执行以下命令安装 Pandas
# pip install pandas
# 如果需要读取 Excel 文件,还需要安装 openpyxl
# pip install openpyxl1.4 导入 Pandas #
# 导入 pandas 库,通常简写为 pd
import pandas as pd
# 同时导入 numpy,因为 Pandas 依赖 NumPy
import numpy as np
# 打印 Pandas 版本(可选)
print(pd.__version__)2. 前置知识:NumPy 基础 #
在学习 Pandas 之前,我们需要了解一些 NumPy 的基础知识,因为 Pandas 是基于 NumPy 构建的。
2.1 NumPy 数组 #
NumPy 提供了高效的数组操作,Pandas 内部使用 NumPy 数组来存储数据。
# 导入 numpy
import numpy as np
# 创建一维数组(类似 Python 列表)
arr = np.array([1, 2, 3, 4, 5])
print(arr)
# 输出:[1 2 3 4 5]
# 创建二维数组(类似表格)
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(arr_2d)
# 输出:
# [[1 2 3]
# [4 5 6]]
# NumPy 的特殊值:NaN(Not a Number,表示缺失值)
nan_value = np.nan
print(nan_value)
# 输出:nan2.2 为什么需要了解 NumPy? #
- Pandas 的 Series 和 DataFrame 底层使用 NumPy 数组
- 很多 Pandas 操作返回 NumPy 数组
- 理解 NumPy 有助于理解 Pandas 的工作原理
3. Series:一维数据结构 #
3.1 什么是 Series? #
Series 是 Pandas 中最基本的数据结构,可以理解为"带标签的一维数组"。它类似于 Excel 中的一列数据,但每个数据都有对应的索引(标签)。
3.2 创建 Series #
# 导入必要的库
import pandas as pd
import numpy as np
# 方法1:从列表创建 Series(最简单的方式)
# 创建一个包含数字的列表
data_list = [10, 20, 30, 40, 50]
# 使用 pd.Series() 创建 Series,会自动生成索引 0, 1, 2, 3, 4
s1 = pd.Series(data_list)
print(s1)
# 输出:
# 0 10
# 1 20
# 2 30
# 3 40
# 4 50
# dtype: int64
# 方法2:从列表创建,并指定自定义索引
# 创建数据列表
data_list = [10, 20, 30, 40, 50]
# 创建索引列表(标签)
index_list = ['a', 'b', 'c', 'd', 'e']
# 使用 index 参数指定索引
s2 = pd.Series(data_list, index=index_list)
print(s2)
# 输出:
# a 10
# b 20
# c 30
# d 40
# e 50
# dtype: int64
# 方法3:从字典创建 Series(字典的键会成为索引)
# 创建字典,键是索引,值是数据
data_dict = {'a': 10, 'b': 20, 'c': 30, 'd': 40, 'e': 50}
# 从字典创建 Series
s3 = pd.Series(data_dict)
print(s3)
# 输出:
# a 10
# b 20
# c 30
# d 40
# e 50
# dtype: int64
# 方法4:创建包含缺失值的 Series
# np.nan 表示缺失值(Not a Number)
data_with_nan = [1, 3, 5, np.nan, 6, 8]
s4 = pd.Series(data_with_nan)
print(s4)
# 输出:
# 0 1.0
# 1 3.0
# 2 5.0
# 3 NaN
# 4 6.0
# 5 8.0
# dtype: float643.3 访问 Series 数据 #
# 创建示例 Series
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])
# 通过索引访问单个值
print(s['a'])
# 输出:10
# 通过位置访问(使用 iloc)
print(s.iloc[0])
# 输出:10
# 访问多个值(使用列表)
print(s[['a', 'c', 'e']])
# 输出:
# a 10
# c 30
# e 50
# dtype: int64
# 通过位置访问多个值
print(s.iloc[0:3])
# 输出:
# a 10
# b 20
# c 30
# dtype: int643.4 Series 的基本操作 #
# 创建示例 Series
s = pd.Series([10, 20, 30, 40, 50])
# 查看 Series 的基本信息
print(s.values) # 获取所有值(返回 NumPy 数组)
# 输出:[10 20 30 40 50]
print(s.index) # 获取索引
# 输出:RangeIndex(start=0, stop=5, step=1)
print(s.dtype) # 获取数据类型
# 输出:int64
print(len(s)) # 获取长度
# 输出:5
# 数学运算(会对每个元素进行操作)
print(s * 2)
# 输出:
# 0 20
# 1 40
# 2 60
# 3 80
# 4 100
# dtype: int64
# 统计函数
print(s.mean()) # 平均值
# 输出:30.0
print(s.sum()) # 求和
# 输出:150
print(s.max()) # 最大值
# 输出:50
print(s.min()) # 最小值
# 输出:104. DataFrame:二维表格数据 #
4.1 什么是 DataFrame? #
DataFrame 是 Pandas 中最重要的数据结构,可以理解为"带标签的二维表格",就像 Excel 表格一样。它由多个 Series 组成,每一列是一个 Series。
4.2 创建 DataFrame #
# 导入必要的库
import pandas as pd
import numpy as np
# 方法1:从字典创建 DataFrame(最常用)
# 字典的键会成为列名,值会成为该列的数据
data_dict = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 15000, 10000]
}
# 使用 pd.DataFrame() 创建 DataFrame
df = pd.DataFrame(data_dict)
print(df)
# 输出:
# 姓名 年龄 城市 工资
# 0 张三 25 北京 8000
# 1 李四 30 上海 12000
# 2 王五 35 广州 15000
# 3 赵六 28 深圳 10000
# 方法2:从列表的列表创建 DataFrame
# 先准备数据(每一行是一个列表)
data_list = [
['张三', 25, '北京', 8000],
['李四', 30, '上海', 12000],
['王五', 35, '广州', 15000],
['赵六', 28, '深圳', 10000]
]
# 使用 columns 参数指定列名
df2 = pd.DataFrame(data_list, columns=['姓名', '年龄', '城市', '工资'])
print(df2)
# 输出:同上
# 方法3:从字典列表创建 DataFrame
# 每个字典代表一行数据
data_dict_list = [
{'姓名': '张三', '年龄': 25, '城市': '北京', '工资': 8000},
{'姓名': '李四', '年龄': 30, '城市': '上海', '工资': 12000},
{'姓名': '王五', '年龄': 35, '城市': '广州', '工资': 15000},
{'姓名': '赵六', '年龄': 28, '城市': '深圳', '工资': 10000}
]
df3 = pd.DataFrame(data_dict_list)
print(df3)
# 输出:同上
# 方法4:创建空的 DataFrame,然后添加数据
# 创建只有列名的空 DataFrame
df4 = pd.DataFrame(columns=['姓名', '年龄', '城市', '工资'])
print(df4)
# 输出:空的 DataFrame
# 方法5:从 NumPy 数组创建
# 创建随机数据
np_data = np.random.randn(4, 3) # 4行3列的随机数
df5 = pd.DataFrame(np_data, columns=['列1', '列2', '列3'])
print(df5)
# 输出:4行3列的随机数表格4.3 DataFrame 的基本结构 #
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 15000, 10000]
})
# 查看 DataFrame 的基本信息
print(df.shape) # 查看形状(行数, 列数)
# 输出:(4, 4)
print(df.columns) # 查看列名
# 输出:Index(['姓名', '年龄', '城市', '工资'], dtype='object')
print(df.index) # 查看索引
# 输出:RangeIndex(start=0, stop=4, step=1)
print(df.dtypes) # 查看每列的数据类型
# 输出:
# 姓名 object
# 年龄 int64
# 城市 object
# 工资 int64
# dtype: object
print(df.size) # 查看总元素数量(行数 × 列数)
# 输出:165. 数据读取与保存 #
5.1 读取 CSV 文件 #
CSV(Comma-Separated Values)是最常见的数据格式,用逗号分隔值。
# 导入必要的库
import pandas as pd
# 读取 CSV 文件
# 假设有一个名为 data.csv 的文件
# df = pd.read_csv('data.csv')
# 如果文件第一行不是列名,可以指定列名
# df = pd.read_csv('data.csv', names=['列1', '列2', '列3'])
# 如果文件使用其他分隔符(如分号),使用 sep 参数
# df = pd.read_csv('data.csv', sep=';')
# 如果文件有编码问题(中文乱码),指定编码
# df = pd.read_csv('data.csv', encoding='utf-8')
# 或者
# df = pd.read_csv('data.csv', encoding='gbk')
# 示例:创建一个 CSV 文件并读取
# 先创建一个示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']
})
# 保存为 CSV 文件
df.to_csv('示例数据.csv', index=False, encoding='utf-8-sig')
# index=False 表示不保存行索引
# encoding='utf-8-sig' 确保中文正常显示
# 读取刚才保存的 CSV 文件
df_read = pd.read_csv('示例数据.csv', encoding='utf-8-sig')
print(df_read)5.2 读取 Excel 文件 #
Excel 文件是另一种常见的数据格式。
# 导入必要的库
import pandas as pd
# 读取 Excel 文件(需要安装 openpyxl:pip install openpyxl)
# df = pd.read_excel('data.xlsx')
# 读取指定的工作表(sheet)
# df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
# 读取多个工作表
# all_sheets = pd.read_excel('data.xlsx', sheet_name=None)
# 返回一个字典,键是工作表名,值是 DataFrame
# 示例:创建并读取 Excel 文件
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']
})
# 保存为 Excel 文件
df.to_excel('示例数据.xlsx', index=False, sheet_name='员工信息')
# index=False 表示不保存行索引
# sheet_name 指定工作表名称
# 读取 Excel 文件
df_read = pd.read_excel('示例数据.xlsx', sheet_name='员工信息')
print(df_read)5.3 读取 JSON 文件 #
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。
# 导入必要的库
import pandas as pd
# 读取 JSON 文件
# df = pd.read_json('data.json')
# 如果 JSON 是嵌套格式,可能需要指定 orient 参数
# df = pd.read_json('data.json', orient='records')
# 示例:创建并读取 JSON 文件
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']
})
# 保存为 JSON 文件
df.to_json('示例数据.json', orient='records', force_ascii=False)
# orient='records' 表示每行数据作为一个 JSON 对象
# force_ascii=False 确保中文正常显示
# 读取 JSON 文件
df_read = pd.read_json('示例数据.json')
print(df_read)5.4 保存数据 #
# 导入必要的库
import pandas as pd
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']
})
# 保存为 CSV(最常用)
df.to_csv('输出数据.csv', index=False, encoding='utf-8-sig')
# index=False 不保存行索引
# encoding='utf-8-sig' 确保中文正常显示
# 保存为 Excel
df.to_excel('输出数据.xlsx', index=False, sheet_name='数据')
# 保存为 JSON
df.to_json('输出数据.json', orient='records', force_ascii=False)6. 数据查看与探索 #
6.1 查看数据的基本方法 #
在开始分析数据之前,我们需要先了解数据的基本情况。
# 导入必要的库
import pandas as pd
import numpy as np
# 创建示例 DataFrame(模拟真实数据)
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'],
'年龄': [25, 30, 35, 28, 32, 27, 29, 31],
'城市': ['北京', '上海', '广州', '深圳', '北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 15000, 10000, 9000, 13000, 16000, 11000],
'部门': ['销售', '技术', '技术', '销售', '技术', '销售', '技术', '销售']
})
# 查看前几行数据(默认前5行)
print(df.head())
# 输出:前5行数据
# 查看前 n 行
print(df.head(3))
# 输出:前3行数据
# 查看后几行数据(默认后5行)
print(df.tail())
# 输出:后5行数据
# 查看后 n 行
print(df.tail(3))
# 输出:后3行数据
# 查看数据的基本信息(非常重要!)
print(df.info())
# 输出:
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 8 entries, 0 to 7
# Data columns (total 5 columns):
# # Column Non-Null Count Dtype
# -- --
# 0 姓名 8 non-null object
# 1 年龄 8 non-null int64
# 2 城市 8 non-null object
# 3 工资 8 non-null int64
# 4 部门 8 non-null object
# dtypes: int64(2), object(3)
# memory usage: 448.0+ bytes
# 查看数据的统计描述(只对数值列有效)
print(df.describe())
# 输出:
# 年龄 工资
# count 8.000000 8.000000
# mean 29.625000 11625.000000
# std 3.204164 2906.797181
# min 25.000000 8000.000000
# 25% 27.750000 9500.000000
# 50% 29.500000 11500.000000
# 75% 31.250000 13500.000000
# max 35.000000 16000.0000006.2 查看数据的形状和结构 #
# 继续使用上面的 df
# 查看数据的形状(行数, 列数)
print(df.shape)
# 输出:(8, 5) # 8行5列
# 查看列名
print(df.columns)
# 输出:Index(['姓名', '年龄', '城市', '工资', '部门'], dtype='object')
# 查看索引
print(df.index)
# 输出:RangeIndex(start=0, stop=8, step=1)
# 查看每列的数据类型
print(df.dtypes)
# 输出:
# 姓名 object
# 年龄 int64
# 城市 object
# 工资 int64
# 部门 object
# dtype: object
# 查看是否有缺失值
print(df.isnull().sum())
# 输出:每列的缺失值数量(这里都是0,因为没有缺失值)
# 查看总元素数量
print(df.size)
# 输出:40 # 8行 × 5列 = 407. 数据选择与筛选 #
7.1 选择列 #
选择列是数据分析中最常用的操作之一。
# 导入必要的库
import pandas as pd
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 15000, 10000]
})
# 方法1:使用方括号选择单列(返回 Series)
name_column = df['姓名']
print(name_column)
# 输出:
# 0 张三
# 1 李四
# 2 王五
# 3 赵六
# Name: 姓名, dtype: object
# 方法2:使用点号选择单列(仅当列名无空格时可用)
age_column = df.年龄
print(age_column)
# 输出:
# 0 25
# 1 30
# 2 35
# 3 28
# Name: 年龄, dtype: int64
# 方法3:选择多列(使用列表,返回 DataFrame)
name_age = df[['姓名', '年龄']]
print(name_age)
# 输出:
# 姓名 年龄
# 0 张三 25
# 1 李四 30
# 2 王五 35
# 3 赵六 287.2 选择行 #
# 继续使用上面的 df
# 方法1:使用 iloc 按位置选择行(最常用)
# iloc 是 "integer location" 的缩写,按整数位置选择
# 选择第一行(索引从0开始)
first_row = df.iloc[0]
print(first_row)
# 输出:
# 姓名 张三
# 年龄 25
# 城市 北京
# 工资 8000
# Name: 0, dtype: object
# 选择前3行
first_three = df.iloc[0:3]
print(first_three)
# 输出:前3行数据
# 选择特定行(使用列表)
specific_rows = df.iloc[[0, 2]]
print(specific_rows)
# 输出:第1行和第3行
# 方法2:使用 loc 按标签选择行
# loc 是 "location" 的缩写,按标签(索引)选择
# 选择索引为0的行
row_0 = df.loc[0]
print(row_0)
# 选择索引0到2的行(包含2)
rows_0_to_2 = df.loc[0:2]
print(rows_0_to_2)
# 方法3:同时选择行和列
# 选择前2行的姓名和年龄列
subset = df.loc[0:1, ['姓名', '年龄']]
print(subset)
# 输出:
# 姓名 年龄
# 0 张三 25
# 1 李四 30
# 使用 iloc 也可以
subset2 = df.iloc[0:2, [0, 1]]
print(subset2)7.3 条件筛选 #
条件筛选是数据分析的核心功能,可以根据条件选择满足要求的数据。
# 继续使用上面的 df
# 简单条件筛选:选择年龄大于25的员工
age_filter = df[df['年龄'] > 25]
print(age_filter)
# 输出:年龄大于25的所有行
# 多条件筛选:使用 &(且)和 |(或)
# 注意:每个条件必须用括号括起来
# 选择年龄大于25且工资大于10000的员工
complex_filter = df[(df['年龄'] > 25) & (df['工资'] > 10000)]
print(complex_filter)
# 输出:同时满足两个条件的行
# 选择年龄大于30或工资大于12000的员工
or_filter = df[(df['年龄'] > 30) | (df['工资'] > 12000)]
print(or_filter)
# 输出:满足任一条件的行
# 使用 isin() 方法:选择城市在北京或上海的员工
city_filter = df[df['城市'].isin(['北京', '上海'])]
print(city_filter)
# 输出:城市为北京或上海的所有行
# 使用字符串方法筛选:选择姓名包含"三"的员工
name_filter = df[df['姓名'].str.contains('三')]
print(name_filter)
# 输出:姓名包含"三"的行8. 数据清洗 #
8.1 处理缺失值 #
真实数据中经常会有缺失值(NaN),需要处理才能进行分析。
# 导入必要的库
import pandas as pd
import numpy as np
# 创建包含缺失值的示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六', '钱七'],
'年龄': [25, 30, np.nan, 28, 32],
'城市': ['北京', '上海', '广州', np.nan, '深圳'],
'工资': [8000, 12000, 15000, np.nan, 9000]
})
print("原始数据:")
print(df)
# 检查缺失值
# isnull() 返回布尔值 DataFrame,True 表示缺失值
print("\n缺失值检查:")
print(df.isnull())
# 输出:每个位置是否为缺失值
# 统计每列的缺失值数量
print("\n每列缺失值数量:")
print(df.isnull().sum())
# 输出:
# 姓名 0
# 年龄 1
# 城市 1
# 工资 1
# dtype: int64
# 检查是否有缺失值(返回 True/False)
print("\n是否有缺失值:")
print(df.isnull().any().any())
# 输出:True
# 处理缺失值的方法1:删除包含缺失值的行
df_dropna = df.dropna()
print("\n删除缺失值后:")
print(df_dropna)
# 输出:只保留没有缺失值的行
# 处理缺失值的方法2:填充缺失值
# 用固定值填充
df_fill_0 = df.fillna(0)
print("\n用0填充后:")
print(df_fill_0)
# 用前一个值填充(前向填充)
df_ffill = df.fillna(method='ffill')
print("\n前向填充后:")
print(df_ffill)
# 用后一个值填充(后向填充)
df_bfill = df.fillna(method='bfill')
print("\n后向填充后:")
print(df_bfill)
# 用列的均值填充(只对数值列有效)
df_fill_mean = df.copy()
df_fill_mean['年龄'].fillna(df_fill_mean['年龄'].mean(), inplace=True)
df_fill_mean['工资'].fillna(df_fill_mean['工资'].mean(), inplace=True)
print("\n用均值填充数值列后:")
print(df_fill_mean)
# 用字典指定不同列的填充值
df_fill_dict = df.fillna({
'年龄': df['年龄'].mean(),
'城市': '未知',
'工资': df['工资'].mean()
})
print("\n用字典指定填充值后:")
print(df_fill_dict)8.2 处理重复值 #
# 导入必要的库
import pandas as pd
# 创建包含重复值的示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '张三', '李四'],
'年龄': [25, 30, 35, 25, 30],
'城市': ['北京', '上海', '广州', '北京', '上海']
})
print("原始数据:")
print(df)
# 检查重复行
print("\n重复行检查:")
print(df.duplicated())
# 输出:每行是否为重复行(True/False)
# 查看重复的行
print("\n重复的行:")
print(df[df.duplicated()])
# 输出:所有重复的行
# 删除重复行(保留第一次出现的)
df_no_dup = df.drop_duplicates()
print("\n删除重复行后:")
print(df_no_dup)
# 根据特定列判断重复
# 只根据'姓名'列判断重复
df_no_dup_name = df.drop_duplicates(subset=['姓名'])
print("\n根据姓名删除重复后:")
print(df_no_dup_name)
# 保留最后一次出现的重复行
df_keep_last = df.drop_duplicates(keep='last')
print("\n保留最后一次出现的重复行:")
print(df_keep_last)8.3 数据类型转换 #
# 导入必要的库
import pandas as pd
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': ['25', '30', '35'], # 字符串类型
'工资': ['8000', '12000', '15000'] # 字符串类型
})
print("原始数据类型:")
print(df.dtypes)
# 转换单列数据类型
# 将年龄列转换为整数
df['年龄'] = df['年龄'].astype('int')
print("\n转换年龄列后:")
print(df.dtypes)
# 将工资列转换为浮点数
df['工资'] = df['工资'].astype('float')
print("\n转换工资列后:")
print(df.dtypes)
# 转换日期类型
# 创建包含日期的 DataFrame
df_date = pd.DataFrame({
'日期': ['2023-01-01', '2023-02-01', '2023-03-01']
})
# 转换为日期类型
df_date['日期'] = pd.to_datetime(df_date['日期'])
print("\n日期转换后:")
print(df_date.dtypes)
print(df_date)
# 使用 pd.to_numeric() 转换(更安全,可以处理错误)
df_safe = pd.DataFrame({
'数值': ['1', '2', 'abc', '4']
})
# errors='coerce' 表示遇到错误时转换为 NaN
df_safe['数值'] = pd.to_numeric(df_safe['数值'], errors='coerce')
print("\n安全转换后:")
print(df_safe)9. 数据操作:增删改查 #
9.1 添加列 #
# 导入必要的库
import pandas as pd
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'工资': [8000, 12000, 15000]
})
print("原始数据:")
print(df)
# 方法1:直接赋值添加新列
df['奖金'] = [1000, 2000, 3000]
print("\n添加奖金列后:")
print(df)
# 方法2:基于现有列计算新列
df['总工资'] = df['工资'] + df['奖金']
print("\n添加总工资列后:")
print(df)
# 方法3:使用 assign() 方法(不修改原 DataFrame,返回新 DataFrame)
df_new = df.assign(绩效 = [500, 600, 700])
print("\n使用 assign 添加列后:")
print(df_new)9.2 删除列 #
# 继续使用上面的 df
# 方法1:使用 drop() 方法(不修改原 DataFrame,返回新 DataFrame)
df_dropped = df.drop('奖金', axis=1)
print("删除奖金列后:")
print(df_dropped)
# 方法2:使用 drop() 并设置 inplace=True(直接修改原 DataFrame)
df.drop('总工资', axis=1, inplace=True)
print("\n直接删除总工资列后:")
print(df)
# 删除多列
df_dropped_multi = df.drop(['奖金', '总工资'], axis=1)
print("\n删除多列后:")
print(df_dropped_multi)9.3 添加行 #
# 继续使用上面的 df
# 方法1:使用 append() 方法(已弃用,但可以用)
# 创建新行数据
new_row = pd.Series({
'姓名': '赵六',
'年龄': 28,
'工资': 10000
})
# 添加到 DataFrame
df = df.append(new_row, ignore_index=True)
print("添加新行后:")
print(df)
# 方法2:使用 pd.concat() 方法(推荐)
# 创建新的 DataFrame
new_df = pd.DataFrame({
'姓名': ['钱七'],
'年龄': [32],
'工资': [11000]
})
# 合并
df = pd.concat([df, new_df], ignore_index=True)
print("\n使用 concat 添加行后:")
print(df)9.4 删除行 #
# 继续使用上面的 df
# 删除指定索引的行
df_dropped_row = df.drop(0) # 删除第一行
print("删除第一行后:")
print(df_dropped_row)
# 删除多行
df_dropped_rows = df.drop([0, 1]) # 删除第1行和第2行
print("\n删除多行后:")
print(df_dropped_rows)
# 根据条件删除行
df_filtered = df[df['年龄'] > 30] # 保留年龄大于30的行
print("\n根据条件筛选后:")
print(df_filtered)9.5 修改数据 #
# 继续使用上面的 df
# 修改单个值
df.loc[0, '工资'] = 9000
print("修改单个值后:")
print(df)
# 修改整列
df['工资'] = df['工资'] * 1.1 # 工资增加10%
print("\n修改整列后:")
print(df)
# 根据条件修改
df.loc[df['年龄'] > 30, '工资'] = df.loc[df['年龄'] > 30, '工资'] * 1.2
print("\n根据条件修改后:")
print(df)9.6 重命名 #
# 继续使用上面的 df
# 重命名列
df_renamed = df.rename(columns={'姓名': '名字', '年龄': '岁数'})
print("重命名列后:")
print(df_renamed)
# 重命名索引
df_renamed_index = df.rename(index={0: '第一行', 1: '第二行'})
print("\n重命名索引后:")
print(df_renamed_index)9.7 排序 #
# 继续使用上面的 df
# 按单列排序(默认升序)
df_sorted = df.sort_values('年龄')
print("按年龄升序排序:")
print(df_sorted)
# 按单列降序排序
df_sorted_desc = df.sort_values('年龄', ascending=False)
print("\n按年龄降序排序:")
print(df_sorted_desc)
# 按多列排序
df_sorted_multi = df.sort_values(['城市', '年龄'])
print("\n按城市和年龄排序:")
print(df_sorted_multi)
# 按索引排序
df_sorted_index = df.sort_index()
print("\n按索引排序:")
print(df_sorted_index)10. 数据分组与聚合 #
10.1 基本分组操作 #
分组是数据分析中非常重要的操作,可以将数据按照某个标准分组,然后对每组进行统计分析。
# 导入必要的库
import pandas as pd
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八'],
'部门': ['销售', '技术', '技术', '销售', '技术', '销售'],
'城市': ['北京', '上海', '北京', '上海', '广州', '北京'],
'工资': [8000, 12000, 15000, 10000, 13000, 9000],
'年龄': [25, 30, 35, 28, 32, 27]
})
print("原始数据:")
print(df)
# 基本分组:按部门分组
grouped = df.groupby('部门')
print("\n分组对象:")
print(grouped)
# 输出:<pandas.core.groupby.generic.DataFrameGroupBy object at ...>
# 查看每个组
print("\n每个组的数据:")
for name, group in grouped:
print(f"\n{name} 部门:")
print(group)
# 对分组后的数据进行聚合操作
# 计算每个部门的平均工资
avg_salary = df.groupby('部门')['工资'].mean()
print("\n每个部门的平均工资:")
print(avg_salary)
# 输出:
# 部门
# 技术 13333.333333
# 销售 9000.000000
# Name: 工资, dtype: float64
# 计算每个部门的工资总和
sum_salary = df.groupby('部门')['工资'].sum()
print("\n每个部门的工资总和:")
print(sum_salary)
# 多重分组:按部门和城市分组
multi_grouped = df.groupby(['部门', '城市'])
print("\n多重分组后的平均工资:")
print(multi_grouped['工资'].mean())10.2 聚合函数 #
# 继续使用上面的 df
# 使用 agg() 方法进行多种聚合操作
# 对工资列计算多个统计量
agg_result = df.groupby('部门')['工资'].agg(['mean', 'sum', 'min', 'max', 'count'])
print("每个部门工资的多种统计:")
print(agg_result)
# 输出:
# mean sum min max count
# 部门
# 技术 13333.33 40000 12000 15000 3
# 销售 9000.00 27000 8000 10000 3
# 对不同列使用不同的聚合函数
agg_multi = df.groupby('部门').agg({
'工资': ['mean', 'sum'],
'年龄': 'mean'
})
print("\n对不同列使用不同聚合函数:")
print(agg_multi)
# 自定义聚合函数
def salary_range(series):
"""计算工资范围"""
return series.max() - series.min()
custom_agg = df.groupby('部门')['工资'].agg(salary_range)
print("\n自定义聚合函数(工资范围):")
print(custom_agg)10.3 数据透视表 #
数据透视表是 Excel 中常用的功能,Pandas 也提供了类似的功能。
# 继续使用上面的 df
# 创建数据透视表
# 以部门为行,城市为列,工资为值,计算平均值
pivot_table = pd.pivot_table(
df,
values='工资', # 要聚合的列
index='部门', # 行索引
columns='城市', # 列索引
aggfunc='mean' # 聚合函数
)
print("数据透视表:")
print(pivot_table)
# 输出:
# 城市 北京 上海 广州
# 部门
# 技术 15000.0 12000.0 13000.0
# 销售 8500.0 10000.0 NaN
# 如果某个组合没有数据,会显示 NaN
# 可以指定 fill_value 填充缺失值
pivot_table_filled = pd.pivot_table(
df,
values='工资',
index='部门',
columns='城市',
aggfunc='mean',
fill_value=0 # 用0填充缺失值
)
print("\n填充缺失值后的透视表:")
print(pivot_table_filled)11. 数据合并与连接 #
11.1 使用 concat 合并 #
concat 用于简单地将多个 DataFrame 合并在一起。
# 导入必要的库
import pandas as pd
# 创建两个示例 DataFrame
df1 = pd.DataFrame({
'姓名': ['张三', '李四'],
'年龄': [25, 30],
'城市': ['北京', '上海']
})
df2 = pd.DataFrame({
'姓名': ['王五', '赵六'],
'年龄': [35, 28],
'城市': ['广州', '深圳']
})
print("df1:")
print(df1)
print("\ndf2:")
print(df2)
# 纵向合并(上下合并,增加行)
df_vertical = pd.concat([df1, df2], ignore_index=True)
print("\n纵向合并后:")
print(df_vertical)
# 输出:4行数据
# 横向合并(左右合并,增加列)
df3 = pd.DataFrame({
'工资': [8000, 12000],
'部门': ['销售', '技术']
})
df_horizontal = pd.concat([df1, df3], axis=1)
print("\n横向合并后:")
print(df_horizontal)
# 输出:2行,5列11.2 使用 merge 连接 #
merge 类似于 SQL 中的 JOIN 操作,根据共同的列连接两个 DataFrame。
# 导入必要的库
import pandas as pd
# 创建两个示例 DataFrame
# 员工信息表
df_employees = pd.DataFrame({
'员工ID': [1, 2, 3, 4],
'姓名': ['张三', '李四', '王五', '赵六'],
'部门ID': [101, 102, 101, 103]
})
# 部门信息表
df_departments = pd.DataFrame({
'部门ID': [101, 102, 103],
'部门名称': ['销售部', '技术部', '财务部']
})
print("员工表:")
print(df_employees)
print("\n部门表:")
print(df_departments)
# 内连接(inner join):只保留两个表中都有的记录
df_inner = pd.merge(df_employees, df_departments, on='部门ID', how='inner')
print("\n内连接结果:")
print(df_inner)
# 输出:只包含有对应部门的员工
# 左连接(left join):保留左表的所有记录
df_left = pd.merge(df_employees, df_departments, on='部门ID', how='left')
print("\n左连接结果:")
print(df_left)
# 输出:包含所有员工,没有部门的显示 NaN
# 右连接(right join):保留右表的所有记录
df_right = pd.merge(df_employees, df_departments, on='部门ID', how='right')
print("\n右连接结果:")
print(df_right)
# 输出:包含所有部门,没有员工的显示 NaN
# 外连接(outer join):保留两个表的所有记录
df_outer = pd.merge(df_employees, df_departments, on='部门ID', how='outer')
print("\n外连接结果:")
print(df_outer)
# 输出:包含所有员工和所有部门
# 如果连接键的列名不同,使用 left_on 和 right_on
df_employees2 = pd.DataFrame({
'员工ID': [1, 2, 3],
'姓名': ['张三', '李四', '王五'],
'部门编号': [101, 102, 101] # 注意列名不同
})
df_merge_diff = pd.merge(
df_employees2,
df_departments,
left_on='部门编号',
right_on='部门ID',
how='inner'
)
print("\n不同列名连接:")
print(df_merge_diff)12. 常用函数与技巧 #
12.1 统计函数 #
# 导入必要的库
import pandas as pd
import numpy as np
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 15000, 10000]
})
print("原始数据:")
print(df)
# 基本统计函数(只对数值列有效)
print("\n平均值:")
print(df.mean())
# 输出:每列的平均值
print("\n总和:")
print(df.sum())
# 输出:每列的总和
print("\n最大值:")
print(df.max())
# 输出:每列的最大值
print("\n最小值:")
print(df.min())
# 输出:每列的最小值
print("\n标准差:")
print(df.std())
# 输出:每列的标准差
print("\n计数:")
print(df.count())
# 输出:每列的非空值数量
# 对单列使用统计函数
print("\n年龄列的平均值:")
print(df['年龄'].mean())
# 输出:29.5
print("\n工资列的总和:")
print(df['工资'].sum())
# 输出:4500012.2 字符串操作 #
Pandas 提供了强大的字符串操作方法,可以方便地处理文本数据。
# 导入必要的库
import pandas as pd
# 创建包含字符串的示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'邮箱': ['zhangsan@example.com', 'lisi@test.com', 'wangwu@demo.com', 'zhaoliu@sample.com'],
'地址': ['北京市朝阳区', '上海市浦东新区', '广州市天河区', '深圳市南山区']
})
print("原始数据:")
print(df)
# 字符串转大写
df['姓名大写'] = df['姓名'].str.upper()
print("\n姓名转大写:")
print(df[['姓名', '姓名大写']])
# 字符串转小写
df['邮箱小写'] = df['邮箱'].str.lower()
print("\n邮箱转小写:")
print(df[['邮箱', '邮箱小写']])
# 检查字符串是否包含某个子串
df['包含北京'] = df['地址'].str.contains('北京')
print("\n检查是否包含'北京':")
print(df[['地址', '包含北京']])
# 字符串替换
df['地址替换'] = df['地址'].str.replace('区', '区(已处理)')
print("\n字符串替换:")
print(df[['地址', '地址替换']])
# 提取字符串的一部分
df['城市'] = df['地址'].str[:2] # 提取前2个字符
print("\n提取城市:")
print(df[['地址', '城市']])
# 字符串长度
df['地址长度'] = df['地址'].str.len()
print("\n地址长度:")
print(df[['地址', '地址长度']])12.3 应用函数(apply) #
apply 方法可以对 DataFrame 的行或列应用自定义函数。
# 导入必要的库
import pandas as pd
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'工资': [8000, 12000, 15000]
})
print("原始数据:")
print(df)
# 对单列应用函数
# 使用 lambda 函数:工资增加10%
df['新工资'] = df['工资'].apply(lambda x: x * 1.1)
print("\n工资增加10%:")
print(df[['姓名', '工资', '新工资']])
# 定义自定义函数
def categorize_age(age):
"""根据年龄分类"""
if age < 30:
return '年轻'
elif age < 35:
return '中年'
else:
return '资深'
# 应用自定义函数
df['年龄分类'] = df['年龄'].apply(categorize_age)
print("\n年龄分类:")
print(df[['姓名', '年龄', '年龄分类']])
# 对整行应用函数
def calculate_total(row):
"""计算总薪资(假设有奖金)"""
return row['工资'] * 1.2
df['总薪资'] = df.apply(calculate_total, axis=1)
print("\n计算总薪资:")
print(df[['姓名', '工资', '总薪资']])
# axis=1 表示对每一行应用函数
# axis=0 表示对每一列应用函数12.4 条件判断(where) #
# 导入必要的库
import pandas as pd
import numpy as np
# 创建示例 DataFrame
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 15000, 10000]
})
print("原始数据:")
print(df)
# 使用 np.where() 进行条件判断
# 语法:np.where(条件, 满足条件的值, 不满足条件的值)
df['工资等级'] = np.where(df['工资'] > 10000, '高', '低')
print("\n工资等级:")
print(df[['姓名', '工资', '工资等级']])
# 多个条件
df['年龄组'] = np.where(
df['年龄'] < 30,
'年轻',
np.where(df['年龄'] < 35, '中年', '资深')
)
print("\n年龄组:")
print(df[['姓名', '年龄', '年龄组']])13. 综合实战案例 #
13.1 案例:员工数据分析 #
让我们通过一个完整的案例来综合运用前面学到的知识。
# 导入必要的库
import pandas as pd
import numpy as np
# 步骤1:创建模拟数据
# 在实际工作中,这些数据通常来自文件或数据库
np.random.seed(42) # 设置随机种子,确保结果可重复
# 创建员工数据
data = {
'员工ID': range(1, 21),
'姓名': [f'员工{i}' for i in range(1, 21)],
'部门': np.random.choice(['销售', '技术', '财务', '人事'], 20),
'城市': np.random.choice(['北京', '上海', '广州', '深圳'], 20),
'年龄': np.random.randint(22, 45, 20),
'工资': np.random.randint(6000, 20000, 20),
'入职年份': np.random.randint(2018, 2024, 20)
}
df = pd.DataFrame(data)
# 添加一些缺失值(模拟真实数据)
df.loc[2, '工资'] = np.nan
df.loc[5, '城市'] = np.nan
df.loc[8, '年龄'] = np.nan
print("步骤1:原始数据")
print(df.head(10))
print(f"\n数据形状:{df.shape}")
print(f"\n缺失值统计:\n{df.isnull().sum()}")
# 步骤2:数据清洗
print("\n" + "="*50)
print("步骤2:数据清洗")
# 处理缺失值
# 工资用中位数填充
df['工资'].fillna(df['工资'].median(), inplace=True)
# 城市用众数填充
df['城市'].fillna(df['城市'].mode()[0], inplace=True)
# 年龄用均值填充
df['年龄'].fillna(df['年龄'].mean(), inplace=True)
print("处理缺失值后:")
print(f"缺失值统计:\n{df.isnull().sum()}")
# 检查并删除重复值
duplicates = df.duplicated().sum()
print(f"\n重复行数量:{duplicates}")
if duplicates > 0:
df.drop_duplicates(inplace=True)
# 步骤3:数据探索
print("\n" + "="*50)
print("步骤3:数据探索")
# 基本统计信息
print("\n数值列统计描述:")
print(df.describe())
# 各部门人数
print("\n各部门人数:")
print(df['部门'].value_counts())
# 各城市人数
print("\n各城市人数:")
print(df['城市'].value_counts())
# 步骤4:数据分析
print("\n" + "="*50)
print("步骤4:数据分析")
# 各部门平均工资
print("\n各部门平均工资:")
dept_avg_salary = df.groupby('部门')['工资'].mean().sort_values(ascending=False)
print(dept_avg_salary)
# 各城市平均工资
print("\n各城市平均工资:")
city_avg_salary = df.groupby('城市')['工资'].mean().sort_values(ascending=False)
print(city_avg_salary)
# 各部门的详细统计
print("\n各部门详细统计:")
dept_stats = df.groupby('部门').agg({
'工资': ['mean', 'min', 'max', 'count'],
'年龄': 'mean'
})
print(dept_stats)
# 步骤5:数据筛选
print("\n" + "="*50)
print("步骤5:数据筛选")
# 筛选高薪员工(工资 > 15000)
high_salary = df[df['工资'] > 15000]
print(f"\n高薪员工(工资>15000)数量:{len(high_salary)}")
print(high_salary[['姓名', '部门', '工资']])
# 筛选年轻员工(年龄 < 30)
young_employees = df[df['年龄'] < 30]
print(f"\n年轻员工(年龄<30)数量:{len(young_employees)}")
# 复合条件:技术部门且工资 > 12000
tech_high = df[(df['部门'] == '技术') & (df['工资'] > 12000)]
print(f"\n技术部门高薪员工数量:{len(tech_high)}")
print(tech_high[['姓名', '工资']])
# 步骤6:数据操作
print("\n" + "="*50)
print("步骤6:数据操作")
# 添加新列:工资等级
df['工资等级'] = np.where(
df['工资'] < 10000, '低',
np.where(df['工资'] < 15000, '中', '高')
)
# 添加新列:工作年限
df['工作年限'] = 2024 - df['入职年份']
# 添加新列:年薪
df['年薪'] = df['工资'] * 12
print("\n添加新列后的数据:")
print(df[['姓名', '部门', '工资', '工资等级', '工作年限', '年薪']].head())
# 步骤7:数据透视表
print("\n" + "="*50)
print("步骤7:数据透视表")
# 创建透视表:部门 vs 城市,显示平均工资
pivot = pd.pivot_table(
df,
values='工资',
index='部门',
columns='城市',
aggfunc='mean',
fill_value=0
)
print("\n部门 vs 城市 平均工资透视表:")
print(pivot)
# 步骤8:保存结果
print("\n" + "="*50)
print("步骤8:保存结果")
# 保存清洗后的数据
df.to_csv('员工数据_清洗后.csv', index=False, encoding='utf-8-sig')
print("数据已保存到:员工数据_清洗后.csv")
# 保存分析结果
dept_stats.to_csv('部门统计.csv', encoding='utf-8-sig')
print("部门统计已保存到:部门统计.csv")
print("\n" + "="*50)
print("分析完成!")13.2 案例总结 #
这个案例涵盖了:
- 数据创建:创建模拟数据
- 数据清洗:处理缺失值和重复值
- 数据探索:查看基本统计信息
- 数据分析:分组聚合分析
- 数据筛选:条件筛选
- 数据操作:添加新列
- 数据透视:创建透视表
- 数据保存:保存结果
14. 总结 #
- Series 和 DataFrame:Pandas 的两个核心数据结构
- 数据读取与保存:处理各种文件格式
- 数据查看:了解数据的基本情况
- 数据选择:选择需要的行和列
- 数据清洗:处理缺失值和重复值
- 数据操作:增删改查
- 数据分组:分组聚合分析
- 数据合并:合并多个数据源
- 常用函数:统计、字符串、应用函数