DataFrame 数据结构详解
目录
- [什么是 DataFrame](#什么是 DataFrame)
- [创建 DataFrame](#创建 DataFrame)
- [DataFrame 的基本属性](#DataFrame 的基本属性)
- 查看和检查数据
- [DataFrame 的索引和列](#DataFrame 的索引和列)
- [DataFrame 的基本操作](#DataFrame 的基本操作)
- 选择和筛选数据
- 添加和删除列
- 添加和删除行
- 数据类型和转换
- 实际应用示例
什么是 DataFrame
DataFrame 是 Pandas 中最重要的数据结构,它是一个二维的、大小可变、可以包含异构数据的表格型数据结构。
DataFrame 的特点:
- 二维表格:有行和列
- 行索引(Index):每一行的标识
- 列索引(Columns):每一列的名称
- 数据类型:每一列可以有不同的数据类型
- 灵活:可以从多种数据源创建
DataFrame 类似于:
- Excel 电子表格
- SQL 表
- R 的 data.frame
创建 DataFrame
1. 从字典创建(最常用)
python
import pandas as pd
# 字典的键作为列名
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
print(df)
# 输出:
# 姓名 年龄 城市 工资
# 0 张三 25 北京 8000
# 1 李四 30 上海 12000
# 2 王五 35 广州 10000
# 3 赵六 28 深圳 11000
# 指定索引
df = pd.DataFrame(data, index=['emp1', 'emp2', 'emp3', 'emp4'])
print(df)
2. 从列表的列表创建
python
import pandas as pd
# 二维列表
data = [
['张三', 25, '北京', 8000],
['李四', 30, '上海', 12000],
['王五', 35, '广州', 10000],
['赵六', 28, '深圳', 11000]
]
# 需要指定列名
df = pd.DataFrame(data, columns=['姓名', '年龄', '城市', '工资'])
print(df)
# 指定索引
df = pd.DataFrame(data,
columns=['姓名', '年龄', '城市', '工资'],
index=['a', 'b', 'c', 'd'])
print(df)
3. 从字典列表创建
python
import pandas as pd
# 每个字典代表一行数据
data = [
{'姓名': '张三', '年龄': 25, '城市': '北京', '工资': 8000},
{'姓名': '李四', '年龄': 30, '城市': '上海', '工资': 12000},
{'姓名': '王五', '年龄': 35, '城市': '广州', '工资': 10000},
{'姓名': '赵六', '年龄': 28, '城市': '深圳', '工资': 11000}
]
df = pd.DataFrame(data)
print(df)
4. 从 NumPy 数组创建
python
import pandas as pd
import numpy as np
# 创建 NumPy 数组
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
df = pd.DataFrame(arr, columns=['A', 'B', 'C'], index=['x', 'y', 'z'])
print(df)
# 输出:
# A B C
# x 1 2 3
# y 4 5 6
# z 7 8 9
5. 从 Series 创建
python
import pandas as pd
# 多个 Series 组合成 DataFrame
s1 = pd.Series(['张三', '李四', '王五'], name='姓名')
s2 = pd.Series([25, 30, 35], name='年龄')
s3 = pd.Series(['北京', '上海', '广州'], name='城市')
df = pd.DataFrame({'姓名': s1, '年龄': s2, '城市': s3})
print(df)
6. 创建空 DataFrame
python
import pandas as pd
# 空 DataFrame
df_empty = pd.DataFrame()
print(df_empty)
# 指定列名的空 DataFrame
df_empty = pd.DataFrame(columns=['姓名', '年龄', '城市'])
print(df_empty)
# 添加数据
df_empty.loc[0] = ['张三', 25, '北京']
print(df_empty)
DataFrame 的基本属性
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 查看索引
print(df.index)
# 输出:RangeIndex(start=0, stop=4, step=1)
# 查看列名
print(df.columns)
# 输出:Index(['姓名', '年龄', '城市', '工资'], dtype='object')
# 查看数据值(返回 NumPy 数组)
print(df.values)
# 输出:
# [['张三' 25 '北京' 8000]
# ['李四' 30 '上海' 12000]
# ['王五' 35 '广州' 10000]
# ['赵六' 28 '深圳' 11000]]
# 查看形状(行数,列数)
print(df.shape)
# 输出:(4, 4)
# 查看大小(总元素数)
print(df.size)
# 输出:16
# 查看维度
print(df.ndim)
# 输出:2
# 查看数据类型
print(df.dtypes)
# 输出:
# 姓名 object
# 年龄 int64
# 城市 object
# 工资 int64
# dtype: object
# 查看是否为空
print(df.empty)
# 输出:False
# 查看内存使用情况
print(df.memory_usage())
查看和检查数据
1. 查看前几行和后几行
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六', '孙七'],
'年龄': [25, 30, 35, 28, 32],
'工资': [8000, 12000, 10000, 11000, 9500]
}
df = pd.DataFrame(data)
# 查看前 n 行(默认 5 行)
print(df.head())
print(df.head(3)) # 查看前 3 行
# 查看后 n 行(默认 5 行)
print(df.tail())
print(df.tail(2)) # 查看后 2 行
# 查看随机几行
print(df.sample(2))
2. 查看基本信息
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 查看信息摘要
df.info()
# 输出:
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 4 entries, 0 to 3
# Data columns (total 4 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 姓名 4 non-null object
# 1 年龄 4 non-null int64
# 2 城市 4 non-null object
# 3 工资 4 non-null int64
# dtypes: int64(2), object(2)
# memory usage: 256.0+ bytes
# 查看统计信息(仅数值列)
print(df.describe())
# 输出:
# 年龄 工资
# count 4.000000 4.000000
# mean 29.500000 10250.000000
# std 4.203173 1789.350849
# min 25.000000 8000.000000
# 25% 27.250000 9250.000000
# 50% 29.000000 10500.000000
# 75% 31.750000 11250.000000
# max 35.000000 12000.000000
# 包含所有列的统计信息
print(df.describe(include='all'))
3. 检查缺失值
python
import pandas as pd
import numpy as np
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, np.nan, 28],
'城市': ['北京', '上海', '广州', np.nan],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 检查缺失值(返回布尔 DataFrame)
print(df.isna())
# 输出:
# 姓名 年龄 城市 工资
# 0 False False False False
# 1 False False False False
# 2 False True False False
# 3 False False True False
# 统计每列的缺失值数量
print(df.isna().sum())
# 输出:
# 姓名 0
# 年龄 1
# 城市 1
# 工资 0
# dtype: int64
# 检查非缺失值
print(df.notna())
# 检查是否有缺失值
print(df.isna().any().any()) # True
DataFrame 的索引和列
1. 设置和重置索引
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'工资': [8000, 12000, 10000]
}
df = pd.DataFrame(data)
# 设置索引
df_indexed = df.set_index('姓名')
print(df_indexed)
# 输出:
# 年龄 工资
# 姓名
# 张三 25 8000
# 李四 30 12000
# 王五 35 10000
# 重置索引
df_reset = df_indexed.reset_index()
print(df_reset)
# 使用多列作为索引
df_multi = df.set_index(['姓名', '年龄'])
print(df_multi)
2. 重命名列和索引
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'工资': [8000, 12000, 10000]
}
df = pd.DataFrame(data)
# 重命名列
df_renamed = df.rename(columns={'姓名': 'name', '年龄': 'age', '工资': 'salary'})
print(df_renamed)
# 重命名索引
df_index_renamed = df.rename(index={0: 'a', 1: 'b', 2: 'c'})
print(df_index_renamed)
# 重命名所有列
df.columns = ['Name', 'Age', 'Salary']
print(df)
选择和筛选数据
1. 选择列
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 选择单列(返回 Series)
col1 = df['姓名']
print(col1)
# 输出:
# 0 张三
# 1 李四
# 2 王五
# 3 赵六
# Name: 姓名, dtype: object
# 选择单列(返回 DataFrame)
col1_df = df[['姓名']]
print(col1_df)
# 选择多列
cols = df[['姓名', '工资']]
print(cols)
# 使用点号访问(列名必须是有效的 Python 标识符)
col2 = df.年龄
print(col2)
2. 选择行
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 使用 iloc(位置索引)
row1 = df.iloc[0] # 第一行
rows = df.iloc[0:2] # 前两行
row_last = df.iloc[-1] # 最后一行
# 使用 loc(标签索引)
row_by_label = df.loc[0] # 索引为 0 的行
rows_range = df.loc[0:2] # 索引 0 到 2(包含结束位置)
# 选择特定位置的值
value = df.iloc[0, 1] # 第 0 行,第 1 列
value2 = df.loc[0, '年龄'] # 索引 0,列名 '年龄'
3. 条件筛选
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 单条件筛选
filtered1 = df[df['年龄'] > 30]
print(filtered1)
# 输出:
# 姓名 年龄 城市 工资
# 2 王五 35 广州 10000
# 多条件筛选(使用 & 和 |)
filtered2 = df[(df['年龄'] > 25) & (df['工资'] > 10000)]
print(filtered2)
filtered3 = df[(df['城市'] == '北京') | (df['城市'] == '上海')]
print(filtered3)
# 使用 isin 方法
filtered4 = df[df['城市'].isin(['北京', '上海'])]
print(filtered4)
# 字符串包含筛选
filtered5 = df[df['姓名'].str.contains('三')]
print(filtered5)
4. 使用 query 方法
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 使用 query 方法(类似 SQL)
result = df.query('年龄 > 30')
print(result)
result2 = df.query('年龄 > 25 and 工资 > 10000')
print(result2)
# 使用变量
age_threshold = 30
result3 = df.query('年龄 > @age_threshold')
print(result3)
添加和删除列
1. 添加列
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'工资': [8000, 12000, 10000]
}
df = pd.DataFrame(data)
# 方法1:直接赋值
df['奖金'] = [1000, 2000, 1500]
print(df)
# 方法2:使用 assign 方法
df = df.assign(总工资=df['工资'] + df.get('奖金', 0))
print(df)
# 方法3:基于现有列计算
df['年薪'] = df['工资'] * 12
print(df)
# 方法4:插入列到指定位置
df.insert(1, '部门', ['销售', '技术', '销售'])
print(df)
2. 删除列
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'工资': [8000, 12000, 10000],
'奖金': [1000, 2000, 1500]
}
df = pd.DataFrame(data)
# 方法1:使用 drop(返回新 DataFrame)
df_new = df.drop('奖金', axis=1)
print(df_new)
# 方法2:修改原 DataFrame
df.drop('奖金', axis=1, inplace=True)
print(df)
# 删除多列
df.drop(['年龄', '工资'], axis=1, inplace=True)
print(df)
# 使用 del 关键字
df['临时列'] = [1, 2, 3]
del df['临时列']
print(df)
添加和删除行
1. 添加行
python
import pandas as pd
data = {
'姓名': ['张三', '李四'],
'年龄': [25, 30],
'工资': [8000, 12000]
}
df = pd.DataFrame(data)
# 方法1:使用 loc
df.loc[2] = ['王五', 35, 10000]
print(df)
# 方法2:使用 append(已弃用,推荐使用 concat)
new_row = pd.DataFrame({'姓名': ['赵六'], '年龄': [28], '工资': [11000]})
df = pd.concat([df, new_row], ignore_index=True)
print(df)
# 方法3:使用字典
df.loc[len(df)] = {'姓名': '孙七', '年龄': 32, '工资': 9500}
print(df)
2. 删除行
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 10000, 11000]
}
df = pd.DataFrame(data)
# 删除指定索引的行
df_new = df.drop(0) # 删除索引为 0 的行
print(df_new)
# 删除多行
df_new = df.drop([0, 2])
print(df_new)
# 修改原 DataFrame
df.drop(0, inplace=True)
print(df)
# 根据条件删除
df = df[df['年龄'] < 35] # 保留年龄小于 35 的行
print(df)
数据类型和转换
python
import pandas as pd
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': ['25', '30', '35'], # 字符串类型
'工资': [8000.5, 12000.3, 10000.7] # 浮点数
}
df = pd.DataFrame(data)
# 查看数据类型
print(df.dtypes)
# 转换单列数据类型
df['年龄'] = df['年龄'].astype(int)
print(df.dtypes)
# 转换多列数据类型
df = df.astype({'年龄': int, '工资': int})
print(df.dtypes)
# 转换为字符串
df['工资'] = df['工资'].astype(str)
# 转换为日期
df['日期'] = ['2024-01-01', '2024-01-02', '2024-01-03']
df['日期'] = pd.to_datetime(df['日期'])
print(df.dtypes)
# 转换为类别类型(节省内存)
df['姓名'] = df['姓名'].astype('category')
print(df.dtypes)
实际应用示例
示例 1:员工信息管理
python
import pandas as pd
# 创建员工数据
employees = pd.DataFrame({
'员工ID': ['E001', 'E002', 'E003', 'E004'],
'姓名': ['张三', '李四', '王五', '赵六'],
'部门': ['销售', '技术', '销售', '技术'],
'年龄': [25, 30, 35, 28],
'工资': [8000, 12000, 10000, 11000],
'入职日期': ['2020-01-01', '2019-06-15', '2018-03-20', '2021-09-10']
})
# 转换日期类型
employees['入职日期'] = pd.to_datetime(employees['入职日期'])
# 添加工作年限列
from datetime import datetime
employees['工作年限'] = (datetime.now() - employees['入职日期']).dt.days // 365
# 添加绩效评级列
employees['绩效评级'] = employees['工资'].apply(
lambda x: 'A' if x >= 12000 else ('B' if x >= 10000 else 'C')
)
# 筛选销售部门
sales_dept = employees[employees['部门'] == '销售']
print("销售部门员工:")
print(sales_dept)
# 按工资排序
employees_sorted = employees.sort_values('工资', ascending=False)
print("\n按工资排序:")
print(employees_sorted)
# 部门统计
dept_stats = employees.groupby('部门').agg({
'工资': 'mean',
'年龄': 'mean',
'员工ID': 'count'
}).rename(columns={'员工ID': '人数'})
print("\n部门统计:")
print(dept_stats)
示例 2:销售数据分析
python
import pandas as pd
# 创建销售数据
sales = pd.DataFrame({
'日期': pd.date_range('2024-01-01', periods=10, freq='D'),
'产品': ['A', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'A', 'C'],
'销售额': [1000, 1500, 1200, 800, 1800, 1100, 900, 1600, 1300, 850],
'数量': [10, 15, 12, 8, 18, 11, 9, 16, 13, 8.5]
})
# 添加单价列
sales['单价'] = sales['销售额'] / sales['数量']
# 按产品分组统计
product_stats = sales.groupby('产品').agg({
'销售额': ['sum', 'mean', 'count'],
'数量': 'sum'
})
print("产品统计:")
print(product_stats)
# 每日销售额
daily_sales = sales.groupby('日期')['销售额'].sum()
print("\n每日销售额:")
print(daily_sales)
# 找出销售额最高的日期
max_date = sales.loc[sales['销售额'].idxmax(), '日期']
print(f"\n销售额最高的日期: {max_date}")
# 计算累计销售额
sales['累计销售额'] = sales['销售额'].cumsum()
print("\n累计销售额:")
print(sales[['日期', '销售额', '累计销售额']])
示例 3:学生成绩分析
python
import pandas as pd
import numpy as np
# 创建学生成绩数据
np.random.seed(42)
scores = pd.DataFrame({
'学号': [f'S{i:03d}' for i in range(1, 21)],
'姓名': [f'学生{i}' for i in range(1, 21)],
'数学': np.random.randint(60, 101, 20),
'英语': np.random.randint(60, 101, 20),
'物理': np.random.randint(60, 101, 20),
'化学': np.random.randint(60, 101, 20)
})
# 计算总分和平均分
scores['总分'] = scores[['数学', '英语', '物理', '化学']].sum(axis=1)
scores['平均分'] = scores[['数学', '英语', '物理', '化学']].mean(axis=1)
# 添加等级
def get_grade(avg):
if avg >= 90:
return '优秀'
elif avg >= 80:
return '良好'
elif avg >= 70:
return '中等'
elif avg >= 60:
return '及格'
else:
return '不及格'
scores['等级'] = scores['平均分'].apply(get_grade)
# 统计各等级人数
grade_count = scores['等级'].value_counts()
print("等级分布:")
print(grade_count)
# 找出各科最高分
print("\n各科最高分:")
for subject in ['数学', '英语', '物理', '化学']:
max_idx = scores[subject].idxmax()
print(f"{subject}: {scores.loc[max_idx, subject]} ({scores.loc[max_idx, '姓名']})")
# 找出总分前 5 名
top5 = scores.nlargest(5, '总分')[['姓名', '总分', '平均分', '等级']]
print("\n总分前 5 名:")
print(top5)
总结
DataFrame 是 Pandas 的核心数据结构,掌握其使用方法至关重要:
- 创建方式多样:可以从字典、列表、NumPy 数组等多种方式创建
- 灵活的选择方法 :使用
loc、iloc、条件筛选等 - 丰富的操作:添加/删除行列、数据类型转换等
- 强大的功能:数据查看、统计、筛选等