【问题标题】:Select data using a regular expression使用正则表达式选择数据
【发布时间】:2017-12-10 16:57:40
【问题描述】:

我有一个这样的数据框

import pandas as pd

df = pd.DataFrame({'a': ['abc', 'r00001', 'r00010', 'rfoo', 'r01234', 'r1234'], 'b': range(6)})

        a  b
0     abc  0
1  r00001  1
2  r00010  2
3    rfoo  3
4  r01234  4
5   r1234  5

我现在要选择此数据框的所有列,其中 a 列中的条目以 r 开头,后跟五个数字。

From here 我知道如果它只以r 开头而不带数字:

print df.loc[df['a'].str.startswith('r'), :]

        a  b
1  r00001  1
2  r00010  2
3    rfoo  3
4  r01234  4
5   r1234  5

类似的东西

print df.loc[df['a'].str.startswith(r'[r]\d{5}'), :]

当然不行。如何正确地做到这一点?

【问题讨论】:

    标签: python regex pandas


    【解决方案1】:

    选项 1
    pd.Series.str.match

    df.a.str.match('^r\d{5}$')
    
    1     True
    2     True
    3    False
    4     True
    5    False
    Name: a, dtype: bool
    

    将其用作过滤器

    df[df.a.str.match('^r\d{5}$')]
    
            a  b
    1  r00001  1
    2  r00010  2
    4  r01234  4
    

    选项 2
    使用字符串方法自定义列表理解

    f = lambda s: s.startswith('r') and (len(s) == 6) and s[1:].isdigit()
    [f(s) for s in df.a.values.tolist()]
    
    [False, True, True, False, True, False]
    

    将其用作过滤器

    df[[f(s) for s in df.a.values.tolist()]]
    
            a  b
    1  r00001  1
    2  r00010  2
    4  r01234  4
    

    时机

    df = pd.concat([df] * 10000, ignore_index=True)
    
    %timeit df[[s.startswith('r') and (len(s) == 6) and s[1:].isdigit() for s in df.a.values.tolist()]]
    %timeit df[df.a.str.match('^r\d{5}$')]
    %timeit df[df.a.str.contains('^r\d{5}$')]
    
    10 loops, best of 3: 22.8 ms per loop
    10 loops, best of 3: 33.8 ms per loop
    10 loops, best of 3: 34.8 ms per loop
    

    【讨论】:

    • 由于str.match 使用re.match,模式可以更改为'r\d{5}',因为默认情况下它从字符串的开头匹配
    • 我发现列表理解优于假定的矢量化 pandas 方法,这对添加时间很有用
    • @EdChum 同意!我知道开发人员有很多事情要做。而且我一直告诉自己,当我可以稍微清理一下我的日程安排时,我会开始为pandas 做贡献……我仍然希望我会在某个时候。
    【解决方案2】:

    您可以使用str.contains 并传递正则表达式模式:

    In[112]:
    df.loc[df['a'].str.contains(r'^r\d{5}')]
    
    Out[112]: 
            a  b
    1  r00001  1
    2  r00010  2
    4  r01234  4
    

    这里的模式计算结​​果为 ^r - 以字符 r 开头,然后 \d{5} 查找 5 位数字

    startswith 查找字符模式,而不是正则表达式模式,这就是它失败的原因

    关于str.containsstr.match 之间的区别,它们是类似的,但str.contains 使用re.searchstr.match 使用更严格的re.match,请参阅docs

    编辑

    要回答您的评论,请添加$,使其匹配特定数量的字符,请参阅related

    In[117]:
    df = pd.DataFrame({'a': ['abc', 'r000010', 'r00010', 'rfoo', 'r01234', 'r1234'], 'b': range(6)})
    df
    
    Out[117]: 
             a  b
    0      abc  0
    1  r000010  1
    2   r00010  2
    3     rfoo  3
    4   r01234  4
    5    r1234  5
    
    
    In[118]:
    df.loc[df['a'].str.match(r'r\d{5}$')]
    
    Out[118]: 
            a  b
    2  r00010  2
    4  r01234  4
    

    【讨论】:

    • 不是原始帖子的一部分,但现在如何排除超过 5 个数字(或任何其他字符)的那些?
    • r'^r\d{5}$ 应该处理这个
    • 太好了,谢谢,很遗憾,我不能两次投票。我接受另一个答案,因为他的速度稍快,但感谢您的详细解释。
    • 不是问题,很高兴我能提供帮助并添加一些有用的解释
    • @Cleb 1 我并不是要为 EdChum 说话,但我相当有信心他会分享我的观点。这种情绪是,我很清楚他的答案和我的一样好,而且你只能选择一个......无论哪种方式,我都可以接受。 2 我在帖子中添加了时间,我对match 会比contains 工作得更快的期望。我的时间证明了这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 2019-10-22
    • 2018-01-05
    • 2012-03-20
    • 2015-01-11
    • 2016-01-11
    相关资源
    最近更新 更多