文章目录
DataFrame是Pandas库中最核心的数据结构,它以一种表格形式组织数据,类似于 Excel 电子表格或 SQL 数据库表。这种二维数据结构特别适合处理结构化数据。
本文介绍了Pandas中DataFrame的核心功能,包括创建、基本操作(查看、选择、过滤、修改)、常用功能(统计、缺失值处理、排序、分组聚合、应用自定义函数、数据结构转换)、合并与分割方法等。
一、DataFrame主要构成
- 行(Rows):每行代表一个独立的观测值或数据记录,例如一个人的完整信息
- 列(Columns):每列代表一个特定的变量或特征,如"年龄"、"城市"等
- 索引(Index):类似于行的身份证号,用于快速定位和识别特定行
二、创建DataFrame
1. 从字典创建
从字典创建最直观,字典的键自动成为列名,值成为对应列的数据。适合数据已经按列组织好的情况。
import pandas as pd
data = {
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35],
'City': ['New York', 'Paris', 'London']
}
df = pd.DataFrame(data)
2. 从列表创建
当数据是按行组织的记录时,这种方法更合适。需要显式指定列名,否则会自动生成数字列名(0,1,2…)。
data = [
['Alice', 25, 'New York'], # 第一个人的完整信息
['Bob', 30, 'Paris'], # 第二个人的完整信息
['Charlie', 35, 'London'] # 第三个人的完整信息
]
# 将列表转换为DataFrame,并指定列名
df = pd.DataFrame(data, columns=['Name', 'Age', 'City'])
三、DataFrame基本操作
1. 查看数据
以下方法可以在不显示全部数据的情况下,快速了解DataFrame的概况。
df.head() # 查看前5行
df.tail(3) # 查看后3行
df.shape # 查看维度(行数,列数)
df.columns # 查看列名
df.dtypes # 查看每列数据类型
2. 选择数据
df['Name'] # 选择单列,返回Series对象
df[['Name', 'Age']] # 选择多列,返回新的DataFrame
df.loc[0] # 按标签选择第一行所有数据
df.iloc[0] # 按位置(索引)选择第一行所有数据
- loc 基于标签选择,适合已知行/列名的情况
- iloc 基于位置选择,适合按数字索引访问
3. 过滤数据
布尔索引是DataFrame的筛选功能,可以使用复杂条件组合来精确选择所需数据。
df[df['Age'] > 30] # 年龄大于30的记录
df[(df['Age'] > 25) & (df['City'] == 'Paris')] # 多条件筛选:年龄>25且城市为Paris
4. 修改数据
DataFrame的列操作可以像操作普通变量一样修改列数据。
df['Age'] = df['Age'] + 1 # 年龄列全部加1
df['New_Column'] = range(3) # 添加新列
四、DataFrame常用功能
1. 描述性统计
使用统计方法能快速了解数据的分布情况和基本特征。
df.info() # 快速查看DataFrame的摘要信息,包括列名、非空值数量、数据类型等
df.describe() # 对数值列计算统计量(计数、均值、标准差等)
df.mean(numeric_only=True) # 计算各数值列的均值
df['Age'].value_counts() # 统计年龄列中每个值的出现次数
df.describe()
默认情况下只对数值列计算统计量,需要会对于非数值列计算,可以使用 include='all'
参数。
- 对于数值列:计算计数(count)、均值(mean)、标准差(std)、最小值(min)、四分位数(25%、50%、75%)和最大值(max)
- 对于非数值列:计算计数(count)、唯一值数量(unique)、出现频率最高的值(top)及其出现频率(freq)
参数
df.describe(
percentiles=None, # 指定要计算的分位数
include=None, # 指定包含的数据类型
exclude=None, # 指定排除的数据类型
datetime_is_numeric=False # 是否将datetime视为数值型
)
基本示例
import pandas as pd
import numpy as np
# 创建示例DataFrame
df = pd.DataFrame({
'数值列': np.random.randn(100),
'分类列': np.random.choice(['A', 'B', 'C'], 100),
'缺失列': np.random.choice([1, 2, np.nan], 100)
})
# 默认describe
print(df.describe())
控制输出内容
# 只计算特定分位数
df.describe(percentiles=[0.1, 0.5, 0.9])
# 只包含数值列
df.describe(include=[np.number])
# 包含所有列
df.describe(include='all')
# 排除数值列
df.describe(exclude=[np.number])
df.mean()
df.mean()
可以对 DataFrame 或 Series 中的数值数据计算算术平均值。
参数
df.mean(
axis=0, # 计算方向:0-列方向,1-行方向
skipna=True, # 是否跳过NaN值
numeric_only=True, # 是否只计算数值列
**kwargs # 其他参数
)
对DataFrame使用
import pandas as pd
df = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [1.5, 2.5, 3.5, 4.5],
'C': ['a', 'b', 'c', 'd'] # 非数值列
})
# 计算各列的平均值
print(df.mean())
对Series使用
s = pd.Series([10, 20, 30, 40])
print(s.mean()) # 输出: 25.0
常见场景
# 计算每列的平均值
column_means = df.mean() # 默认axis=0
# 计算每行的平均值
row_means = df.mean(axis=1)
# 只计算数值列(推荐方式)
df.mean(numeric_only=True)
df_with_nan = pd.DataFrame({'A': [1, 2, np.nan, 4]})
# 跳过NaN计算
df_with_nan.mean() # 输出A列平均值:(1+2+4)/3 ≈ 2.333
# 包含NaN计算
df_with_nan.mean(skipna=False) # 输出NaN
2. 处理缺失值
处理缺失值是数据清洗的重要步骤,根据情况选择删除或填充。
df.dropna() # 删除含缺失值的整行
df.fillna(0) # 用0填充缺失值
df.isna().sum() # 统计每列的缺失值数量
3. 排序
排序可以快速发现数据的极值等等。
df.sort_values('Age', ascending=False) # 按年龄降序排列
4. 分组聚合
分组聚合可以计算各组的统计量。
print(df.groupby('City')['Age'].mean()) # 按城市分组计算平均年龄
5. 应用自定义函数
对DataFrame的行或列应用自定义函数。
df['Age'].apply(lambda x: x * 2) # 对Age列的值进行加倍
6. 数据结构转换
DataFrame转列表
# 返回所有数据(不含列名)的二维列表。
data = df.values.tolist()
# 提取某一列的数据,转换为列表。
names = df['Name'].tolist()
DataFrame转字典
# 默认返回 {列名: {索引: 值}} 的嵌套字典结构。
dict_data = df.to_dict()
# 返回 [{列名: 值}, ...] 格式的字典列表,适合 JSON 转换。
dict_records = df.to_dict('records')
DataFrame转NumPy数组
# 返回 DataFrame 的 NumPy 数组形式(不包含列名)。
numpy_array = df.to_numpy()
DataFrame转JSON
# 将 DataFrame 转换为 JSON 字符串。
json_str = df.to_json()
# 指定格式(orient 参数)
json_records = df.to_json(orient='records')
五、DataFrame合并
1. concat连接合并
concat是最简单的合并方法,适合结构相同的数据集简单拼接。
import pandas as pd
# 创建示例DataFrame
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index=[0, 1, 2])
df2 = pd.DataFrame({'A': ['A3', 'A4', 'A5'],
'B': ['B3', 'B4', 'B5']},
index=[3, 4, 5])
# 纵向连接(默认axis=0)
result = pd.concat([df1, df2])
# 横向连接
result_h = pd.concat([df1, df2], axis=1)
2. merge键值合并
merge是基于列的合并,类似于SQL的JOIN操作,根据键值合并相关数据。
left = pd.DataFrame({'key': ['K0', 'K1', 'K2'],
'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
# 内连接(默认)
result = pd.merge(left, right, on='key')
# 左连接
result_left = pd.merge(left, right, how='left', on='key')
# 右连接
result_right = pd.merge(left, right, how='right', on='key')
# 外连接
result_outer = pd.merge(left, right, how='outer', on='key')
3. join索引合并
join是基于索引的合并方法,适合索引有意义的场景。
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index=['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C1', 'C2'],
'D': ['D0', 'D1', 'D2']},
index=['K0', 'K1', 'K3'])
# 左连接(默认)
result = left.join(right)
# 右连接
result_right = left.join(right, how='right')
# 内连接
result_inner = left.join(right, how='inner')
# 外连接
result_outer = left.join(right, how='outer')
4. 多键合并
当需要多个字段共同确定关联关系时,可以使用多键合并。
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
result = pd.merge(left, right, on=['key1', 'key2'])
5. 合并时处理重复列名
当两个DataFrame有相同列名但不是连接键时,添加后缀避免冲突。
# 合并时对重复列名添加后缀区分
result = pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
6. 合并时添加指示列
indicator列会显示每行是来自"left_only"、“right_only"还是"both”,便于追踪合并结果。
# 合并后添加一列指示每行数据的来源
result = pd.merge(left, right, on='key1', how='outer', indicator=True)
六、DataFrame分割
1. 按行分割
行分割常用于将大数据集分成多个部分处理,或提取符合条件的子集。
# 创建示例DataFrame
df = pd.DataFrame({'A': range(10),
'B': range(10, 20),
'C': range(20, 30)})
# 取前3行
df_head = df.head(3)
# 取后3行
df_tail = df.tail(3)
# 按位置分割
df_part1 = df.iloc[:5] # 前5行
df_part2 = df.iloc[5:] # 第6行到最后
# 按条件分割
df_filtered = df[df['A'] > 5] # A列大于5的行
2. 按列分割
列分割常用于特征选择,提取感兴趣的变量或特定类型的数据。
# 选择单列(返回Series)
col_a = df['A']
# 选择多列(返回DataFrame)
cols_ab = df[['A', 'B']]
# 按数据类型选择:只选择数值型列
numeric_cols = df.select_dtypes(include=['number'])
3. 随机分割
随机分割常用于创建训练集和测试集,或进行随机抽样分析。
# 随机抽取30%的数据
df_sample = df.sample(frac=0.3)
# 随机抽取固定数量(5行)
df_sample_fixed = df.sample(n=5)
4. 分组后分割
分组后分割可以让我们分别处理每个组的数据。
# 创建分组对象
grouped = df.groupby('A')
# 获取特定组
group_5 = grouped.get_group(5)
# 遍历所有组
for name, group in grouped:
print(f"Group {name}:")
print(group)
七、DataFrame与原生数据结构比较
DataFrame适合场景
- 结构化数据处理(表格、CSV、SQL 表等):天然适合处理行-列结构的数据,更直观
- 数据清洗与转换:内置方法丰富,处理缺失值、数据归一化、分类聚合等
- 数据分析与统计计算:内置 mean()、sum()、describe() 等方法,便捷。
- 大数据处理:底层基于 NumPy/C,运算速度比纯 Python list 快(特别是百万级数据)。
- 数据可视化支持:可直接对接 Matplotlib、Seaborn 等库。
- 数据库/文件交互:支持直接读取/写入 CSV、Excel、SQL、JSON、Parquet 等格式,比手动解析更简单。
原生数据结构适合场景
- 非结构化数据(如嵌套 JSON、树形数据):如果数据不是表格形式,dict 或 list 更灵活。
- 小规模数据:如果数据量极小,DataFrame 可能显得“杀鸡用牛刀”。
- 需要低内存开销:list 和 dict 的内存占用通常比 DataFrame 更小(但牺牲了计算速度)。
- 需要定制化操作:Python 原生结构可能更易控制。