如何用正则拆分DataFrame列并填充未匹配捕获组的NA值?
问题:Pandas提取Text列为Expense、Unit、Time三列
问题背景
给定如下包含Text列的DataFrame:
import pandas as pd from io import StringIO df = pd.read_csv(StringIO( """Text Electric consumption L1 (kiosk) (01/02-15/02/2022) AC Expense L2-13b (21/12-12/01/2023) AC Overtime Expense L4-24,25,26 Overtime Expense (16/05-15/06/2021)"""), sep=";")
期望将Text列拆分为Expense、Unit、Time三列,目标输出如下:
| Expense | Unit | Time |
|---|---|---|
| Electric consumption | L1 (kiosk) | (01/02-15/02/2022) |
| AC Expense | L2-13b | (21/12-12/01/2023) |
| AC Overtime Expense | L4-24,25,26 | np.nan |
| Overtime Expense | np.nan | (16/05-15/06/2021) |
但使用原正则表达式df['Text'].str.extract('(?P<Expense>.*) (?P<Unit>[L|B].*)(?P<Time>\(.*)', expand=True)时,仅能匹配前两行,后两行所有列均为np.nan,需要调整实现方式。
解决方案
原正则的问题在于强制要求Unit和Time同时存在,但后两行分别缺少其中一个字段,且贪婪匹配的.*会错误吃掉部分内容。我们需要使用非贪婪匹配并将Unit、Time设为可选字段,调整后的实现如下:
正确代码示例
import pandas as pd from io import StringIO df = pd.read_csv(StringIO( """Text Electric consumption L1 (kiosk) (01/02-15/02/2022) AC Expense L2-13b (21/12-12/01/2023) AC Overtime Expense L4-24,25,26 Overtime Expense (16/05-15/06/2021)"""), sep=";") # 适配所有场景的正则表达式 pattern = r'(?P<Expense>.*?)(?: (?P<Unit>L[\d,-]+(?: \(.*?\))?)|)(?: (?P<Time>\(.*?\))|)$' extracted_df = df['Text'].str.extract(pattern, expand=True) # 将空字符串替换为缺失值(等价于np.nan) extracted_df = extracted_df.replace('', pd.NA) print(extracted_df)
正则逻辑说明
(?P<Expense>.*?):用非贪婪匹配*?捕获Expense部分,避免过度匹配吃掉后续的Unit或Time字段(?: (?P<Unit>L[\d,-]+(?: \(.*?\))?)|):可选的Unit匹配单元:- 匹配以
L开头、包含数字/连字符/逗号的内容 - 可选匹配括号包裹的附加信息(如
(kiosk)) |表示该单元可以为空(即无Unit字段的情况)
- 匹配以
(?: (?P<Time>\(.*?\))|)$:可选的Time匹配单元:- 匹配括号包裹的时间字符串
|表示该单元可以为空(即无Time字段的情况)$锚定到字符串结尾,确保匹配完整
最终输出
运行代码后将得到与期望一致的结果:
Expense Unit Time 0 Electric consumption L1 (kiosk) (01/02-15/02/2022) 1 AC Expense L2-13b (21/12-12/01/2023) 2 AC Overtime Expense L4-24,25,26 <NA> 3 Overtime Expense <NA> (16/05-15/06/2021)
内容的提问来源于stack exchange,提问作者adrenaline245
相关产品推荐
相关产品推荐

