Pandas 数据结构 DataFrame
DataFrame 是 Pandas 中最重要的二维数据结构,可以理解为带标签的二维数组或表格。本章将全面介绍 DataFrame 的创建、操作和应用。
📚 DataFrame 概述
什么是 DataFrame
DataFrame 是一个二维的标记数据结构,具有以下特点:
- 行和列都有标签:每行每列都有对应的索引
- 列可以是不同数据类型:整数、浮点数、字符串等
- 大小可变:可以插入和删除列
- 类似电子表格:Excel 或 SQL 表的 Python 版本
DataFrame 的组成
- 数据(values):实际存储的数据
- 行索引(index):行的标签
- 列索引(columns):列的标签
- 数据类型(dtypes):每列的数据类型
🔨 创建 DataFrame
从字典创建
python
import pandas as pd
import numpy as np
# 从字典创建 DataFrame
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'薪资': [8000, 12000, 15000, 9500]
}
df = pd.DataFrame(data)
print(df)
# 输出:
# 姓名 年龄 城市 薪资
# 0 张三 25 北京 8000
# 1 李四 30 上海 12000
# 2 王五 35 广州 15000
# 3 赵六 28 深圳 9500从列表创建
python
# 从二维列表创建
data_list = [
['张三', 25, '北京', 8000],
['李四', 30, '上海', 12000],
['王五', 35, '广州', 15000],
['赵六', 28, '深圳', 9500]
]
df_list = pd.DataFrame(data_list,
columns=['姓名', '年龄', '城市', '薪资'])
print(df_list)从 Series 创建
python
# 从多个 Series 创建
names = pd.Series(['张三', '李四', '王五', '赵六'])
ages = pd.Series([25, 30, 35, 28])
cities = pd.Series(['北京', '上海', '广州', '深圳'])
df_series = pd.DataFrame({
'姓名': names,
'年龄': ages,
'城市': cities
})
print(df_series)从 NumPy 数组创建
python
# 从 NumPy 数组创建
array_data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
df_array = pd.DataFrame(array_data,
columns=['A', 'B', 'C'],
index=['行1', '行2', '行3'])
print(df_array)创建空 DataFrame
python
# 创建空 DataFrame
empty_df = pd.DataFrame()
print(f"空 DataFrame: {empty_df}")
# 创建指定结构的空 DataFrame
structured_empty = pd.DataFrame(columns=['姓名', '年龄', '城市'])
print(structured_empty)从文件创建(预览)
python
# 从 CSV 文件创建(示例)
# df_csv = pd.read_csv('data.csv')
# 从 Excel 文件创建(示例)
# df_excel = pd.read_excel('data.xlsx')
# 从 JSON 创建(示例)
# df_json = pd.read_json('data.json')🔍 DataFrame 属性
基本属性
python
# 创建示例 DataFrame
students = pd.DataFrame({
'学号': ['S001', 'S002', 'S003', 'S004', 'S005'],
'姓名': ['张三', '李四', '王五', '赵六', '钱七'],
'数学': [85, 92, 78, 96, 88],
'英语': [90, 85, 92, 89, 94],
'物理': [88, 90, 85, 92, 87]
})
# 查看基本属性
print(f"形状: {students.shape}") # (5, 5)
print(f"大小: {students.size}") # 25
print(f"维度: {students.ndim}") # 2
print(f"行数: {len(students)}") # 5
print(f"列数: {len(students.columns)}") # 5
print(f"列名: {list(students.columns)}") # ['学号', '姓名', '数学', '英语', '物理']
print(f"索引: {list(students.index)}") # [0, 1, 2, 3, 4]
print(f"数据类型:\n{students.dtypes}") # 各列的数据类型查看数据概览
python
# 查看前几行
print("前3行:")
print(students.head(3))
# 查看后几行
print("\n后2行:")
print(students.tail(2))
# 查看基本信息
print("\n基本信息:")
print(students.info())
# 查看统计摘要
print("\n统计摘要:")
print(students.describe())内存使用
python
# 查看内存使用情况
print("内存使用情况:")
print(students.memory_usage(deep=True))
print(f"\n总内存使用: {students.memory_usage(deep=True).sum()} bytes")🎯 索引和选择
列选择
python
# 选择单列(返回 Series)
math_scores = students['数学']
print("数学成绩:")
print(math_scores)
print(f"类型: {type(math_scores)}")
# 选择多列(返回 DataFrame)
scores = students[['姓名', '数学', '英语']]
print("\n姓名和成绩:")
print(scores)
print(f"类型: {type(scores)}")
# 使用点号访问列(列名不能有空格或特殊字符)
# names = students.姓名 # 可以使用,但不推荐行选择
python
# 使用 iloc 按位置选择行
print("第一行:")
print(students.iloc[0])
print("\n前三行:")
print(students.iloc[0:3])
print("\n最后一行:")
print(students.iloc[-1])
# 使用 loc 按标签选择行
print("\n索引为2的行:")
print(students.loc[2])
# 选择多行
print("\n索引为1和3的行:")
print(students.loc[[1, 3]])条件选择
python
# 布尔索引
high_math = students[students['数学'] > 90]
print("数学成绩大于90的学生:")
print(high_math)
# 多条件选择
excellent = students[(students['数学'] > 85) & (students['英语'] > 90)]
print("\n数学>85且英语>90的学生:")
print(excellent)
# 使用 isin 方法
selected_students = students[students['姓名'].isin(['张三', '王五'])]
print("\n指定学生:")
print(selected_students)
# 使用 query 方法
query_result = students.query('数学 > 85 and 英语 > 88')
print("\n使用 query 查询:")
print(query_result)高级索引
python
# loc:基于标签的索引
print("使用 loc 选择特定行列:")
print(students.loc[1:3, ['姓名', '数学']])
# iloc:基于位置的索引
print("\n使用 iloc 选择特定位置:")
print(students.iloc[1:4, 2:4])
# at 和 iat:快速访问单个值
print(f"\n第2行数学成绩: {students.at[1, '数学']}")
print(f"第2行第3列的值: {students.iat[1, 2]}")🔧 DataFrame 操作
添加列
python
# 添加新列
students_copy = students.copy()
# 计算总分
students_copy['总分'] = students_copy['数学'] + students_copy['英语'] + students_copy['物理']
# 计算平均分
students_copy['平均分'] = students_copy['总分'] / 3
# 添加等级列
def get_grade(avg_score):
if avg_score >= 90:
return 'A'
elif avg_score >= 80:
return 'B'
elif avg_score >= 70:
return 'C'
else:
return 'D'
students_copy['等级'] = students_copy['平均分'].apply(get_grade)
print("添加列后:")
print(students_copy)删除列
python
# 删除列(不修改原 DataFrame)
df_dropped = students_copy.drop(['总分', '平均分'], axis=1)
print("删除列后:")
print(df_dropped)
# 删除列(修改原 DataFrame)
students_copy.drop(['等级'], axis=1, inplace=True)
print("\n原地删除后:")
print(students_copy)
# 使用 del 删除列
del students_copy['总分']
print("\n使用 del 删除后:")
print(students_copy)添加行
python
# 使用 loc 添加行
students_new = students.copy()
students_new.loc[5] = ['S006', '孙八', 89, 91, 86]
print("添加行后:")
print(students_new)
# 使用 concat 添加行
new_student = pd.DataFrame({
'学号': ['S007'],
'姓名': ['周九'],
'数学': [93],
'英语': [88],
'物理': [90]
})
students_concat = pd.concat([students, new_student], ignore_index=True)
print("\n使用 concat 添加行:")
print(students_concat)删除行
python
# 删除指定索引的行
students_drop_row = students.drop([0, 2])
print("删除行后:")
print(students_drop_row)
# 根据条件删除行
students_filtered = students[students['数学'] >= 85]
print("\n保留数学>=85的行:")
print(students_filtered)📊 数据操作和计算
数学运算
python
# 创建数值 DataFrame
scores_df = students[['数学', '英语', '物理']].copy()
# 标量运算
print("所有成绩加5分:")
print(scores_df + 5)
print("\n所有成绩乘以1.1:")
print(scores_df * 1.1)
# 列间运算
print("\n数学和英语成绩之差:")
print(scores_df['数学'] - scores_df['英语'])
# 行间运算
print("\n每个学生的总分:")
print(scores_df.sum(axis=1))
print("\n每科的平均分:")
print(scores_df.mean(axis=0))统计函数
python
# 基本统计
print("基本统计信息:")
print(scores_df.describe())
print(f"\n各科最高分:")
print(scores_df.max())
print(f"\n各科最低分:")
print(scores_df.min())
print(f"\n各科平均分:")
print(scores_df.mean())
print(f"\n各科标准差:")
print(scores_df.std())
# 相关性分析
print("\n科目间相关性:")
print(scores_df.corr())排序
python
# 按单列排序
sorted_by_math = students.sort_values('数学', ascending=False)
print("按数学成绩降序排列:")
print(sorted_by_math)
# 按多列排序
sorted_multi = students.sort_values(['数学', '英语'], ascending=[False, True])
print("\n按数学降序、英语升序排列:")
print(sorted_multi)
# 按索引排序
sorted_by_index = students.sort_index(ascending=False)
print("\n按索引降序排列:")
print(sorted_by_index)🔄 数据处理
缺失值处理
python
# 创建包含缺失值的 DataFrame
data_with_nan = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [np.nan, 2, 3, 4, np.nan],
'C': [1, 2, 3, np.nan, 5],
'D': [1, np.nan, np.nan, 4, 5]
})
print("包含缺失值的数据:")
print(data_with_nan)
# 检测缺失值
print("\n缺失值检测:")
print(data_with_nan.isnull())
print("\n每列缺失值数量:")
print(data_with_nan.isnull().sum())
# 删除缺失值
print("\n删除包含缺失值的行:")
print(data_with_nan.dropna())
print("\n删除包含缺失值的列:")
print(data_with_nan.dropna(axis=1))
# 填充缺失值
print("\n用0填充缺失值:")
print(data_with_nan.fillna(0))
print("\n用平均值填充缺失值:")
print(data_with_nan.fillna(data_with_nan.mean()))
print("\n前向填充:")
print(data_with_nan.fillna(method='ffill'))重复值处理
python
# 创建包含重复值的 DataFrame
data_with_duplicates = pd.DataFrame({
'姓名': ['张三', '李四', '张三', '王五', '李四'],
'年龄': [25, 30, 25, 35, 30],
'城市': ['北京', '上海', '北京', '广州', '上海']
})
print("包含重复值的数据:")
print(data_with_duplicates)
# 检测重复值
print("\n重复行检测:")
print(data_with_duplicates.duplicated())
# 删除重复值
print("\n删除重复行:")
print(data_with_duplicates.drop_duplicates())
# 基于特定列删除重复值
print("\n基于姓名列删除重复:")
print(data_with_duplicates.drop_duplicates(subset=['姓名']))数据类型转换
python
# 创建混合类型数据
mixed_data = pd.DataFrame({
'数字字符串': ['1', '2', '3', '4', '5'],
'浮点数': [1.1, 2.2, 3.3, 4.4, 5.5],
'整数': [1, 2, 3, 4, 5]
})
print("原始数据类型:")
print(mixed_data.dtypes)
# 转换数据类型
mixed_data['数字字符串'] = pd.to_numeric(mixed_data['数字字符串'])
mixed_data['浮点数'] = mixed_data['浮点数'].astype(int)
print("\n转换后的数据类型:")
print(mixed_data.dtypes)
print(mixed_data)🔗 DataFrame 合并和连接
连接 DataFrame
python
# 创建示例数据
df1 = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6]
})
df2 = pd.DataFrame({
'A': [7, 8, 9],
'B': [10, 11, 12]
})
# 垂直连接
vertical_concat = pd.concat([df1, df2])
print("垂直连接:")
print(vertical_concat)
# 重置索引
vertical_reset = pd.concat([df1, df2], ignore_index=True)
print("\n重置索引后:")
print(vertical_reset)
# 水平连接
df3 = pd.DataFrame({
'C': [13, 14, 15],
'D': [16, 17, 18]
})
horizontal_concat = pd.concat([df1, df3], axis=1)
print("\n水平连接:")
print(horizontal_concat)合并 DataFrame
python
# 创建用于合并的数据
employees = pd.DataFrame({
'员工ID': [1, 2, 3, 4],
'姓名': ['张三', '李四', '王五', '赵六'],
'部门ID': [101, 102, 101, 103]
})
departments = pd.DataFrame({
'部门ID': [101, 102, 103],
'部门名称': ['技术部', '销售部', '人事部']
})
print("员工表:")
print(employees)
print("\n部门表:")
print(departments)
# 内连接
inner_merge = pd.merge(employees, departments, on='部门ID')
print("\n内连接结果:")
print(inner_merge)
# 左连接
left_merge = pd.merge(employees, departments, on='部门ID', how='left')
print("\n左连接结果:")
print(left_merge)🎨 实际应用示例
示例1:销售数据分析
python
# 创建销售数据
sales_data = pd.DataFrame({
'日期': pd.date_range('2024-01-01', periods=10, freq='D'),
'产品': ['A', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'A', 'C'],
'销量': [100, 150, 120, 80, 200, 110, 90, 180, 130, 95],
'单价': [10.5, 15.2, 10.5, 8.8, 15.2, 10.5, 8.8, 15.2, 10.5, 8.8],
'销售员': ['张三', '李四', '张三', '王五', '李四', '张三', '王五', '李四', '张三', '王五']
})
# 计算销售额
sales_data['销售额'] = sales_data['销量'] * sales_data['单价']
print("销售数据:")
print(sales_data)
# 按产品分组统计
product_summary = sales_data.groupby('产品').agg({
'销量': 'sum',
'销售额': 'sum',
'单价': 'mean'
}).round(2)
print("\n按产品统计:")
print(product_summary)
# 按销售员统计
salesperson_summary = sales_data.groupby('销售员').agg({
'销量': 'sum',
'销售额': 'sum'
}).sort_values('销售额', ascending=False)
print("\n按销售员统计:")
print(salesperson_summary)
# 找出最佳销售日
best_day = sales_data.loc[sales_data['销售额'].idxmax()]
print(f"\n最佳销售日: {best_day['日期'].strftime('%Y-%m-%d')}, 销售额: {best_day['销售额']}")示例2:学生成绩管理系统
python
# 创建学生成绩数据
student_scores = pd.DataFrame({
'学号': ['S001', 'S002', 'S003', 'S004', 'S005', 'S006'],
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八'],
'班级': ['1班', '1班', '2班', '2班', '1班', '2班'],
'语文': [85, 92, 78, 96, 88, 91],
'数学': [90, 85, 92, 89, 94, 87],
'英语': [88, 90, 85, 92, 87, 89],
'物理': [82, 88, 90, 85, 91, 86],
'化学': [89, 87, 88, 90, 85, 92]
})
print("学生成绩数据:")
print(student_scores)
# 计算总分和平均分
subjects = ['语文', '数学', '英语', '物理', '化学']
student_scores['总分'] = student_scores[subjects].sum(axis=1)
student_scores['平均分'] = student_scores[subjects].mean(axis=1).round(2)
# 计算排名
student_scores['排名'] = student_scores['总分'].rank(ascending=False, method='min').astype(int)
print("\n添加统计信息后:")
print(student_scores[['姓名', '班级', '总分', '平均分', '排名']])
# 班级统计
class_summary = student_scores.groupby('班级').agg({
'总分': ['mean', 'max', 'min'],
'平均分': 'mean'
}).round(2)
print("\n班级统计:")
print(class_summary)
# 科目统计
subject_stats = student_scores[subjects].describe().round(2)
print("\n各科目统计:")
print(subject_stats)
# 找出各科最高分学生
print("\n各科最高分学生:")
for subject in subjects:
max_idx = student_scores[subject].idxmax()
max_student = student_scores.loc[max_idx]
print(f"{subject}: {max_student['姓名']} ({max_student[subject]}分)")示例3:财务数据分析
python
# 创建财务数据
financial_data = pd.DataFrame({
'月份': pd.date_range('2024-01-01', periods=12, freq='M'),
'收入': [120000, 135000, 142000, 158000, 163000, 171000,
185000, 192000, 178000, 165000, 155000, 148000],
'支出': [80000, 85000, 90000, 95000, 98000, 102000,
108000, 115000, 110000, 105000, 98000, 92000],
'员工数': [50, 52, 55, 58, 60, 62, 65, 68, 66, 64, 62, 60]
})
# 计算利润和利润率
financial_data['利润'] = financial_data['收入'] - financial_data['支出']
financial_data['利润率'] = (financial_data['利润'] / financial_data['收入'] * 100).round(2)
# 计算人均收入
financial_data['人均收入'] = (financial_data['收入'] / financial_data['员工数']).round(0)
print("财务数据:")
print(financial_data)
# 季度统计
financial_data['季度'] = financial_data['月份'].dt.quarter
quarterly_summary = financial_data.groupby('季度').agg({
'收入': 'sum',
'支出': 'sum',
'利润': 'sum',
'员工数': 'mean'
}).round(0)
quarterly_summary['季度利润率'] = (quarterly_summary['利润'] / quarterly_summary['收入'] * 100).round(2)
print("\n季度统计:")
print(quarterly_summary)
# 年度总结
annual_summary = {
'年度总收入': financial_data['收入'].sum(),
'年度总支出': financial_data['支出'].sum(),
'年度总利润': financial_data['利润'].sum(),
'平均月利润率': financial_data['利润率'].mean(),
'最高月利润': financial_data['利润'].max(),
'最低月利润': financial_data['利润'].min()
}
print("\n年度总结:")
for key, value in annual_summary.items():
if '率' in key:
print(f"{key}: {value:.2f}%")
else:
print(f"{key}: {value:,.0f}")📈 性能优化技巧
向量化操作
python
import time
# 创建大型 DataFrame
large_df = pd.DataFrame({
'A': np.random.randn(100000),
'B': np.random.randn(100000),
'C': np.random.randn(100000)
})
# 比较循环和向量化操作的性能
# 方法1:循环(慢)
start_time = time.time()
result1 = []
for i in range(len(large_df)):
if large_df.iloc[i]['A'] > 0:
result1.append(large_df.iloc[i]['B'] * large_df.iloc[i]['C'])
else:
result1.append(0)
loop_time = time.time() - start_time
# 方法2:向量化(快)
start_time = time.time()
result2 = np.where(large_df['A'] > 0, large_df['B'] * large_df['C'], 0)
vectorized_time = time.time() - start_time
print(f"循环方法耗时: {loop_time:.4f} 秒")
print(f"向量化方法耗时: {vectorized_time:.4f} 秒")
print(f"性能提升: {loop_time/vectorized_time:.1f} 倍")内存优化
python
# 数据类型优化
original_df = pd.DataFrame({
'整数列': [1, 2, 3, 4, 5] * 1000,
'浮点列': [1.1, 2.2, 3.3, 4.4, 5.5] * 1000,
'分类列': ['A', 'B', 'C'] * 1667
})
print(f"原始内存使用: {original_df.memory_usage(deep=True).sum()} bytes")
# 优化数据类型
optimized_df = original_df.copy()
optimized_df['整数列'] = optimized_df['整数列'].astype('int8')
optimized_df['浮点列'] = optimized_df['浮点列'].astype('float32')
optimized_df['分类列'] = optimized_df['分类列'].astype('category')
print(f"优化后内存使用: {optimized_df.memory_usage(deep=True).sum()} bytes")
print(f"内存节省: {(1 - optimized_df.memory_usage(deep=True).sum()/original_df.memory_usage(deep=True).sum())*100:.1f}%")📝 本章小结
通过本章学习,您应该已经掌握:
✅ DataFrame 基础概念:理解 DataFrame 的结构和特点
✅ 创建 DataFrame:掌握多种创建 DataFrame 的方法
✅ 索引和选择:熟练使用各种索引和选择方式
✅ 数据操作:进行增删改查等基本操作
✅ 数据处理:处理缺失值、重复值等数据质量问题
✅ 数据合并:连接和合并多个 DataFrame
✅ 实际应用:解决真实的数据分析问题
✅ 性能优化:提高代码执行效率和内存使用
关键要点
- DataFrame 是 Pandas 的核心:掌握 DataFrame 是数据分析的基础
- 索引的灵活性:loc、iloc、布尔索引等提供了强大的数据选择能力
- 向量化操作的重要性:避免循环,使用 Pandas 内置方法
- 数据质量管理:及时处理缺失值和重复值
- 内存优化:选择合适的数据类型可以显著节省内存
下一步
现在您已经掌握了 Pandas 的核心数据结构,接下来将学习如何处理 CSV 和 Excel 文件。