【问题标题】:Regex to compare and extract alphabet characters using python正则表达式使用python比较和提取字母字符
【发布时间】:2019-05-15 07:57:30
【问题描述】:

您好,我有一个如下所示的数据集:

Format,Message,time
A,ab@1 yl@5 rd@20 pp@40,3
B,bc@1 gn@7 yl@20 ss@25 rd@50, 21
C,cc@1 yl@9 rd@20, 22

我想使用从消息的 yl 和 rd 中提取的数字,然后比较其数字(例如 yl@5 ---> 5)和时间列的数字之间的数字。所以如果第 1 行、第 3 行将与第 5 行和第 20 行进行比较。所以如果它小于这两个元素,它将被赋值为 g。如果时间为 7,则赋值为 y,同样,如果时间为 20 及以上,则赋值为 r。

所以会是这样的

Format,Message,time,status
A,ab@1 yl@5 rd@20 pp@40,3,g
B,bc@1 gn@7 yl@20 ss@25 rd@50,21,y
C,cc@1 yl@9 rd@20,22,r

【问题讨论】:

  • 请发布您尝试过的内容。
  • re.search('yl@(\d+).+?rd@(\d+)', 'ab@1 yl@5 rd@20 pp@40').groups() 是正则表达式,它将为您提供这些数字的元组。

标签: python regex dataframe


【解决方案1】:

您的问题实际上是多个问题。从“数据框”标签来看,您似乎正在使用 pandas 执行此操作。您询问的正则表达式可能会增加 'yl' 和 'rd' 的数字(如果有的话,我假设它们总是在那里)。但是正则表达式通常不进行数学或数值比较,所以这是第三位。

匹配 'yl' 数值的正则表达式(假设整数,而不是浮点数):

r'yl@(\d+)'

您可以在单个表达式中提取它们,但这会假定它们始终处于相同的顺序,或者变成一个复杂的正则表达式。

为了确保只有yl@5 匹配,而xyl@5 不匹配,您可以在开头(需要空格或行首)和结尾(需要空格或行尾)添加一些限制:

r'(^|\s)yl@(\d+)($|\s)'

或者,如果您遇到yl 是名称空间的情况,例如a:yl,您也可以添加它:

r'(^|\s)([a-z]+:)?l@(\d+)($|\s)'

然而,所有这些只是使用正则表达式语言构建更具体的表达式。我喜欢使用的一个非常好的编写正则表达式的工具(没有隶属关系)是 RegexBuddy,但也有非常好的在线工具,比如https://regex101.com/

在代码示例中使用基本上按照您的建议进行:

import re
from pandas import DataFrame

df = DataFrame({
    'Format': ['A', 'B', 'C'],
    'Message': ['ab@1 yl@5 rd@20 pp@40', 'bc@1 gn@7 yl@20 ss@25 rd@50', 'cc@1 yl@9 rd@20'],
    'time': [3, 21, 22]
})


def determine_status(row):
    def find(tag, message):
        match = re.search(rf"{tag}@(\d+)", message)
        if match:
            return match.group(1)
        else:
            raise ValueError(f'{tag} not in message.')

    yl = int(find('yl', row['Message']))
    rd = int(find('rd', row['Message']))

    time = int(row['time'])
    if time < yl < rd:
        return 'g'
    if yl <= time < rd:
        return 'y'
    return 'r'


df['status'] = df.apply(determine_status, axis=1)

print(df)

find 函数接受标签和消息,并使用正则表达式为消息中的标签生成数值。

determine_status 函数就是这样做的 - 它需要来自 DataFrame 的一行,并将使用 Messagetime 列来确定状态并返回它。

df.apply 然后用于创建一个新的status 列,并为DataFrame 中的每一行填充determine_status 的结果。

您没有指定您使用的 Python 版本,但如果它是 Python 3.6 之前的版本,您会发现像 f'{tag} not in message.' 这样的表达式将不起作用 - 相反,您会使用像 @987654338 这样的表达式@。

【讨论】:

  • 嗨!感谢您的回答,想问我在其他回答中提出的相同问题,如果元素采用这种格式(例如 a:yl@5 ),代码是否适用?
  • 是的,事实上,如果有像 xyl@5 这样的片段,代码甚至可以工作,因为它仍然以 'yl' 结尾,后跟一个 @ 和一些数字,这就是正则表达式 @987654339 @ 火柴。您可以要求它被空白包围,其中(^|\s)yl@(\d+)($|\s) 匹配组2 而不是1。或者,如果您想添加可选的命名空间,则(^|\s)([a-z]+:)?yl@(\d+)($|\s) 之类的东西会起作用,如果您匹配第三组。
  • 我如何收到错误,例如“ValueError: ('yl not in message.', 'occured at index 0')”
  • 这实际上是预期的:显然,并非数据中的所有消息都有yl 元素?如果消息中没有yl,您期望得到什么?该错误是由脚本中的raise ValueError 行生成的,以指示此问题。
  • 除标题行和第一行数据外,其余行应有元素yl
【解决方案2】:

我认为,这可以通过内置的字符串函数来完成。试试这个!

def f(mess):
    p1 = mess.find('yl')
    p2 = mess.find('rd')
    return int(mess[p1+3:].split(' ')[0]),int(mess[p2+3:].split(' ')[0])

df['vals'] =df['Message'].apply(f) 

df['status'] = df.apply(lambda row:  'g' if min(row['vals']) > row.time \
                        else 'y' if row.vals[1]>row.time  \
                        else 'r', axis=1)

print(df)

输出:

  Format                  Message  time      vals status
0      A    ab@1 yl@5 rd@20 pp@40     3   (5, 20)      g
1      B  bc@1  yl@20 ss@25 rd@50    21  (20, 50)      y
2      C          cc@1 yl@9 rd@20    22   (9, 20)      r

【讨论】:

  • 您好,感谢您的回答!想与您核对代码。返回部分有“+3”是什么意思?
  • p1 选择 yl 开始的位置,然后我们必须移动 3 个位置才能找到数字,直到我们看到一个空格
  • 感谢您的解释!如果元素采用这种格式(例如 a:yl@5 ),代码是否适用?
  • 是的,它会的。它只是在整个字符串中搜索 y1 的位置。希望对你有帮助
  • 您好,感谢您的回复,我还在尝试中!问题解决后接受!再次感谢!
猜你喜欢
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多