
本文介绍三种无需显式循环、基于向量化操作的 Pandas 列筛选方法:布尔索引 + str.startswith()、drop() 配合条件掩码,以及正则表达式 filter(),兼顾可读性与性能。
本文介绍三种无需显式循环、基于向量化操作的 pandas 列筛选方法:布尔索引 + `str.startswith()`、`drop()` 配合条件掩码,以及正则表达式 `filter()`,兼顾可读性与性能。
在数据清洗过程中,经常需要根据列名特征批量删除列(例如剔除所有以 "post_" 开头但保留 "post_title" 的列)。若使用传统 for 循环或列表推导式,不仅代码冗长,还违背 Pandas 的向量化设计哲学——而真正“正确”的做法是利用其内置的字符串向量化方法与布尔索引机制。
以下以原始列名为例:['sku', 'total_sales', 'post_author', 'post_date', 'post_date_gmt', 'product_type', 'post_title', 'post_excerpt', 'post_name', 'post_modified', 'post_modified_gmt', 'guid', 'post_type']
✅ 方法一:drop() + 向量化布尔掩码(推荐,清晰直观)
# 构建布尔掩码:列名以 'post_' 开头 且 不等于 'post_title'
mask = df_web.columns.str.startswith('post_') & (df_web.columns != 'post_title')
# 直接删除满足条件的列
df_web.drop(columns=df_web.columns[mask], inplace=True)
该方法逻辑直白、易于调试,str.startswith() 是向量化操作,性能优异;inplace=True 可避免重复赋值,节省内存。
✅ 方法二:布尔索引 + loc(函数式风格,不可变思维)
# 保留:列名等于 'post_title' OR 不以 'post_' 开头
df_web = df_web.loc[:, (df_web.columns == 'post_title') | ~df_web.columns.str.startswith('post_')]
此写法强调“保留规则”,符合函数式编程习惯,适合链式操作(如配合 assign() 或 pipe()),且天然支持不可变操作(不修改原 DataFrame)。
✅ 方法三:正则 filter()(简洁但需理解正则逻辑)
# 使用负向先行断言:匹配不以 'post_' 开头,或虽以 'post_' 开头但后缀不是 'title' 的列名 df_web = df_web.filter(regex=r'^(?!post_(?!title))')
正则 ^(?!post_(?!title)) 含义为:
-
^:字符串开头 -
(?!...):负向先行断言 -
post_(?!title):匹配"post_"后不紧接"title"的情况 - 整体即排除所有
"post_XXX"(XXX ≠ title),但保留"post_title"和其他无关列。
⚠️ 注意:正则虽一行解决,但可读性较低,建议仅在团队熟悉正则或需高度泛化(如多关键词/复杂模式)时采用。
? 最佳实践建议
- 优先使用方法一或二:语义明确、调试友好、性能无损;
- 避免在循环中调用
drop()(每次触发副本与索引重建,O(n²) 时间复杂度); - 列名筛选前,建议先验证掩码结果:
print(df_web.columns[mask].tolist()); - 若需复用逻辑,可封装为函数:
def drop_columns_by_pattern(df, pattern, keep=None): mask = df.columns.str.contains(pattern) & ~(df.columns.isin([keep] if keep else [])) return df.drop(columns=df.columns[mask]) # 调用:df_web = drop_columns_by_pattern(df_web, r'^post_', keep='post_title')
掌握这些向量化技巧,不仅能提升代码执行效率,更是深入理解 Pandas 设计范式的起点。










