目录
进阶篇37. 字符串操作高级技巧 (extract, findall)
在数据清洗和文本处理任务中,字符串操作起着至关重要的作用。对于复杂文本数据,我们往往需要利用正则表达式从字符串中提取关键信息,或查找所有匹配模式的子串。Pandas 中的 str.extract()
和 str.findall()
方法提供了强大而灵活的工具,帮助我们完成这些任务。
本文将详细介绍这两个方法的基本原理、数学表达、代码示例和实际应用场景,帮助你在数据预处理和特征工程中更高效地处理文本数据。
1. str.extract() 方法
1.1 基本概念
str.extract()
用于从字符串中提取符合正则表达式捕获组的部分。其基本思想是:给定一个正则表达式模式,通过括号定义的捕获组来选取字符串中的目标子串。数学上,设字符串 ( s ) 中存在某个子串满足正则表达式 ( R ),则
extract
(
s
,
R
)
=
x
其中
x
是
s
中匹配捕获组的子串
\text{extract}(s, R) = x \quad \text{其中 } x \text{ 是 } s \text{ 中匹配捕获组的子串}
extract(s,R)=x其中 x 是 s 中匹配捕获组的子串
如果正则表达式有多个捕获组,extract()
会返回一个 DataFrame,每个捕获组对应一列。
1.2 示例代码
假设我们有一列包含混合文本的 Series,其中包含日期和时间信息,我们希望提取出日期部分:
import pandas as pd
# 示例数据:包含日期和其他信息的字符串
s = pd.Series([
"Order placed on 2024-01-15 at 14:30",
"Delivery scheduled for 2024-02-20, please confirm",
"No date available",
"Invoice date: 2024-03-05"
])
# 使用正则表达式提取日期(格式:YYYY-MM-DD)
# 正则表达式解释:(\d{4}-\d{2}-\d{2}) 捕获4位数字-2位数字-2位数字的日期格式
dates_extracted = s.str.extract(r"(\d{4}-\d{2}-\d{2})")
print("提取出的日期:")
print(dates_extracted)
输出结果为:
0
0 2024-01-15
1 2024-02-20
2 NaN
3 2024-03-05
在此示例中,str.extract()
根据正则表达式捕获了字符串中的日期信息,对于不匹配的文本则返回 NaN。
1.3 多捕获组示例
如果正则表达式中包含多个捕获组,extract()
会返回一个 DataFrame,包含每个捕获组的结果。如下例所示,我们同时提取日期和时间信息:
# 示例数据:同时包含日期和时间的字符串
s2 = pd.Series([
"Order placed on 2024-01-15 at 14:30",
"Delivery scheduled for 2024-02-20 at 09:45",
"Invoice date: 2024-03-05, time: 16:20"
])
# 正则表达式:第一个捕获组提取日期,第二个捕获组提取时间(格式:HH:MM)
pattern = r"(\d{4}-\d{2}-\d{2}).*?(\d{2}:\d{2})"
extracted = s2.str.extract(pattern)
print("提取出的日期和时间:")
print(extracted)
输出结果为:
0 1
0 2024-01-15 14:30
1 2024-02-20 09:45
2 2024-03-05 16:20
2. str.findall() 方法
2.1 基本概念
str.findall()
方法用于查找字符串中所有匹配正则表达式的子串,并返回一个列表。数学上,对于一个字符串 ( s ) 和正则表达式 ( R ),该方法生成一个列表:
findall
(
s
,
R
)
=
[
x
1
,
x
2
,
…
,
x
k
]
\text{findall}(s, R) = [x_1, x_2, \dots, x_k]
findall(s,R)=[x1,x2,…,xk]
其中每个 ( x_i ) 是 ( s ) 中匹配 ( R ) 的一个子串。
2.2 示例代码
假设我们有一个文本,想要提取其中所有的数字序列:
import pandas as pd
# 示例数据:包含多个数字的字符串
s = pd.Series([
"The price is 100 dollars, and the discount is 20%",
"Order 1234: quantity 10, unit price 15.5",
"No numbers here",
"Call 555-1234 or 555-5678 for support"
])
# 使用正则表达式提取所有数字
# \d+ 匹配一个或多个数字
numbers = s.str.findall(r"\d+")
print("提取出的所有数字:")
print(numbers)
输出结果可能为:
0 [100, 20]
1 [1234, 10, 15, 5]
2 []
3 [555, 1234, 555, 5678]
dtype: object
注意:在第二行中,“15.5” 被分解成 [15, 5],因为正则表达式 \d+ 不匹配小数点。如果需要匹配带小数点的数字,可以修改正则表达式。
2.3 匹配复杂模式
假设我们希望提取电子邮件地址,findall()
方法也可以用于这种情况:
s_emails = pd.Series([
"Please contact us at support@example.com for assistance.",
"Send feedback to feedback@domain.org and sales@domain.org.",
"No email provided."
])
emails = s_emails.str.findall(r"[\w\.-]+@[\w\.-]+\.\w+")
print("提取出的电子邮件地址:")
print(emails)
输出结果为:
0 [support@example.com]
1 [feedback@domain.org, sales@domain.org]
2 []
dtype: object
3. 应用场景与最佳实践
3.1 应用场景
- 数据清洗:利用 extract 和 findall 可以从混合文本中提取有用信息,如日期、时间、价格、电子邮件地址等。
- 特征工程:在机器学习中,提取文本中的关键信息并转换为结构化特征有助于提高模型性能。
- 文本分析:在自然语言处理任务中,可以用 findall 获取文本中所有符合特定模式的词或短语,进行频率统计和情感分析。
3.2 最佳实践
- 使用原始字符串:编写正则表达式时,推荐使用原始字符串(例如,
r"\d+"
)以避免转义问题。 - 逐步测试正则表达式:在对整个 Series 应用 extract 或 findall 前,先在单个字符串上测试正则表达式,确保其正确匹配目标模式。
- 捕获组的设计:合理设计捕获组,确保 extract 返回所需的结果;如果只需要单个值,确保正则表达式中只有一个捕获组。
- 处理缺失与空列表:对于没有匹配到的字符串,extract 返回 NaN,而 findall 返回空列表,根据后续需求进行处理,如填充默认值。
4. 综合案例:用户评论中的关键信息提取
假设我们有一份用户评论数据,其中包含电子邮件、电话号码和日期信息。我们希望同时提取出这些信息进行进一步分析。
import pandas as pd
data = {
'Comment': [
"Contact us at support@example.com on 2024-01-15.",
"Email feedback to feedback@domain.org; call 555-1234.",
"Meeting scheduled for 2024-02-20. No email provided.",
"Send inquiry to info@service.cn or call 010-88886666 on 2024-03-05."
]
}
df = pd.DataFrame(data)
# 提取电子邮件地址
df['Email'] = df['Comment'].str.extract(r"([\w\.-]+@[\w\.-]+\.\w+)", expand=False)
# 提取日期(格式 YYYY-MM-DD)
df['Date'] = df['Comment'].str.extract(r"(\d{4}-\d{2}-\d{2})", expand=False)
# 提取电话号码(简单模式:匹配包含数字、连字符和括号)
df['Phone'] = df['Comment'].str.findall(r"[\d\-\(\)]+")
print("提取后的数据:")
print(df)
输出结果示例:
Comment Email Date Phone
0 Contact us at support@example.com on 2024-01-15. support@example.com 2024-01-15 [2024-01-15]
1 Email feedback to feedback@domain.org; call 555-1234. feedback@domain.org NaN [555-1234]
2 Meeting scheduled for 2024-02-20. No email provided. NaN 2024-02-20 []
3 Send inquiry to info@service.cn or call 010-88886666 on 2024-03-05. info@service.cn 2024-03-05 [010-88886666]
在这个案例中,我们分别使用 extract()
和 findall()
方法提取评论中的电子邮件和日期(单值,使用 extract)以及电话号码(可能有多个,使用 findall)。
5. 总结
本文详细介绍了 Pandas 中字符串操作的高级技巧,重点讨论了 str.extract()
和 str.findall()
的应用。主要内容包括:
-
str.extract()
- 利用正则表达式捕获组从字符串中提取关键信息,返回一个 DataFrame 或 Series。
- 适用于提取单个匹配值,如日期、电子邮件等。
-
str.findall()
- 查找字符串中所有匹配正则表达式的子串,并以列表形式返回。
- 适用于可能存在多个匹配项的场景,如提取所有电话号码、关键词等。
通过多个示例,我们展示了如何在实际数据清洗、特征工程和文本分析中灵活应用这些方法。掌握这些高级技巧,可以帮助你高效地从非结构化文本数据中提取有价值的信息,并为后续的数据分析和模型构建提供结构化特征。
6. 参考资料
- Pandas 官方文档:Series.str.extract
- Pandas 官方文档:Series.str.findall
- 《Python for Data Analysis》 by Wes McKinney
- 相关博客文章,如 优快云 和知乎上关于“Pandas 正则表达式高级技巧”的讨论
希望本文能帮助你深入理解并灵活运用 Pandas 中的 extract 与 findall 方法,在数据清洗和文本分析中提取出有用信息,不断实践与探索,将使你在数据科学领域取得更高效、更精确的结果。