Pandas: 字符串操作

最后更新:2026-08-26

真实数据里的文本从来不干净——多余空格、大小写混乱、特殊字符、格式不统一。Pandas 的 .str 访问器让字符串操作也享受向量化:一行代码清洗整列文本,比 Python 循环快 100 倍。本节覆盖最常用的字符串方法,并构建链式清洗流水线。

⚠️ 注意: 以下代码需在本地 Python 环境中运行。

1. 你将学到


2. Carol 的用户输入清洗

(1) 痛点:用户输入五花八门

Carol 收集的用户数据格式混乱:

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['  alice  ', 'BOB', 'Charlie!', '  carol  ', 'DaVid123'],
    'email': ['alice@EXAMPLE.com', 'bob@company.ORG', 'charlie@uni.edu',
              'CAROL@example.COM', 'david@test.net'],
    'phone': ['555-0100', '(555) 0200', '555.0300', '+1-555-0400', '5550500']
})
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

(2) 解法:.str 链式清洗

▶ 示例:链式文本清洗(难度⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['  alice  ', 'BOB', 'Charlie!', '  carol  ', 'DaVid123'],
    'email': ['alice@EXAMPLE.com', 'bob@company.ORG', 'charlie@uni.edu',
              'CAROL@example.COM', 'david@test.net']
})

# Chain .str methods: strip → replace → title
df['name_clean'] = (df['name']
    .str.strip()
    .str.replace(r'[^a-zA-Z ]', '', regex=True)
    .str.title()
)
print(df[['name', 'name_clean']])
#          name name_clean
# 0    alice        Alice
# 1         BOB        Bob
# 2   Charlie!    Charlie
# 3     carol        Carol
# 4   DaVid123       David

# Lowercase email domain
df['email_clean'] = df['email'].str.lower()
print(df['email_clean'])
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

3. .str 访问器

(1) .str vs Python str

特性 Python str Pandas .str
操作对象 单个字符串 整列(向量化)
NaN 处理 报错 自动跳过(返回 NaN)
链式 不支持 ✅ 连续调用
性能 循环→慢 向量化→快

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:.str 基础方法(难度⭐)

PYTHON
import pandas as pd

s = pd.Series(['Hello World', 'pandas', 'DATA SCIENCE', None, 'python'])

# Case conversion
print(s.str.lower())    # hello world, pandas, data science, NaN, python
print(s.str.upper())    # HELLO WORLD, PANDAS, DATA SCIENCE, NaN, PYTHON
print(s.str.title())    # Hello World, Pandas, Data Science, NaN, Python
print(s.str.capitalize())  # Hello world, Pandas, Data science, NaN, Python

# Whitespace
s2 = pd.Series(['  hello  ', '\tworld\n', '  pandas  '])
print(s2.str.strip())    # hello, world, pandas
print(s2.str.lstrip())   # hello  , world\n, pandas  
print(s2.str.rstrip())   #   hello, \tworld,   pandas

# Length
print(s.str.len())       # 11, 6, 12, NaN, 6
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

4. replace / contains / match

(1) replace 替换

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:replace 字符串替换(难度⭐⭐)

PYTHON
import pandas as pd

s = pd.Series(['price: $100', 'price: $200', 'cost: €50'])

# Simple replacement
print(s.str.replace('$', 'USD'))
# price: USD100, price: USD200, cost: €50

# Regex replacement
print(s.str.replace(r'[\$€]', '', regex=True))
# price: 100, price: 200, cost: 50

# Replace multiple patterns
phones = pd.Series(['555-0100', '(555) 0200', '555.0300'])
clean = phones.str.replace(r'[^0-9]', '', regex=True)
print(clean)  # 5550100, 5550200, 5550300
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

(2) contains / startswith / endswith

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:条件筛选字符串(难度⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice Johnson', 'Bob Smith', 'Charlie Brown', 'Carol White'],
    'email': ['alice@example.com', 'bob@company.org', 'charlie@uni.edu', 'carol@test.net']
})

# Contains substring
has_com = df[df['email'].str.contains('.com')]
print(has_com['name'])  # Alice Johnson

# Startswith / endswith
starts_with_a = df[df['name'].str.startswith('A')]
print(starts_with_a)

