杰瑞科技汇

如何高效操作Python DataFrame行?

Python DataFrame 行操作终极指南:从选取、筛选到高效修改(2025最新版)

** 在Python数据处理的世界里,Pandas DataFrame 的“行操作”是绕不开的核心技能,无论是数据清洗、特征工程还是探索性分析,我们几乎每天都要对DataFrame的行进行各种操作,本文将为你系统梳理、深度解析DataFrame行的所有关键操作,从基础的选取、筛选,到高效的条件修改,再到性能优化的进阶技巧,助你从“会”到“精”,彻底掌握数据处理的行云流水。


引言:为什么“行操作”是Pandas的灵魂?

作为一名数据科学家或分析师,你的工作流程往往始于一个杂乱无章的数据集,而你的第一个任务,通常是“看一眼”数据,挑出”你关心的部分,或者“剔除”不相关的信息,这个过程,本质上就是对DataFrame的“行”进行操作。

想象一下,你有一个包含数万条用户行为记录的DataFrame,你需要:

  • 找出所有来自北京的、在过去30天内活跃过的用户。
  • 删除那些信息不全或明显是异常值的记录。
  • 为满足特定条件的用户,打上一个“高价值客户”的标签。

所有这些任务,都离不开对DataFrame行的精准操控,如果你还在为如何高效地操作行而烦恼,或者想系统性地巩固你的知识,那么这篇文章就是为你准备的,让我们开始这段“行”云流水的旅程。


第一部分:行选取 - 精准定位你想要的数据

选取行是所有行操作的基础,Pandas提供了多种方式,从简单到复杂,满足不同场景的需求。

基于标签的选取:.loc

.loc 是基于行标签(index)列标签(column name)进行索引的利器,它的语法是 df.loc[row_indexer, column_indexer]

场景: 当你知道数据的索引或列名时,这是最直观、最安全的方式。

import pandas as pd
import numpy as np
# 创建一个示例DataFrame
data = {'name': ['Alice', 'Bob', 'Charlie', 'David'],
        'city': ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen'],
        'age': [25, 30, 35, 28]}
df = pd.DataFrame(data, index=['user_01', 'user_02', 'user_03', 'user_04'])
# 选取单行
print("选取 user_02 的信息:")
print(df.loc['user_02'])
# 选取多行
print("\n选取 user_01 和 user_03 的信息:")
print(df.loc[['user_01', 'user_03']])
# 选取多行多列
print("\n选取 user_01 和 user_03 的 name 和 city 信息:")
print(df.loc[['user_01', 'user_03'], ['name', 'city']])

专家提示: .loc 是一个“闭区间”,即包含你指定的起始和结束标签,这是与 .iloc(稍后介绍)最大的区别。

基于整数位置的选取:.iloc

.iloc 是基于整数位置进行索引的,就像一个Python列表,从0开始计数,它的语法是 df.iloc[row_position, column_position]

场景: 当你不知道具体标签,或者在进行循环、动态生成索引时,.iloc 非常有用。

# 选取第一行
print("选取第一行:")
print(df.iloc[0])
# 选取前两行
print("\n选取前两行:")
print(df.iloc[:2])
# 选取第一行和第三行的第一列和第三列
print("\n选取特定位置的行和列:")
print(df.iloc[[0, 2], [0, 2]])

专家提示: .iloc 是一个“半开区间”,即包含起始位置,不包含结束位置(df.iloc[0:2] 只包含第0行和第1行)。

快速选取单行:.at.iat

如果你只需要获取或设置单个值,使用 .at(基于标签)和 .iat(基于整数位置)会比 .loc.iloc 更快,因为它们的底层实现更轻量。

# 使用 .at 获取单个值
print("使用 .at 获取 user_02 的年龄:")
print(df.at['user_02', 'age'])
# 使用 .iat 获取单个值
print("\n使用 .iat 获取第一行的年龄:")
print(df.iat[0, 2])

第二部分:行筛选 - 筛选有价值的“金矿”数据

筛选是数据分析中最频繁的操作,它基于一个或多个条件,从DataFrame中“过滤”出符合要求的行。

布尔索引 - 核心中的核心

这是Pandas最强大、最常用的筛选方式,通过生成一个由 TrueFalse 组成的布尔Series,来筛选出对应 True 的行。

# 筛选年龄大于30的用户
print("年龄大于30的用户:")
print(df[df['age'] > 30])
# 筛选来自北京或上海的用户
print("\n来自北京或上海的用户:")
print(df[df['city'].isin(['Beijing', 'Shanghai'])])
# 筛选年龄在25到30之间(包含)的用户
print("\n年龄在25到30之间的用户:")
print(df[(df['age'] >= 25) & (df['age'] <= 30)])
# 筛选年龄大于30 OR 来自北京的用户
# 注意:使用 | (OR) 时,每个条件都要用括号括起来
print("\n年龄大于30或来自北京的用户:")
print(df[(df['age'] > 30) | (df['city'] == 'Beijing')])

专家提示:

  • & 代表逻辑与, 代表逻辑或, 代表逻辑非。
  • 每个条件都必须用括号 包围起来,因为Python的运算符优先级问题。
  • .isin() 方法是判断某个值是否在列表中的高效方式,比 多个 组合更清晰、性能更好。

使用 query() 方法 - 更优雅的语法

当你的筛选条件非常复杂时,使用 query() 方法可以让你的代码读起来更像自然语言,非常优雅。

