【Python基础】Pandas向量化字符串操作

247 阅读10分钟

一、向量化操作概述

Python内置一系列强大的字符串处理方法,但这些方法只能处理单个字符串,处理一个序列的字符串时,需要用到循环。

那么,有没有办法,不用循环就能同时处理多个字符串呢,pandas的向量化操作就提供了这样的方法。

向量化的操作使我们不必担心数组的长度和维度,只需要关系操作功能,尤为强大的是,除了支持常用的字符串操作方法,还集成了正则表达式的大部分功能,这使得pandas在处理字符串列时,具有非常大的魔力。

例如,要计算每个单词中 **‘a’**的个数,下面一行代码就可以搞定,非常高效

s = pd.Series(['amazon','alibaba','baidu'])
s.str.count('a')
0    2
1    3
2    1

假如用内置的字符串函数进行操作,需要进行遍历,且Python原生的遍历操作无法处理缺失值。

#用循环进行处理
s = ['amazon','alibaba','baidu']
[i.count('a') for i in s]
[2, 3, 1]

#存在缺失值时,打印报错
s = ['amazon','alibaba','baidu',None]
[i.count('a') for i in s]
AttributeError: 'NoneType' object has no attribute 'lower'

Pandas的向量化操作,能够正确的处理缺失值,无报错信息,如下

s = pd.Series(['amazon','alibaba','baidu',None])
s.str.count('a')
Out[36]: 
0    2.0
1    3.0
2    1.0
3    NaN
dtype: float64

通过上面的例子,对向量化进行简单总结,向量化是一种同时操作整个数组而不是一次操作一个元素的方法,下面从看看具体怎么应用。

 

二、向量化的字符方法

Pandas的字符串属的方法几乎包括了大部分Python的内置字符串方法(内置共有45个方法),下面将列举一些常见的方法的用法,例如上面的count()方法将会返回某个字符的个数,而len方法将会返回整个字符的长度。

方法说明
len()计算字符串长度
strip()等价于str.strip,去除字符串开头和结尾处指定的字符
rstrip()等价于str.rstrip ,删除字符串末尾的指定字符(默认为空格)
lstrip()等价于str.lstrip,截掉字符串左边的空格或指定字符
partition()等价于str.partition,根据指定的分隔符(sep)将字符串进行分割,从左边开始
rpartition()等价于str.rpartition,根据指定的分隔符(sep)将字符串进行分割,从右边开始
lower()等价于str.lower,所有大写字母转换为小写字母,仅限英文
casefold()等价于str.casefold,所有大写字母转换为小写字母,包括非英文
upper()等价于str.upper,小写字母转换为大写字母
find()等价于str.find,查找字符串中指定的子字符串sub第一次出现的位置
rfind()等价于str.rfind,查找字符串中指定的子字符串sub最后一次出现的位置
index()等价于str.index,查找字符串中第一次出现的子字符串的位置
rindex()等价于str.rindex,返回子字符串最后一次出现在字符串中的索引位置
capitalize()等价于str.capitalize,将字符串的第一个字母变成大写,其余字母变为小写
swapcase()等价于str.swapcase,将字符串str中的大小写字母同时进行互换
normalize()返回Unicode 标注格式。等价于 unicodedata.normalize
translate()等价于str.translate,根据maketrans()函数给出的字符映射表来转换字符
isalnum()等价于str.isalnum,检测字符串是否由字母和数字组成
isalpha()等价于str.isalpha,检测字符串是否只由字母组成
isdigit()等价于str.isdigit,检测字符串是否只由数字组成
isspace()等价于str.isspace,检测字符串是否只由空格组成
islower()等价于str.islower,检测字符串中的字母是否全由小写字母组成
isupper()等价于str.isupper,检测字符串中的字母是否全由大写字母组成
istitle()等价于str.istitle,检测所有单词首字母是否为大写,且其它字母是否为小写
isnumeric()等价于str.isnumeric,测字符串是否只由数字组成
isdecimal()等价于str.isdecimal,检查字符串是否只包含十进制字符
startswith()等价于str.startswith(pat),判断字符串是否以指定字符或子字符串开头
endswith()等价于str.endswith(pat),判断字符串是否以指定字符或子字符串结尾
center()等价于str.center,即字符串str居中,两边用字符填充
ljust()等价于str.ljust,左对齐填充,并使用fillchar填充(默认为空格)
rjust()等价于str.rjust,右对齐填充,默认为空格
zfill()等价于str.zfill,右对齐,前面用0填充到指定字符串长度