edu_email = df[df['email'].str.endswith('.edu')]
print(edu_email['name'])  # Charlie Brown

# Regex contains — find org or edu domains
org_or_edu = df[df['email'].str.contains(r'\.(org|edu)$', regex=True)]
print(org_or_edu[['name', 'email']])
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

(3) contains vs match 区别

方法 匹配位置 正则
contains 任意位置
match 从开头
startswith 从开头 ❌(纯字符串)
endswith 从末尾 ❌(纯字符串)

5. split / extract

(1) split 分割

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:split 分割字符串(难度⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice Johnson', 'Bob Smith', 'Charlie Brown'],
    'email': ['alice@example.com', 'bob@company.org', 'charlie@uni.edu']
})

# Split into list
print(df['name'].str.split(' '))
# 0    [Alice, Johnson]
# 1    [Bob, Smith]
# 2    [Charlie, Brown]

# Split into separate columns
name_split = df['name'].str.split(' ', expand=True)
print(name_split)
#         0        1
# 0   Alice  Johnson
# 1     Bob    Smith
# 2 Charlie    Brown

# Split and keep only first part
df['first_name'] = df['name'].str.split(' ').str[0]
print(df['first_name'])

# Split email to get domain
df['domain'] = df['email'].str.split('@').str[1]
print(df['domain'])
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

(2) extract 正则提取

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:extract 提取模式(难度⭐⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'contact': [
        'Alice <alice@example.com>',
        'Bob <bob@company.org>',
        'Charlie (555-0100)',
        'Carol <carol@test.net>'
    ]
})

# Extract email with regex group
df['email'] = df['contact'].str.extract(r'<(.+?)>')
print(df[['contact', 'email']])
#                   contact             email
# 0  Alice <alice@example.com>  alice@example.com
# 1      Bob <bob@company.org>    bob@company.org
# 2     Charlie (555-0100)                NaN
# 3    Carol <carol@test.net>    carol@test.net

# Extract multiple groups
df2 = pd.DataFrame({
    'email': ['alice@example.com', 'bob@company.org']
})
parts = df2['email'].str.extract(r'(.+?)@(.+)')
print(parts)
#        0             1
# 0  alice  example.com
# 1    bob   company.org

# Extract all matches
colors = pd.Series(['red,blue,green', 'yellow,pink'])
print(colors.str.extractall(r'(\w+)'))
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

6. 其他常用方法

(1) 速查表

方法 功能 示例
len() 字符串长度 s.str.len()
count() 子串出现次数 s.str.count('a')
find() 子串位置 s.str.find('bc')
isdigit() 是否全数字 s.str.isdigit()
isalpha() 是否全字母 s.str.isalpha()
join() 用分隔符连接 s.str.join(',')
pad() 填充到固定宽度 s.str.pad(10, fillchar='0')
repeat() 重复字符串 s.str.repeat(3)
slice() 切片 s.str.slice(0, 5)

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:pad/slice/zfill(难度⭐)

PYTHON
import pandas as pd

ids = pd.Series(['1', '23', '456', '7890'])

# Pad with zeros to 4 digits
print(ids.str.zfill(4))
# 0    0001
# 1    0023
# 2    0456
# 3    7890

# Slice first 2 characters
codes = pd.Series(['US-001', 'UK-002', 'JP-003'])
print(codes.str.slice(0, 2))  # US, UK, JP
print(codes.str[:2])           # same, Python slice syntax
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

7. 完整示例:用户数据文本清洗流水线

(5) ▶ 文本清洗流程

100%
graph TB
    A[原始文本] --> B[strip 去空格]
    B --> C[lower / upper 统一大小写]
    C --> D[replace 去特殊字符]
    D --> E[split / extract 提取信息]
    E --> F[title 格式化]
    F --> G[干净文本]
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:文本清洗全流程(难度⭐⭐⭐)

PYTHON
import pandas as pd

# ============================================
# Comprehensive example: Text cleaning
# pipeline for user data
# ============================================

