pandas教程:String Manipulation 字符串处理和正则表达式re

文章目录

  • [7.3 String Manipulation(字符串处理)](#7.3 String Manipulation(字符串处理))
  • [1 String Object Methods(字符串对象方法)](#1 String Object Methods(字符串对象方法))
  • [2 Regular Expressions(正则表达式)](#2 Regular Expressions(正则表达式))
  • [3 Vectorized String Functions in pandas(pandas中的字符串向量化函数)](#3 Vectorized String Functions in pandas(pandas中的字符串向量化函数))

7.3 String Manipulation(字符串处理)

python很多内建方法很适合处理string。而且对于更复杂的模式,可以配合使用正则表达式。而pandas则混合了两种方式。

1 String Object Methods(字符串对象方法)

大部分string处理,使用内建的一些方法就足够了。比如,可以用split来分割用逗号区分的字符串:

python 复制代码
val = 'a,b, guido'
python 复制代码
val.split(',')
复制代码
['a', 'b', ' guido']

split经常和strip一起搭配使用来去除空格(包括换行符):

python 复制代码
pieces = [x.strip() for x in val.split(',')]
pieces
复制代码
['a', 'b', 'guido']

可以使用+号把::和字符串连起来:

python 复制代码
first, second, third = pieces
python 复制代码
first + '::' + second + '::' + third
复制代码
'a::b::guido'

但这种方法并不python,更快的方法是直接用join方法:

python 复制代码
'::'.join(pieces)
复制代码
'a::b::guido'

其他一些方法适合锁定子字符串位置相关的。用in关键字是检测substring最好的方法,当然,indexfind也能完成任务:

python 复制代码
'guido' in val
复制代码
True
python 复制代码
val.index(',')
复制代码
1
python 复制代码
val.find(':')
复制代码
-1

注意indexfind的区别。如果要找的string不存在的话,index会报错。而find会返回-1:

python 复制代码
val.index(':')
复制代码
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-11-280f8b2856ce> in <module>()
----> 1 val.index(':')


ValueError: substring not found

count会返回一个substring出现的次数:

python 复制代码
val.count(',')
复制代码
2

replace会取代一种出现方式(pattern)。也通常用于删除pattern,传入一个空字符串即可:

python 复制代码
val.replace(',', '::')
复制代码
'a::b:: guido'
python 复制代码
val.replace(',', '')
复制代码
'ab guido'

2 Regular Expressions(正则表达式)

正则表达式能让我们寻找更复杂的pattern。通常称一个表达式为regex,由正则表达语言来代表一个字符串模式。可以使用python内建的re模块来使用。

关于正则表达式,有很多教学资源,可以自己找几篇来学一些,这里不会介绍太多。

re模块有以下三个类别:patther matching(模式匹配), substitution(替换), splitting(分割)。通常这三种都是相关的,一个regex用来描述一种pattern,这样会有很多种用法。这里举个例子,假设我们想要根据空格(tabsspacesnewlines)来分割一个字符串。用于描述一个或多个空格的regex\s+:

python 复制代码
import re
python 复制代码
text = "foo    bar\t baz  \tqux"
python 复制代码
re.split('\s+', text)
复制代码
['foo', 'bar', 'baz', 'qux']

当调用re.split('\s+', text)的时候,正则表达式第一次被compile编译,并且split方法会被调用搜索text。我们可以自己编译regex,用re.compile,可以生成一个可以多次使用的regex object

python 复制代码
regex = re.compile('\s+')
python 复制代码
regex.split(text)
复制代码
['foo', 'bar', 'baz', 'qux']

如果想要得到符合regex的所有结果,以一个list结果返回,可以使用findall方法:

python 复制代码
regex.findall(text)
复制代码
['    ', '\t ', '  \t']

为了防止\在正则表达式中的逃逸,推荐使用raw string literal,比如r'C:\x',而不是使用'C:\\x

使用re.compile创建一个regex object是被强烈推荐的,如果你打算把一个表达式用于很多string上的话,这样可以节省CPU的资源。

matchsearch,与findall关系紧密。不过findall会返回所有匹配的结果,而search只会返回第一次匹配的结果。更严格地说,match只匹配string开始的部分。这里举个例子说明,我们想要找到所有的邮件地址:

python 复制代码
text = """Dave dave@google.com 
          Steve steve@gmail.com 
          Rob rob@gmail.com 
          Ryan ryan@yahoo.com """

pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'
python 复制代码
# re.IGNORECASE makes the regex case-insensitive 
regex = re.compile(pattern, flags=re.IGNORECASE)

使用findall找到一组邮件地址:

python 复制代码
regex.findall(text)
复制代码
['dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']

search返回text中的第一个匹配结果。match object能告诉我们找到的结果在text中开始和结束的位置:

python 复制代码
m = regex.search(text)
python 复制代码
m
复制代码
<_sre.SRE_Match object; span=(5, 20), match='dave@google.com'>
python 复制代码
text[m.start():m.end()]
复制代码
'dave@google.com'

regex.match返回None,因为它只会在pattern存在于strng开头的情况下才会返回匹配结果:

python 复制代码
print(regex.match(text))
复制代码
None

sub返回一个新的string,把pattern出现的地方替换为我们指定的string

python 复制代码
print(regex.sub('REDACTED', text))
复制代码
Dave REDACTED 
          Steve REDACTED 
          Rob REDACTED 
          Ryan REDACTED 

假设你想要找到邮件地址,同时,想要把邮件地址分为三个部分,username, domain name, and domain suffix.(用户名,域名,域名后缀)。需要给每一个pattern加一个括号:

python 复制代码
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
python 复制代码
regex = re.compile(pattern, flags=re.IGNORECASE)

match object会返回一个tuple,包含多个pattern组份,通过groups方法:

python 复制代码
m = regex.match('wesm@bright.net')
python 复制代码
m.groups()
复制代码
('wesm', 'bright', 'net')

findall会返回a list of tuples:

python 复制代码
regex.findall(text)
复制代码
[('dave', 'google', 'com'),
 ('steve', 'gmail', 'com'),
 ('rob', 'gmail', 'com'),
 ('ryan', 'yahoo', 'com')]

sub也能访问groups的结果,不过要使用特殊符号 \1, \2。\1表示第一个匹配的group,\2表示第二个匹配的group,以此类推:

python 复制代码
print(regex.sub(r'Username: \1, Domain: \2, Suffix: \3', text))
复制代码
Dave Username: dave, Domain: google, Suffix: com 
          Steve Username: steve, Domain: gmail, Suffix: com 
          Rob Username: rob, Domain: gmail, Suffix: com 
          Ryan Username: ryan, Domain: yahoo, Suffix: com 

3 Vectorized String Functions in pandas(pandas中的字符串向量化函数)

一些复杂的数据清理中,string会有缺失值:

python 复制代码
import numpy as np
import pandas as pd
python 复制代码
data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com', 
        'Rob': 'rob@gmail.com', 'Wes': np.nan}
python 复制代码
data = pd.Series(data)
data
复制代码
Dave     dave@google.com
Rob        rob@gmail.com
Steve    steve@gmail.com
Wes                  NaN
dtype: object
python 复制代码
data.isnull()
复制代码
Dave     False
Rob      False
Steve    False
Wes       True
dtype: bool

可以把一些字符串方法和正则表达式(用lambda或其他函数)用于每一个value上,通过data.map,但是这样会得到NA(null)值。为了解决这个问题,series有一些数组导向的方法可以用于字符串操作,来跳过NA值。这些方法可以通过seriesstr属性;比如,我们想检查每个电子邮箱地址是否有'gmail' with str.contains:

python 复制代码
data.str
复制代码
<pandas.core.strings.StringMethods at 0x111f305c0>
python 复制代码
data.str.contains('gmail')
复制代码
Dave     False
Rob       True
Steve     True
Wes        NaN
dtype: object

正则表达式也可以用,配合任意的re选项,比如IGNORECASE

python 复制代码
pattern
复制代码
'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'
python 复制代码
data.str.findall(pattern, flags=re.IGNORECASE)
复制代码
Dave     [(dave, google, com)]
Rob        [(rob, gmail, com)]
Steve    [(steve, gmail, com)]
Wes                        NaN
dtype: object

有很多方法用于向量化。比如str.getindex索引到str属性:

python 复制代码
matches = data.str.match(pattern, flags=re.IGNORECASE)
matches
复制代码
/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py:1: FutureWarning: In future versions of pandas, match will change to always return a bool indexer.
  if __name__ == '__main__':





Dave     (dave, google, com)
Rob        (rob, gmail, com)
Steve    (steve, gmail, com)
Wes                      NaN
dtype: object

为了访问嵌套list里的元素,我们可以传入一个index给函数:

python 复制代码
matches.str.get(1)
复制代码
Dave     google
Rob       gmail
Steve     gmail
Wes         NaN
dtype: object
python 复制代码
matches.str.get(0)
复制代码
Dave      dave
Rob        rob
Steve    steve
Wes        NaN
dtype: object

也可以使用这个语法进行切片:

python 复制代码
data.str[:5]
复制代码
Dave     dave@
Rob      rob@g
Steve    steve
Wes        NaN
dtype: object
相关推荐
找不到、了1 小时前
MySQL的窗口函数介绍
数据库·mysql
精灵vector1 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习1 小时前
Python入门Day2
开发语言·python
执笔诉情殇〆1 小时前
springboot集成达梦数据库,取消MySQL数据库,解决问题和冲突
数据库·spring boot·mysql·达梦
Vertira1 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉2 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗2 小时前
黑马python(二十四)
开发语言·python
软件技术NINI2 小时前
springMvc的简单使用:要求在浏览器发起请求,由springMVC接受请求并响应,将个人简历信息展示到浏览器
数据库·mysql
晓13132 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~3 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain