.str 访问器提供了一套强大的向量化字符串操作,让你能够像处理 Python 字符串一样处理整个 Series,并支持正则表达式、提取、替换等高级功能。
import pandas as pd
import numpy as np
df = pd.DataFrame({
'姓名': [' 张三 ', '李四', '王五', '赵六', '陈七'],
'邮箱': ['zhangsan@example.com', 'lisi@work.com', 'wangwu@test.org', 'zhaoliu@company.cn', np.nan],
'电话': ['138-1234-5678', '139-8765-4321', '158-1111-2222', '177-3333-4444', '186-5555-6666'],
'备注': ['VIP', '普通', 'VIP,老客户', '新用户', '普通']
})
print(df)
要使用 Pandas 的字符串方法,需要通过 .str 访问器,它会将 Series 的每个元素视为字符串,并应用相应的方法。
# 去除姓名两端的空格
df['姓名'] = df['姓名'].str.strip()
print(df['姓名'])
# 将邮箱转为小写(若存在)
df['邮箱小写'] = df['邮箱'].str.lower()
print(df[['邮箱', '邮箱小写']])
下表列出了一些常用的 .str 方法,用法与 Python 内置字符串方法类似。
# 计算邮箱长度
df['邮箱长度'] = df['邮箱'].str.len()
print(df[['邮箱', '邮箱长度']])
# 判断邮箱是否以 'com' 结尾
df['是否com邮箱'] = df['邮箱'].str.endswith('.com')
print(df[['邮箱', '是否com邮箱']])
# 统计电话中 '-' 出现的次数
df['连字符数'] = df['电话'].str.count('-')
print(df[['电话', '连字符数']])
# 将电话中的 '-' 替换为空格
df['电话_clean'] = df['电话'].str.replace('-', ' ')
print(df[['电话', '电话_clean']])
.str.contains() 可以检查每个字符串是否包含某个模式(支持正则)。
# 查找邮箱中包含 'example' 的行
mask = df['邮箱'].str.contains('example', na=False) # na=False 将缺失值视为 False
print(df[mask])
# 备注中是否包含 'VIP'(注意正则元字符)
df['是否VIP'] = df['备注'].str.contains(r'VIP', regex=True, na=False)
print(df[['备注', '是否VIP']])
.str.split() 配合 expand=True 可以将分割后的结果展开为多列。
# 将电话按 '-' 分割成三列
phone_split = df['电话'].str.split('-', expand=True)
phone_split.columns = ['区号', '中间', '后四位']
df = pd.concat([df, phone_split], axis=1)
print(df[['电话', '区号', '中间', '后四位']].head())
# 将备注按 ',' 分割(可能有多项)
note_split = df['备注'].str.split(',', expand=True)
print(note_split)
.str.extract().str.extract() 用于从字符串中提取匹配正则表达式分组的内容,返回 DataFrame。
# 从邮箱中提取用户名和域名
pattern = r'(?P<用户名>.*)@(?P<域名>.*)'
extracted = df['邮箱'].str.extract(pattern)
print(extracted)
# 提取电话中的前三位(区号)
df['电话前缀'] = df['电话'].str.extract(r'^(\d{3})')
print(df[['电话', '电话前缀']])
.str.extractall()当每个字符串可能包含多个匹配时,使用 extractall()。
# 模拟数据:多个标签
df_tags = pd.DataFrame({'tags': ['apple,banana', 'cat,dog,fish', 'red,blue,green']})
# 提取所有单词(\w+)
result = df_tags['tags'].str.extractall(r'(\w+)')
print(result)
.str.replace() 支持正则表达式,可以完成复杂的替换任务。
# 将电话中的所有数字替换为 '#'
df['电话隐藏'] = df['电话'].str.replace(r'\d', '#', regex=True)
print(df[['电话', '电话隐藏']])
# 去除多余的空格(多个空格替换为一个)
df['备注_clean'] = df['备注'].str.replace(r'\s+', ' ', regex=True)
print(df[['备注', '备注_clean']])
.str.get_dummies()对于用分隔符分隔的类别列,.str.get_dummies() 可以快速生成虚拟变量(one-hot encoding)。
# 从备注列生成虚拟变量(按 ',' 分割)
dummies = df['备注'].str.get_dummies(sep=',')
print(dummies)
# 合并到原 DataFrame
df = pd.concat([df, dummies], axis=1)
print(df.head())
字符串方法遇到缺失值默认返回 NaN,可通过参数控制。
# 邮箱列有 NaN,直接调用 .str.lower() 会保留 NaN
print(df['邮箱'].str.lower())
# 使用 fillna 先填充
df['邮箱_filled'] = df['邮箱'].fillna('').str.lower()
print(df['邮箱_filled'])
import pandas as pd
# 原始脏数据
raw = pd.DataFrame({
'name': [' Alice ', 'Bob', 'Charlie ', ' David', 'Eve'],
'email': ['alice@example.com', 'bob@work.com', 'charlie@test', 'david@company', np.nan],
'phone': ['123-456-7890', '234-567-8901', '345-6789', '456-789-0123', '567-890-1234'],
'tags': ['new,premium', 'regular', 'premium,vip', 'new', 'regular,new']
})
print("原始数据:")
print(raw)
# 1. 清洗姓名:去除空格,首字母大写
raw['name'] = raw['name'].str.strip().str.title()
# 2. 验证邮箱格式(简单检查是否包含 @ 和 .)
raw['email_valid'] = raw['email'].str.contains(r'.+@.+\..+', regex=True, na=False)
# 3. 提取电话区号(前三位)并标准化电话格式
raw['area_code'] = raw['phone'].str.extract(r'^(\d{3})')
raw['phone_std'] = raw['phone'].str.replace(r'(\d{3})-(\d{3})-(\d{4})', r'\1-\2-\3', regex=True)
# 4. 从 tags 生成虚拟变量
tags_dummies = raw['tags'].str.get_dummies(sep=',')
raw = pd.concat([raw, tags_dummies], axis=1)
print("\n清洗后数据:")
print(raw)
na=False 或 fillna() 控制。
r'...')避免歧义。
fillna() 处理缺失值,或使用参数 na=False/na='' 等控制行为。extract, contains 等)。.str.get_dummies() 比循环 + pd.get_dummies() 更高效。apply + Python 函数可能会比 .str 更快(需测试)。