正则表达式清洗联系方式
今天在处理一个数据集,里面有一个字段contact
存有联系方式。但却是电话和email混合在一起的。如下为部分contact
记录。
372 MiloslawWisniewski@rhyta.com618-346-3914
58 YasminCardosoAraujo@superrito.com412-640-7035
483 731-577-0292AngelGrant@fleckens.hu
19 ZakKelly@rhyta.com1 530 532 8397
466 AndreaBrodahl@armyspy.com+1 (612) 589-1495
这样的数据是不整洁的。电话跟email应该是分开的两个字段。所以应该对contact
这个字段进行处理,分开为phone_number
和email
两个字段。
这里主要用到pandas
的str.extract方法和正则表达式进行拆分。
phone_number正则表达式
首先来研究下电话号码。可以看到上面给出的几条记录中的电话号码最直接的结构就是3个数字+3个数字+4个数字,然后部分可选的就是可能一些带有括号、间隔符可能是空格或者横线、在电话号码最前面可能有+1(应该是区号吧)。
由这些先确定第一个正则表达式:
(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}
然后把这个表达式用到str.extract
中,如下:
df['phone_number'] = df.contact.str.extract(r'((\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4})', expand=True)
结果报错了?
ValueError: Wrong number of items passed 2, placement implies 1
这应该是返回了两个值导致的错误。还是对正则表达式不是很熟啊,各种查资料才发现是(\+\d{1,2}\s)?
的问题,因为有括号,所以被当成一个捕获分组,导致返回多个匹配结果。加上?:
进行非捕获匹配。运行没报错。
然后查看是否有匹配不到的phone_number
。发现有一条记录并没有被筛出来。虽然1234567890
应该不会是电话号码,但却提醒了我电话号码有可能没有间隔成三部分,而是连着的。所以需要改进下正则表达式。
df[df['phone_number'].isnull()]
contact johndoe@email.com1234567890
phone_number NaN
间隔符-
和空格可能有也可能没有。所以表达式应该为:
(?:\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}
然后将这条新的表达式替换到前面的代码里运行,再进行检查,发现已没有问题。
email正则表达式
email
的结构比较简单,就是字符串@字符串.字符串。
正则表达式为:
[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+
但在这里有一个问题就是如果不设边界,那么会把电话号码也匹配进去。所以应该在前后限制为英文字母。
完整代码为:
df['email'] = df.contact.str.extract('([a-zA-Z][a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+[a-zA-Z])', expand=True)
最后,在拆分出来两个新的字段phone_number
和email
之后,contact
字段就是多余的了,应该删掉。
# Note: axis=1 表明我们参考的是一列,而不是一行
df = df.drop('contact', axis=1)
参考
Regular expression to match standard 10 digit phone number
Email Address Regular Expression That 99.99% Works