在上一篇文章中,我们掌握了 Pandas 一维数据结构 Series 的使用。本文将聚焦 Pandas 的二维核心 ——DataFrame,从概念、创建、索引切片、函数应用,到数据清洗(缺失值、去重)、数据合并(concat/merge),最后通过一个 “美国各州人口密度分析” 的综合案例,帮你打通 DataFrame 的实战应用思路。
一、DataFrame 是什么?核心概念解析
DataFrame 是 Pandas 中用于存储二维表格数据的结构,可理解为 “多个 Series 共用一个索引” 的集合,其特点如下:
- 结构:包含行索引(index)、列索引(columns)和数据(values),类似 Excel 表格或 SQL 表。
- 灵活性:每列数据类型可不同(如一列整数、一列字符串、一列浮点数)。
- 用途:适用于存储结构化数据(如用户信息表、销售数据表),是 Pandas 数据处理的核心载体。
二、DataFrame 的创建方式
1. 基于 NumPy 数组创建
通过pd.DataFrame()
函数,传入 NumPy 二维数组,并指定行索引(index
)和列索引(columns
)。
import pandas as pd
import numpy as np
# 生成3行5列的随机数组(值在0-1之间)
arr = np.random.rand(3, 5)
# 创建DataFrame
df = pd.DataFrame(
arr,
index=['行1', '行2', '行3'], # 行索引
columns=['列1', '列2', '列3', '列4', '列5'] # 列索引
)
print("基于NumPy数组创建的DataFrame:")
print(df)
2. 基于字典创建
字典的 “键” 作为列索引,“值”(需为等长序列)作为列数据,适用于已有列标签 - 数据映射的场景。
import pandas as pd
# 定义字典(键:列名,值:列数据)
data_dict = {
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']
}
# 创建DataFrame
df = pd.DataFrame(data_dict, index=['用户1', '用户2', '用户3'])
print("基于字典创建的DataFrame:")
print(df)
3. 读取外部文件创建(实战常用)
实际工作中,DataFrame 多从 Excel、CSV 等外部文件读取,Pandas 提供了read_excel()
、read_csv()
等便捷函数。
import pandas as pd
# 读取CSV文件(encoding需根据文件编码调整,如utf-8、gbk)
df_csv = pd.read_csv('data.csv', encoding='utf-8')
# 读取Excel文件(指定工作表sheet_name)
df_excel = pd.read_excel('data.xlsx', sheet_name='Sheet1')
print("读取CSV文件的前5行:")
print(df_csv.head()) # head()默认显示前5行
三、DataFrame 索引与切片:行、列访问技巧
DataFrame 的索引切片需区分 “行” 和 “列”,核心规则如下:
- 直接用
[]
:默认访问列(传入列名)。 - 访问行:需用
loc
(显式索引)或iloc
(隐式索引)。
1. 访问列数据
- 单个列:
df['列名']
或df.列名
(列名无特殊字符时),返回 Series。 - 多个列:
df[['列1', '列2']]
(传入列名列表),返回 DataFrame。
import pandas as pd
import numpy as np
# 创建示例DataFrame
df = pd.DataFrame(
np.random.rand(3, 5),
index=['a', 'b', 'c'],
columns=['one', 'two', 'three', 'four', 'five']
)
# 1. 访问单个列
print("访问'one'列:")
print(df['one']) # 或 df.one,返回Series
# 2. 访问多个列
print("\n访问'one'和'three'列:")
print(df[['one', 'three']]) # 返回DataFrame
2. 访问行数据
- 显式索引(
loc
):支持标签索引,切片前闭后闭。 - 隐式索引(
iloc
):支持整数索引,切片前闭后开。
import pandas as pd
import numpy as np
df = pd.DataFrame(
np.random.rand(3, 5),
index=['a', 'b', 'c'],
columns=['one', 'two', 'three', 'four', 'five']
)
# 1. 显式索引访问行
print("显式索引访问行'a':")
print(df.loc['a']) # 返回Series(行数据)
print("\n显式切片'a':'b'(前闭后闭):")
print(df.loc['a':'b']) # 返回DataFrame(包含a、b两行)
# 2. 隐式索引访问行
print("\n隐式索引访问行0:")
print(df.iloc[0]) # 返回Series
print("\n隐式切片0:2(前闭后开):")
print(df.iloc[0:2]) # 返回DataFrame(包含0、1两行)
3. 访问指定行与列(交叉定位)
通过loc[行索引, 列索引]
或iloc[行索引, 列索引]
,精准定位单个或多个数据。
import pandas as pd
import numpy as np
df = pd.DataFrame(
np.random.rand(3, 5),
index=['a', 'b', 'c'],
columns=['one', 'two', 'three', 'four', 'five']
)
# 1. 显式定位:行'a'、列'three'
print("行'a'列'three'的值:", df.loc['a', 'three'])
# 2. 显式切片:行'a':'b'、列'two':'four'
print("\n行'a':'b'、列'two':'four'的数据:")
print(df.loc['a':'b', 'two':'four'])
# 3. 隐式定位:行1、列2
print("\n行1列2的值:", df.iloc[1, 2])
四、DataFrame 函数应用:批量处理数据
Pandas 支持将函数应用到 DataFrame 的行、列或每个元素,常用方式有 3 种:
1. 直接使用 NumPy 函数
NumPy 的通用函数(如np.abs()
、np.mean()
)可直接作用于 DataFrame,实现批量计算。
import pandas as pd
import numpy as np
# 创建包含负数的DataFrame
df = pd.DataFrame(np.random.randn(5, 4) - 1) # randn生成正态分布数据,减1使数据包含负数
print("原始DataFrame:")
print(df)
# 应用NumPy函数(计算绝对值)
df_abs = np.abs(df)
print("\n计算绝对值后的DataFrame:")
print(df_abs)
2. apply ():作用于行或列
apply()
默认作用于列(axis=0
),通过axis=1
可指定作用于行,常用于自定义统计(如求每行 / 列的最大值、平均值)。
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5, 4) - 1)
# 1. 作用于列(默认axis=0):求每列的最大值
col_max = df.apply(lambda x: x.max())
print("每列的最大值:")
print(col_max)
# 2. 作用于行(axis=1):求每行的平均值
row_mean = df.apply(lambda x: x.mean(), axis=1)
print("\n每行的平均值:")
print(row_mean)
3. applymap ():作用于每个元素
applymap()
仅作用于 DataFrame 的每个元素,适用于格式统一(如保留小数位)、值转换等场景。
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5, 4) - 1)
# 定义函数:保留2位小数
format_func = lambda x: '%.2f' % x
# 应用到每个元素
df_formatted = df.applymap(format_func)
print("保留2位小数后的DataFrame:")
print(df_formatted)
五、数据清洗:缺失值、去重与排序
数据清洗是分析前的关键步骤,Pandas 提供了简洁的 API 处理常见问题。
1. 缺失值处理(NaN)
缺失值的处理流程:判断缺失值→填充 / 删除缺失值。
import pandas as pd
import numpy as np
# 创建含缺失值的DataFrame
df = pd.DataFrame([
np.random.randn(3),
[1., 2., np.nan],
[np.nan, 4., np.nan],
[1., 2., 3.]
], columns=['A', 'B', 'C'])
print("含缺失值的DataFrame:")
print(df)
# 1. 判断缺失值(isnull())
print("\n缺失值位置(True表示缺失):")
print(df.isnull())
# 2. 填充缺失值(fillna()):用0填充所有NaN
df_filled = df.fillna(0)
print("\n用0填充缺失值后的DataFrame:")
print(df_filled)
# 3. 删除缺失值(dropna()):
# axis=0:删除含缺失值的行(默认);axis=1:删除含缺失值的列
df_dropped_row = df.dropna(axis=0) # 删除行
df_dropped_col = df.dropna(axis=1) # 删除列
print("\n删除含缺失值的行:")
print(df_dropped_row)
2. 去重(duplicated () 与 drop_duplicates ())
duplicated()
:判断每行是否重复(返回布尔值)。drop_duplicates()
:删除重复行(默认保留第一行)。
import pandas as pd
# 创建含重复行的DataFrame
df = pd.DataFrame({
'A': [1, 1, 2, 2],
'B': ['x', 'x', 'y', 'z']
})
print("含重复行的DataFrame:")
print(df)
# 1. 判断重复行
print("\n重复行标记(True表示重复):")
print(df.duplicated())
# 2. 删除重复行
df_unique = df.drop_duplicates()
print("\n删除重复行后的DataFrame:")
print(df_unique)
3. 排序(sort_index () 与 sort_values ())
- 按索引排序:
sort_index()
,ascending=False
表示降序(默认升序)。 - 按值排序:
sort_values(by='列名')
,按指定列的值排序。
import pandas as pd
import numpy as np
df = pd.DataFrame(
np.random.randint(0, 10, (3, 3)),
index=['c', 'a', 'b'],
columns=['B', 'A', 'C']
)
print("原始DataFrame:")
print(df)
# 1. 按行索引排序(升序)
df_sort_index = df.sort_index()
print("\n按行索引升序排序:")
print(df_sort_index)
# 2. 按列值排序(按'A'列降序)
df_sort_values = df.sort_values(by='A', ascending=False)
print("\n按'A'列降序排序:")
print(df_sort_values)
六、数据合并:concat 与 merge 的区别
数据合并是多表分析的核心,Pandas 提供concat
和merge
两种方式,适用场景不同。
1. concat:轴向拼接(无关联键)
concat
按行(axis=0
)或列(axis=1
)拼接数据,无需关联键,仅需指定拼接方向和连接方式(join='outer'
保留所有列,join='inner'
仅保留共同列)。
import pandas as pd
# 创建两个DataFrame
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'B': [5, 6], 'C': [7, 8]})
# 1. 按列拼接(axis=1),保留所有列(outer)
df_concat_col = pd.concat([df1, df2], axis=1, join='outer')
print("按列拼接(保留所有列):")
print(df_concat_col)
# 2. 按行拼接(axis=0),仅保留共同列(inner)
df_concat_row = pd.concat([df1, df2], axis=0, join='inner')
print("\n按行拼接(仅保留共同列):")
print(df_concat_row)
2. merge:按关联键合并(类似 SQL JOIN)
merge
需指定关联键(如共同列名),实现类似 SQL 的内连接、外连接等,适用于多表间有逻辑关联的场景(如 “用户表” 与 “订单表” 按 “用户 ID” 合并)。
import pandas as pd
# 创建两个有共同列('手机型号')的DataFrame
df2 = pd.DataFrame({
'手机型号': ['iPhone 13', '华为Mate 50', '小米12'],
'销量': [5000, 3000, 4000]
})
df3 = pd.DataFrame({
'手机型号': ['iPhone 13', '华为Mate 50', 'OPPO Find X5'],
'价格': [5999, 4999, 3999]
})
# 1. 按关联键'手机型号'外连接(保留所有数据)
df_merge_outer = pd.merge(df2, df3, on='手机型号', how='outer')
print("按'手机型号'外连接:")
print(df_merge_outer)
# 2. 按关联键'手机型号'内连接(仅保留共同数据)
df_merge_inner = pd.merge(df2, df3, on='手机型号', how='inner')
print("\n按'手机型号'内连接:")
print(df_merge_inner)
七、综合实战:美国各州人口密度分析
下面通过一个完整案例,串联 DataFrame 的创建、合并、缺失值处理、计算、排序等操作,实现 “美国各州人口密度分析”。
数据说明
我们使用 3 个 CSV 文件:
state-abbrevs.csv
:州全称(state)与简称(abbreviation)映射。state-areas.csv
:州全称(state)与面积(area (sq. mi))。state-population.csv
:州简称(state/region)、年龄分组(ages)、年份(year)、人口数(population)。
分析目标
- 合并人口数据与州简称数据,删除重复列。
- 处理缺失值,补全州全称。
- 合并州面积数据,计算 2010 年各州人口密度(人口 / 面积)。
- 排序并找出人口密度最高的 5 个州。
完整代码与步骤
import pandas as pd
# 1. 读取数据
abb = pd.read_csv("state-abbrevs.csv") # 州全称-简称映射
areas = pd.read_csv("state-areas.csv") # 州-面积数据
pop = pd.read_csv("state-population.csv") # 州-人口数据
# 2. 合并人口数据与州简称数据(按“简称”关联)
abb_pop = pd.merge(
abb, pop,
how="outer",
left_on="abbreviation", # 左表关联键(州简称)
right_on="state/region" # 右表关联键(州简称)
)
# 3. 删除重复的“abbreviation”列
abb_pop.drop(labels="abbreviation", axis=1, inplace=True)
# 4. 处理缺失值:补全州全称(state列)
# 4.1 找出state列缺失的行,提取对应的州简称
null_state = abb_pop["state"].isnull()
missing_regions = abb_pop.loc[null_state, "state/region"].unique() # 缺失的州简称:['USA', 'PR']
# 4.2 补全缺失值(USA→United States,PR→Puerto Rico)
abb_pop.loc[abb_pop["state/region"] == "USA", "state"] = "United States"
abb_pop.loc[abb_pop["state/region"] == "PR", "state"] = "Puerto Rico"
# 5. 合并州面积数据
abb_pop_areas = pd.merge(abb_pop, areas, how="outer", on="state")
# 6. 删除面积列(area (sq. mi))缺失的行
area_null_index = abb_pop_areas.loc[abb_pop_areas["area (sq. mi)"].isnull()].index
abb_pop_areas.drop(index=area_null_index, inplace=True)
# 7. 筛选2010年全民人口数据(ages='total',year=2010)
pop_2010_total = abb_pop_areas.query('ages == "total" & year == 2010')
# 8. 计算人口密度(人口数/面积)
pop_2010_total["density"] = pop_2010_total["population"] / pop_2010_total["area (sq. mi)"]
# 9. 按人口密度降序排序,取前5个州
top5_density = pop_2010_total.sort_values(by="density", ascending=False).head(5)
print("2010年美国人口密度最高的5个州:")
print(top5_density[["state", "population", "area (sq. mi)", "density"]])
结果解释
运行代码后,会输出 2010 年美国人口密度最高的 5 个州,包含州全称、人口数、面积和人口密度,直观反映各州的人口分布紧凑程度。
八、总结
DataFrame 作为 Pandas 的核心二维结构,其知识点可归纳为:
- 创建:支持 NumPy 数组、字典、外部文件,实战中以文件读取为主。
- 访问:列用
[]
,行用loc/iloc
,交叉定位用loc[行,列]
。 - 清洗:缺失值用
fillna/dropna
,去重用drop_duplicates
,排序用sort_index/sort_values
。 - 合并:无关联键用
concat
,有关联键用merge
,按需选择。 - 实战:通过 “数据读取→合并→清洗→计算→排序” 的流程,可解决大部分结构化数据分析问题。
掌握 DataFrame 后,你将能轻松应对日常数据处理需求,为后续的数据分析和可视化打下坚实基础。