从数据集中提取文本

时间:2019-11-07 17:05:35

标签: python regex pandas

我正在处理一个数据集,在其中我需要提取所有可用的日期。日期可以采用以下格式:

04/20/2009; 04/20/09; 4/20/09; 4/3/09
Mar-20-2009; Mar 20, 2009; March 20, 2009; Mar. 20, 2009; Mar 20 2009;
20 Mar 2009; 20 March 2009; 20 Mar. 2009; 20 March, 2009
Mar 20th, 2009; Mar 21st, 2009; Mar 22nd, 2009
Feb 2009; Sep 2009; Oct 2010
6/2008; 12/2009
2009; 2010

我编写了以下代码:

df['dates'] = df['text'].str.extract(r'((?:\d{1,2}[/ ])?(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec[a-z.,]*[- ])?(?:\d{1,2}[a-z-, /]{1,4})?(?:\d{2,4}))')

除了某些文字,它给了我正确的结果

  

文本输出

     

实验室:B12 969 2007 \ n 12 969#应该给出2007

     

长达35年,于1985年出售\ n 35#应该给出1985

     

在14岁时死于... 14岁的人不应该被认为

我试图将提取代码更改为

r'((?:\d{1,2}[/ ])?(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec[a-z.,]*[- ])?(?:\d{1,2}[a-z-, ]{1,4})?(?:[/]\d{2})?(?:\d{4})?)' 

但是整个结果变得糟糕

3 个答案:

答案 0 :(得分:2)

您的正则表达式的问题在于它的组成部分都是可选的,并且它与实际上与日期无关的数字匹配。您需要构建带有必需部分的正则表达式,以避免匹配任意部分。

这很棘手:示例输入中有不同类型的日期。对于这些输入,我建议:

(?<!\d)((?<!\d[ \t])(?:A(?:pr(?:il)?|ug(?:ust)?)|Dec(?:ember)?|Feb(?:ruary)?|J(?:an(?:uary)?|u(?:ly|ne|[ln]))|Ma(?:rch|[ry])|Nov(?:ember)?|Oct(?:ober)?|Sep(?:tember)?)(?:-\d{1,2}-\d{4}|(?:\.?\s*\d{1,2}(?:st|[rn]d|th)?,?)?\s*\d{4})|\d{1,2}\s+(?:A(?:pr(?:il)?|ug(?:ust)?)|Dec(?:ember)?|Feb(?:ruary)?|J(?:an(?:uary)?|u(?:ly|ne|[ln]))|Ma(?:rch|[ry])|Nov(?:ember)?|Oct(?:ober)?|Sep(?:tember)?)\.?,?\s*\d{4}|(?:\d{1,2}/)?\d{1,2}/\d{2}(?:\d{2})?|(?:19|20)\d{2})(?!\d)

请参见regex demo。符合条件:

  • (?<!\d)-向后看是负数:当前位置的左侧不允许有数字
  • (-外部捕获组的开始(.str.extract必需的)
    • (?<!\d[ \t])-当前位置左侧不允许带数字或空格或制表符
    • (?:A(?:pr(?:il)?|ug(?:ust)?)|Dec(?:ember)?|Feb(?:ruary)?|J(?:an(?:uary)?|u(?:ly|ne|[ln]))|Ma(?:rch|[ry])|Nov(?:ember)?|Oct(?:ober)?|Sep(?:tember)?)-月份名称及其缩写
    • (?:-\d{1,2}-\d{4}|(?:\.?\s*\d{1,2}(?:st|[rn]d|th)?,?)?\s*\d{4})-两种选择之一:
      • -\d{1,2}-\d{4}--,先输入1或2位数字,再输入-,再输入4位数字
      • |-或
      • (?:\.?\s*\d{1,2}(?:st|[rn]d|th)?,?)?-匹配1或0次出现的可选非捕获组:
      • \.?-可选的.
      • \s*-超过0个空格
      • \d{1,2}-1或2位数字
      • (?:st|[rn]d|th)?-可选的字符序列:strn,后跟dth
      • ,?-可选逗号
      • \s*\d{4}-空格+0 + 4位数字
  • |-或
    • \d{1,2}\s+-1或2位数字,然后是1+个空格
    • (?:A(?:pr(?:il)?|ug(?:ust)?)|Dec(?:ember)?|Feb(?:ruary)?|J(?:an(?:uary)?|u(?:ly|ne|[ln]))|Ma(?:rch|[ry])|Nov(?:ember)?|Oct(?:ober)?|Sep(?:tember)?)-月份名称及其缩写(与上面相同)
    • \.?-可选点
    • ,?-可选逗号
    • \s*-超过0个空格
    • \d{4}-四位数
  • |-或
    • (?:\d{1,2}/)?-可选的1或2位数字序列,然后是/
    • \d{1,2}-1或2位数字
    • /-/
    • \d{2}(?:\d{2})?-2位数字和2位数字的可选序列(允许2位或4位数字,但不允许3位)
  • |-或
    • (?:19|20)-1920
    • \d{2}-两位数字
  • )-推杆捕获组的结束
  • (?!\d)-前瞻性否定:当前位置的右边不允许有数字。

在Python中,您可以为模式定义块并动态构建:

months = r'(?:A(?:pr(?:il)?|ug(?:ust)?)|Dec(?:ember)?|Feb(?:ruary)?|J(?:an(?:uary)?|u(?:ly|ne|[ln]))|Ma(?:rch|[ry])|Nov(?:ember)?|Oct(?:ober)?|Sep(?:tember)?)'
pattern = rf'(?<!\d)((?<!\d[ \t]){months}(?:-\d{{1,2}}-\d{{4}}|(?:\.?\s*\d{{1,2}}(?:st|[rn]d|th)?,?)?\s*\d{{4}})|\d{{1,2}}\s+{months}\.?,?\s*\d{{4}}|(?:\d{{1,2}}/)?\d{{1,2}}/\d{{2}}(?:\d{{2}})?|(?:19|20)\d{{2}})(?!\d)'

答案 1 :(得分:0)

尝试使用pandas.to_datetime(),它将最常用的日期格式转换为datetime对象。

答案 2 :(得分:0)

尝试this模式。我的建议是,您应该将问题分解为多个部分,并尝试一次匹配一个模式。因为用于此问题的正则表达式有点凌乱,很难用一个表达式覆盖所有边缘情况。

我已包含了子正则表达式,因此您可以对其进行优化以验证边缘情况。