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 原生结构可能更易控制。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论