下面选取部分函数举例,其他函数参考字符串模块:Python字符串的45个方法详解

len()

import pandas as pd
import numpy as np 
 
s = pd.Series(['amazon','alibaba','Baidu'])
s.str.len()
Out[5]: 
0    6
1    7
2    5
dtype: int64

lower()

s = pd.Series(['amazon','alibaba','Baidu'])
s.str.lower()
0     amazon
1    alibaba
2      baidu

zfill()

右对齐,前面用0填充到指定字符串长度

s = pd.Series(['56783','34','987766721','326'])
s.str.zfill(10) 
Out[53]: 
0    0000056783
1    0000000034
2    0987766721
3    0000000326
dtype: object

 

三、向量化的正则表达式

Pandas的字符串方法根据Python标准库的re模块实现了正则表达式,下面将介绍Pandas的str属性内置的正则表达式相关方法

方法说明
match()对每个元素调用re.match(),将会返回一个布尔数组
extract()对每个元素调用re.match(),将会返回所有结果构成的字符串数组
findall()对每个元素用re.findall()
replace()用正则模式替换字符串
contains()对每个元素调用re.search()返回布尔类型
count()计算符合正则表达式的字符串数量
split()等价于str.spilt(),支持正则表达式
rsplit()等价于str.rsplit()支持正则表达式

split()

split,按指定字符分割字符串,类似split的方法返回一个列表类型的序列

#按数字分割
pd.Series(['QQ1252号码','QQ1353加我','我389的']).str.split('\d+')
Out[39]: 
0    [QQ, 号码]
1    [QQ, 加我]
2     [我, 的]
dtype: object


s=pd.Series(['a_b_c', 'c_d_e', np.nan, 'f_g_h'])
s.str.split('_')
Out[94]: 
0    [a, b, c]
1    [c, d, e]
2          NaN
3    [f, g, h]
dtype: object

切分后的列表中的元素可以通过get方法或者 [] 方法进行读取

s.str.split('_').str.get(1)
Out[96]: 
0      b
1      d
2    NaN
3      g
dtype: object

使用expand方法可以轻易地将这种返回展开为一个数据表

s.str.split('_', expand=True)
Out[97]: 
     0    1    2
0    a    b    c
1    c    d    e
2  NaN  NaN  NaN
3    f    g    h

同样,我们也可以限制切分的次数:

In [20]: s.str.split('_', expand=True, n=1)
Out[20]: 
     0    1
0    a  b_c
1    c  d_e
2  NaN  NaN
3    f  g_h

rsplit()

rsplit与split相似,不同的是,这个切分的方向是反的。即,从字串的尾端向首段切分

In [21]: s.str.rsplit('_', expand=True, n=1)
Out[21]: 
     0    1
0  a_b    c
1  c_d    e
2  NaN  NaN
3  f_g    h

replace ()

replace方法默认使用正则表达式

s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca','', np.nan, 'CABA', 'dog', 'cat'])




s.str.replace('^.a|dog', 'XX-XX ', case=False)
Out[27]: 
0           A
1           B
2           C
3    XX-XX ba
4    XX-XX ca
5            
6         NaN
7    XX-XX BA
8      XX-XX 
9     XX-XX t
 

findall()

提取聊天记录中的QQ号
s=pd.Series(['QQ号码123452124','QQ123356123','我的Q123356189','Q号123356111注意','加我Q号123356124有惊喜'])
s.str.findall('\d+')
0    [123452124]
1    [123356123]
2    [123356189]
3    [123356111]
4    [123356124]

 

四、其他向量化的方法

除了上面介绍的Pandas字符串的正常操作和正则表达式外,Pandas的str属性还提供了其他的一些方法,这些方法非常的有用,在进行特征提取或者数据清洗时,非常高效,具体如下:

方法说明
get()获取元素索引位置上的值,索引从0开始
slice()对元素进行切片取值
slice_replace()对元素进行切片替换
cat()连接字符串
repeat()重复元素
normalize()将字符串转换为Unicode规范形式
pad()在字符串的左边右边或者两边增加空格
wrap()将字符串按照指定的宽度换行
join()用分隔符连接Series对象的每个元素
get_dummies()按照分隔符提取每个元素的dummy变量,转换为one-hot编码的DataFrame

wrap()

s = pd.Series(['0000056783','0000000034','0987766721'])
s.str.wrap(5)
Out[68]: 
0    00000\n56783
1    00000\n00034
2    09877\n66721
dtype: object

