Skip to content

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
实际应用:解决真实的数据分析问题
性能优化:提高代码执行效率和内存使用

关键要点

  1. DataFrame 是 Pandas 的核心:掌握 DataFrame 是数据分析的基础
  2. 索引的灵活性:loc、iloc、布尔索引等提供了强大的数据选择能力
  3. 向量化操作的重要性:避免循环,使用 Pandas 内置方法
  4. 数据质量管理:及时处理缺失值和重复值
  5. 内存优化:选择合适的数据类型可以显著节省内存

下一步

现在您已经掌握了 Pandas 的核心数据结构,接下来将学习如何处理 CSV 和 Excel 文件。


下一章:Pandas CSV与Excel处理