# 使用 query 筛选年龄大于30且来自北京的用户
print("使用 query 筛选:")
print(df.query('age > 30 and city == "Beijing"'))
# 甚至可以引用外部变量
min_age = 28
print(f"\n年龄大于 {min_age} 的用户:")
print(df.query('age > @min_age'))

专家提示:query 字符串中引用外部变量时,需要在变量名前加上 符号。


第三部分:行修改与删除 - 数据清洗的“手术刀”

找到问题数据后,我们需要对其进行修改或删除。

基于条件修改行数据

结合布尔索引,我们可以轻松地修改满足特定条件的行数据。

# 将年龄大于30的用户年龄设置为30
df.loc[df['age'] > 30, 'age'] = 30
print("修改后年龄大于30的用户年龄被设置为30:")
print(df)
# 为来自北京的用户添加一个新标签列
df.loc[df['city'] == 'Beijing', 'is_capital'] = True
df.loc[df['city'] != 'Beijing', 'is_capital'] = False
print("\n添加 is_capital 列后:")
print(df)

删除行:drop() 方法

drop() 方法用于删除指定的行或列。

# 重置df以便演示
df = pd.DataFrame(data, index=['user_01', 'user_02', 'user_03', 'user_04'])
# 删除 user_03 这一行
df_dropped = df.drop('user_03')
print("删除 user_03 后:")
print(df_dropped)
# 注意:默认情况下,drop() 返回一个新DataFrame,原DataFrame不变。
# 如果想在原DataFrame上修改,使用 inplace=True
df.drop('user_04', inplace=True)
print("\n原DataFrame上删除 user_04 后:")
print(df)

删除包含缺失值的行:dropna()

在真实数据中,缺失值是家常便饭。dropna() 是处理缺失值的重要工具。

# 创建一个包含NaN的DataFrame
df_with_nan = df.copy()
df_with_nan.loc['user_02', 'age'] = np.nan
print("包含NaN的DataFrame:")
print(df_with_nan)
# 删除任何包含NaN的行
df_dropped_na = df_with_nan.dropna()
print("\n删除包含NaN的行后:")
print(df_dropped_na)

第四部分:进阶技巧与性能优化

当数据量达到百万甚至千万级别时,行操作的效率变得至关重要。

避免循环,拥抱向量化

新手误区: 使用 for 循环逐行处理DataFrame,这是Pandas中的大忌,性能极差。

正确做法: 尽可能使用Pandas内置的向量化操作(如上文提到的布尔索引、apply等)。

# 错误示范(极慢)
for index, row in df.iterrows():
    if row['age'] > 30:
        df.at[index, 'age_group'] = 'senior'
    else:
        df.at[index, 'age_group'] = 'junior'
# 正确示范(极快)
df['age_group'] = 'junior' # 先设置默认值
df.loc[df['age'] > 30, 'age_group'] = 'senior'

apply()applymap() 的使用

当没有现成的向量化函数时,apply 是你的好帮手。

  • df.apply(func, axis=1): 对每一行应用函数 func
  • df.applymap(func): 对每一个元素应用函数 func
# 定义一个函数来处理每一行
def get_location_info(row):
    if row['city'] == 'Beijing':
        return 'Capital'
    else:
        return 'Other City'
# 使用 apply 对每一行应用函数
df['location_info'] = df.apply(get_location_info, axis=1)
print("\n使用 apply 添加 location_info 列:")
print(df)

使用 eval() 加速复杂表达式

对于非常复杂的计算,eval() 可以通过解析字符串表达式来优化计算速度,并减少内存占用。

# 一个复杂的计算
df['complex_score'] = df['age'] * 2 + (df['age'] ** 2) / 10
# 使用 eval 可能会更快(在复杂表达式中效果更明显)
df.eval('complex_score = age * 2 + (age ** 2) / 10', inplace=True)

总结与最佳实践

我们已经系统地学习了Python DataFrame的行操作,现在来总结一下核心要点和最佳实践:

操作类型 核心方法/概念 适用场景 专家建议
选取 .loc (标签), .iloc (位置) 精准定位单行、多行、区域 优先使用 .loc,因为它更具可读性,对单个值用 .at/.iat
筛选 布尔索引, query() 根据条件过滤数据 布尔索引是基石query() 适合复杂条件,代码更优雅。
修改/删除 .loc[] 赋值, drop(), dropna() 数据清洗、特征工程 修改时优先使用 .loc 进行赋值,避免链式索引,删除时注意 inplace 的使用。
性能优化 向量化操作, apply(), eval() 大数据处理 坚决避免 for 循环,优先用向量化,其次用 apply,复杂计算考虑 eval

行操作心法:

  1. 明确目标: 你是想“选”数据、“筛”数据还是“改”数据?
  2. 选择工具: 根据目标(标签/位置、简单/复杂)选择最合适的工具(.loc/.iloc/布尔索引/query)。
  3. 拥抱向量化: 始终思考如何用Pandas的内置函数一次性处理整列或整行,而不是逐行循环。
  4. 代码可读性: 写代码时,不仅要实现功能,更要考虑未来自己或他人阅读的便利性,清晰的变量名和结构化的代码至关重要。

掌握了这些行操作,你就拥有了在Pandas海洋中自由航行的能力,希望这篇“终极指南”能成为你数据处理工具箱中的瑞士军刀,打开你的IDE,开始实践吧!


#Python #Pandas #DataFrame #数据科学 #数据分析 #行操作 #数据清洗 #编程技巧

分享:
扫描分享到社交APP
上一篇
下一篇