You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于正则的Python CASE语句实现:DataFrame列生成问题求助

解决Pandas中根据FileName生成RdwyId列的问题

问题背景

先明确下你的需求:你有这样一个DataFrame:

FileName
01011RT0TU7
11041NT4TU8
51391RST0U2
01011645RT0TU9
11311455TX0TU8
51041545ST3TU9

想要新增一列RdwyId,规则很清晰:

  • 如果FileName前5位全是数字,就拿这5位拼接"000"当RdwyId
  • 如果前8位全是数字,直接取这8位当RdwyId

你尝试了三种方案都没成功,咱们先拆解下每个方案的问题,再给你靠谱的解决办法。


你的三个方案为什么失败?

方案1的问题

rdwyre1=re.compile(r'\d\d\d\d\d')
rdwyre2=re.compile(r'\d\d\d\d\d\d\d\d')
rdwy1=rdwyre1.findall(str(thous["FileName"]))
rdwy2=rdwyre2.findall(str(thous["FileName"]))
thous["RdwyId"]=re.sub(r'\d\d\d\d\d', str(thous["FileName"].loc[:4])+"000",thous["FileName"])
  • 你把整个FileName列转成字符串str(thous["FileName"]),这会把所有单元格值拼成一个大字符串,正则匹配的是整个列的文本,不是单个单元格
  • re.sub只能处理单个字符串,直接传Pandas Series会报错,而且你的替换逻辑也不对——你不是要修改原文件名,而是要生成新的RdwyId值

方案2的问题

thous["RdwyId"]=np.select(
 [ re.search(r'\d\d\d\d\d',thous["FileName"])!="None",
 rdwyre2.findall(str(thous["FileName"]))!="None" ],
 [ rdwyre1.findall(str(thous["FileName"]))+"000",
 rdwyre2.findall(str(thous["FileName"])), ],
 default="Unknown"
 )
  • re.search和findall都只能处理单个字符串,你传整个Series进去根本没法正确匹配
  • findall返回的是列表,你拿它和字符串"None"比较永远是False,条件判断完全失效
  • 就算条件对了,列表和"000"拼接会得到['01011']000这种奇怪的结果,不是你要的字符串

方案3的问题

thous=thous.assign(RdwyID=lambda x: str(rdwyre1.search(x).group())+"000" if bool(rdwyre1.search(x))==True else str(rdwyre2.search(x).group()))
  • 这里的lambda x里的x是整个DataFrame,不是单个FileName单元格!你相当于在搜索整个DataFrame的文本,完全不是你要的操作
  • 就算你改成针对FileName列,当某个值不满足前5位数字时,直接调用rdwyre2.search(x).group()会报错——如果前8位也不是数字,search返回None,调用group()会直接抛出AttributeError

正确的解决方法

方法1:用Pandas字符串方法(直观易懂,适合新手)

咱们直接针对每个单元格处理,用字符串切片和判断就搞定:

import pandas as pd

# 先构造你的DataFrame
data = {
    "FileName": [
        "01011RT0TU7",
        "11041NT4TU8",
        "51391RST0U2",
        "01011645RT0TU9",
        "11311455TX0TU8",
        "51041545ST3TU9"
    ]
}
thous = pd.DataFrame(data)

# 定义生成RdwyId的函数
def get_rdwy_id(file_name):
    # 先判断前5位是不是全数字,且第6位不是数字(避免和前8位数字的情况冲突)
    if file_name[:5].isdigit() and not file_name[5].isdigit():
        return file_name[:5] + "000"
    # 再判断前8位是不是全数字
    elif file_name[:8].isdigit():
        return file_name[:8]
    # 可选:处理不符合两种规则的情况,返回默认值
    else:
        return "Unknown"

# 对每个FileName单元格应用这个函数
thous["RdwyId"] = thous["FileName"].apply(get_rdwy_id)

运行后就能得到你想要的结果:

FileName    RdwyId
0      01011RT0TU7   01011000
1      11041NT4TU8   11041000
2      51391RST0U2   51391000
3   01011645RT0TU9   01011645
4   11311455TX0TU8   11311455
5   51041545ST3TU9   51041545

方法2:用正则表达式提取(更灵活,适合规则变化的场景)

如果以后规则有调整,用正则会更方便。咱们用str.extract匹配两种模式:

# 匹配两种模式:开头5位数字+非数字,或者开头8位数字
extracted = thous["FileName"].str.extract(r'^(\d{5})(?=\D)|^(\d{8})', expand=False)
# 取每行第一个非空的匹配结果
thous["RdwyId"] = extracted.bfill(axis=1).iloc[:, 0]
# 对5位的结果拼接000
thous["RdwyId"] = thous["RdwyId"].apply(lambda x: x + "000" if len(x) == 5 else x)

解释下正则:

  • ^(\d{5})(?=\D):匹配开头5位数字,且后面跟着非数字(确保不是前8位数字的情况),提取这5位
  • ^(\d{8}):匹配开头8位数字,提取这8位
  • bfill(axis=1):对每一行,从左到右取第一个非空的匹配值

核心要点总结

你之前踩的坑本质是没有针对单个单元格处理,而是把整个Series当成单个字符串操作,混淆了Pandas Series和单个Python字符串的处理逻辑。用apply方法或者Pandas的str系列方法,都是逐个处理单元格的正确姿势,这是处理这类问题的关键。

内容的提问来源于stack exchange,提问作者SQLserving

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:50:02