Pandas 数据处理实战:DataFrame 核心操作与综合案例

在上一篇文章中,我们掌握了 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 提供concatmerge两种方式,适用场景不同。

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)。

分析目标

  1. 合并人口数据与州简称数据,删除重复列。
  2. 处理缺失值,补全州全称。
  3. 合并州面积数据,计算 2010 年各州人口密度(人口 / 面积)。
  4. 排序并找出人口密度最高的 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 的核心二维结构,其知识点可归纳为:

  1. 创建:支持 NumPy 数组、字典、外部文件,实战中以文件读取为主。
  2. 访问:列用[],行用loc/iloc,交叉定位用loc[行,列]
  3. 清洗:缺失值用fillna/dropna,去重用drop_duplicates,排序用sort_index/sort_values
  4. 合并:无关联键用concat,有关联键用merge,按需选择。
  5. 实战:通过 “数据读取→合并→清洗→计算→排序” 的流程,可解决大部分结构化数据分析问题。

掌握 DataFrame 后,你将能轻松应对日常数据处理需求,为后续的数据分析和可视化打下坚实基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值