# 1. Raw messy user data
df = pd.DataFrame({
    'raw_name': ['  ALICE johnson  ', 'bob   SMITH', '  CHARLIE! brown  ', 'CAROL white123'],
    'raw_email': ['ALICE@Example.COM', 'bob@company.ORG', 'charlie@uni.EDU', 'CAROL@TEST.NET'],
    'raw_phone': ['555-0100', '(555) 0200', '555.0300 ext 42', '+1-555-0400'],
    'raw_zip': ['0123', '12345', '  90210  ', '10001-2345']
})

# 2. Name cleaning: strip → remove special chars → title case
df['name'] = (df['raw_name']
    .str.strip()
    .str.replace(r'[^a-zA-Z\s]', '', regex=True)
    .str.replace(r'\s+', ' ', regex=True)  # collapse multiple spaces
    .str.title()
)

# 3. Email cleaning: lowercase
df['email'] = df['raw_email'].str.lower()

# 4. Phone cleaning: keep only digits
df['phone_digits'] = df['raw_phone'].str.replace(r'[^0-9]', '', regex=True)
df['phone_formatted'] = df['phone_digits'].str.slice(0, 3) + '-' + df['phone_digits'].str.slice(3, 7)

# 5. Zip code: extract 5-digit code
df['zip5'] = df['raw_zip'].str.strip().str.extract(r'(\d{5})')

# 6. Email domain
df['domain'] = df['email'].str.split('@').str[1]

# 7. Show cleaned results
print("=== Cleaned Data ===")
print(df[['name', 'email', 'phone_formatted', 'zip5', 'domain']])
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

❓ 常见问题

Q .str 和 Python str 区别?
A Python str 方法操作单个字符串,.str 访问器操作整列(向量化)。.str 自动跳过 NaN(返回 NaN),Python str 遇 NaN 报错。.str 支持链式调用(s.str.strip().str.lower()),Python str 不支持。性能上 .str 比循环快 10-100 倍。
Q 对 NaN 怎么处理?
A .str 方法自动跳过 NaN——输入 NaN,输出 NaN。不需要手动检测缺失值。这也是 .str 比用 apply + Python str 更方便的原因之一。contains/match 对 NaN 返回 False(不是 NaN),筛选时注意。
Q extract 返回什么?
A extract 返回 DataFrame——每个正则组一列。单组返回 1 列 DataFrame,多组返回多列。extractall 返回所有匹配(MultiIndex)。注意:无匹配的行返回 NaN。需要 Series 则用 .str.extract(...)[0]
Q contains 和 match 区别?
A contains 在字符串任意位置匹配(如 'abc' contains 'b' → True)。match 只从字符串开头匹配(如 'abc' match 'b' → False)。startswith/endswith 是纯字符串匹配(不支持正则)。需要正则用 contains/match,纯文本用 startswith/endswith。
Q StringDtype 有什么好处?
A 默认字符串列是 object 类型(Python 对象),每个值是独立 str 对象。StringDtype(df['col'].astype('string'))是 Pandas 专用字符串类型,用 pd.NA 表示缺失,内存更高效,.str 方法更一致。Pandas 2.x 推荐用 StringDtype 替代 object。
Q str.replace 和 df.replace 区别?
A str.replace 只操作字符串列,支持正则(regex=True),逐元素替换。df.replace 操作整个 DataFrame,可替换任意类型的值,也支持正则。字符串替换优先用 str.replace(语义更清晰),跨列替换用 df.replace。
Q 如何提取电话号码中的数字?
As.str.replace(r'[^0-9]', '', regex=True) 删除所有非数字字符。或用 extract:s.str.extract(r'(\d{3})[-.)\s]*(\d{3})[-.)\s]*(\d{4})') 提取区号+前缀+后缀。正则表达式是文本提取的核心能力。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建含空格和大小写混乱的姓名 Series,用 .str.strip().str.title() 清洗,再统计清洗后以 'A' 开头的姓名数。
  2. 进阶题(难度⭐⭐):创建含电话号码的 Series(格式不一),用 replace+正则提取纯数字,再用 zfill 格式化为 10 位。
  3. 挑战题(难度⭐⭐⭐):模拟用户注册数据(name/email/phone/zip 全有格式问题),构建 5 步链式清洗流水线,每步一种 .str 方法,最后输出干净表格。

← 上一课:重塑与透视 · 下一课:日期时间 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