pad()

s = pd.Series(['A','E','C','D','E'])
s.str.pad(5)
Out[65]: 
0        A
1        E
2        C
3        D
4        E
dtype: object

slice()

Series_1 = pd.Series(['马 云:2000亿','马化腾:1800亿','王健林:1200亿','小伍哥:0.000012亿'])
Series_1.str.slice(0,3)
Out[33]: 
0    马 云
1    马化腾
2    王健林
3    小伍哥
dtype: object

get()

Series_1.str.get(0)
Out[34]: 
0    马
1    马
2    王
3    小
dtype: object

slice_replace()

切片替换
Series_1.str.slice_replace(0,3,'小伍哥')
Out[36]: 
0        小伍哥:2000亿
1        小伍哥:1800亿
2        小伍哥:1200亿
3    小伍哥:0.000012亿
dtype: object




Series_1.str.join('-')
Out[41]: 
0            马- -云-:-2-0-0-0-亿
1            马-化-腾-:-1-8-0-0-亿
2            王-健-林-:-1-2-0-0-亿
3    小-伍-哥-:-0-.-0-0-0-0-1-2-亿
dtype: object


Series_1 = pd.Series(['A','E','C','D','E'])
Series_1.str.get_dummies()
   A  C  D  E
0  1  0  0  0
1  0  0  0  1
2  0  1  0  0
3  0  0  1  0
4  0  0  0

get_dummies()

另一个需要好好解释的是get_dummies()方法,举个例子:假如我们用A,B,C,D来表示一个人的某个特征:

monte = pd.Series(['Graham Chapman', 'John Cleese', 'Terry Gilliam',
                   'Eric Idle', 'Terry Jones', 'Michael Palin'])
full_monte = pd.DataFrame({'name': monte,
                           'info': ['B|C|D', 'B|D', 'A|C',
                                    'B|D', 'B|C', 'B|C|D']})
full_monte
             name   info
0  Graham Chapman  B|C|D
1     John Cleese    B|D
2   Terry Gilliam    A|C
3       Eric Idle    B|D
4     Terry Jones    B|C
5   Michael Palin  B|C|D
可以看到比如Graham Chapman有B,C,D三种特征,而John Cleese有B和D两种特征。这时我们可以用get_dummies('|')以|作为分隔符,将这些特征进行one-hotfull_monte['info'].str.get_dummies('|')
Out[52]: 
   A  B  C  D
0  0  1  1  1
1  0  1  0  1
2  1  0  1  0
3  0  1  0  1
4  0  1  1  0
5  0  1  1  1

repeat()

s = pd.Series(['A','E','C','D','E'])
s.str.repeat(2)
Out[62]: 
0    AA
1    EE
2    CC
3    DD
4    EE
dtype: object

cat()

作用:连接字符串 

用法:Series.str.cat(others=None, sep=None, na_rep=None) 

参数: 

  • others : 列表或复合列表,默认为None,如果为None则连接本身的元素 
  • sep : 字符串 或者None,默认为None 
  • na_rep : 字符串或者 None, 默认 None。如果为None缺失值将被忽略。 

返回值: concat : 序列(Series)/索引(Index)/字符串(str)

#如果连接的是两个序列,则会一一对应连接
s1 = pd.Series(['A','E','C','D','E'])
s2 = pd.Series(['1','2','3','4','5'])
s1.str.cat(s2)
Out[74]: 
0    A1
1    E2
2    C3
3    D4
4    E5
dtype: object




#只提供一个序列,则只连接自己,默认为空格
s1.str.cat()
'AECDE'
s1.str.cat(sep='-')
'A-E-C-D-E'


#也可以同时复核连接,参数可以是二维的
d = pd.concat([s1, s2], axis=1)
s3.str.cat(d, na_rep='-')
Out[87]: 
0    xA1
1    xE2
2    yC3
3    yD4
4    yE5
dtype: objec

五、Pandas学习文档

官方文档

链接:pandas.pydata.org/pandas-docs…

官方文档,每个函数和方法,都有详细的介绍,对于英语比较好的同学,强烈推荐直接看官方文档,非常清晰,完整。

             

中文文档

文档链接:www.pypandas.cn/docs/

对于英语不好的同学,可以看国内翻译过来的文档,还是比较全面的,基本上也能获得比较好的学习效果。

              

接口文档

pandas.pydata.org/pandas-docs…