在Pandas中,为Series对象和Index对象配备了很对处理文本数据的方法,可以轻松地对数组中的每个元素进行操作。最重要的是,这些方法自动排除缺失值/NA值。这些方法可以通过str属性访问,方法名称与python内置的字符处理函数方法一致:
In [1]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [2]: s.str.lower()
Out[2]:
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
In [3]: s.str.upper()
Out[3]:
0 A
1 B
2 C
3 AABA
4 BACA
5 NaN
6 CABA
7 DOG
8 CAT
dtype: object
In [4]: s.str.len()
Out[4]:
0 1.0
1 1.0
2 1.0
3 4.0
4 4.0
5 NaN
6 4.0
7 3.0
8 3.0
dtype: float64
In [5]: idx = pd.Index([' jack', 'jill ', ' jesse ', 'frank'])
In [6]: idx.str.strip()
Out[6]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')
In [7]: idx.str.lstrip()
Out[7]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')
In [8]: idx.str.rstrip()
Out[8]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')
虽然DataFrame对象不能直接使用str属性,但DataFrame对象的数据是由Series对象构造的.所有同样可以使用这些方法.
例如,处理DataFrame对象的列标签对象df.columns也是一个Series对象,要转换列标签对象就可以使用str属性:
In [9]: df = pd.DataFrame(randn(3, 2), columns=[' Column A ', ' Column B '],
...: index=range(3))
...:
In [10]: df
Out[10]:
Column A Column B
0 -1.425575 -1.336299
1 0.740933 1.032121
2 -1.585660 0.913812
In [11]: df.columns.str.strip()
Out[11]: Index(['Column A', 'Column B'], dtype='object')
In [12]: df.columns.str.lower()
Out[12]: Index([' column a ', ' column b '], dtype='object')
当然这些方法也是可以链式使用的.
例如,将其去除空白,转化小写后再进行字符替换:
In [13]: df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_')
In [14]: df
Out[14]:
column_a column_b
0 -1.425575 -1.336299
1 0.740933 1.032121
2 -1.585660 0.913812
和字符串的spilt方法一样,str属性的spilt方法返回的也是列表.
In [15]: s2 = pd.Series(['a_b_c', 'c_d_e', np.nan, 'f_g_h'])
In [16]: s2.str.split('_')
Out[16]:
0 [a, b, c]
1 [c, d, e]
2 NaN
3 [f, g, h]
dtype: object
在分割后生成的列表中的元素可以通过str属性的get方法或者[ ]标记方法获取:
In [17]: s2.str.split('_').str.get(1)
Out[17]:
0 b
1 d
2 NaN
3 g
dtype: object
In [18]: s2.str.split('_').str[1]
Out[18]:
0 b
1 d
2 NaN
3 g
dtype: object
通过expand参数,也可以很容易的将split方法的结果转换为DataFrame对象.
In [19]: s2.str.split('_', expand=True)
Out[19]:
0 1 2
0 a b c
1 c d e
2 NaN NaN NaN
3 f g h
使用参数n也可以限制分割的次数.
In [20]: s2.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
rspilt方法与spilt方法是类似的,只是起始分割的方向不同,从字符的结束方向既右侧进行分割.
In [21]: s2.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方法默认情况下是使用正则表达式进行字符替换:
In [22]: s3 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca',
....: '', np.nan, 'CABA', 'dog', 'cat'])
....:
In [23]: s3
Out[23]:
0 A
1 B
2 C
3 Aaba
4 Baca
5
6 NaN
7 CABA
8 dog
9 cat
dtype: object
In [24]: s3.str.replace('^.a|dog', 'XX-XX ', case=False)
Out[24]:
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
dtype: object
在使用正则模式下的replace方式时,必须要注意正则表达式的正确性,特别是特殊字符的转义,比如:
# Consider the following badly formatted financial data
In [25]: dollars = pd.Series(['12', '-$10', '$10,000'])
# This does what you'd naively expect:
In [26]: dollars.str.replace('$', '')
Out[26]:
0 12
1 -10
2 10,000
dtype: object
# But this doesn't:
In [27]: dollars.str.replace('-$', '-')
Out[27]:
0 12
1 -$10
2 $10,000
dtype: object
# We need to escape the special character (for >1 len patterns)
In [28]: dollars.str.replace(r'-\$', '-')
Out[28]:
0 12
1 -10
2 $10,000
dtype: object
如果仅仅是想在字面上的替换字符,可以设置regex参数为False,这样类似python字符的replace方法,就不再涉及特殊字符的转义问题了,但需要注意的替换和别替换的都只能是纯字符:
# These lines are equivalent
In [29]: dollars.str.replace(r'-\$', '-')
Out[29]:
0 12
1 -10
2 $10,000
dtype: object
In [30]: dollars.str.replace('-$', '-', regex=False)
Out[30]:
0 12
1 -10
2 $10,000
dtype: object
replace方法同样也可以适应函数调用的模式.类似于对每一个匹配的模式进行re.sub方法.
被调用的函数只能接收一个位置参数且返回一个字符串.
# Reverse every lowercase alphabetic word
In [31]: pat = r'[a-z]+'
In [32]: repl = lambda m: m.group(0)[::-1]
In [33]: pd.Series(['foo 123', 'bar baz', np.nan]).str.replace(pat, repl)
Out[33]:
0 oof 123
1 rab zab
2 NaN
dtype: object
# Using regex groups
In [34]: pat = r"(?P\w+) (?P\w+) (?P\w+)"
In [35]: repl = lambda m: m.group('two').swapcase()
In [36]: pd.Series(['Foo Bar Baz', np.nan]).str.replace(pat, repl)
Out[36]:
0 bAR
1 NaN
dtype: object
replace方法也可以接受一个编译过的正则对象,并且该对象应该要包含匹配的正则模式:
In [37]: import re
In [38]: regex_pat = re.compile(r'^.a|dog', flags=re.IGNORECASE)
In [39]: s3.str.replace(regex_pat, 'XX-XX ')
Out[39]:
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
dtype: object
需要注意的是,如果使用编译后的正则对象,那么在replace方法中不能再包含代表正则匹配模式flag参数,否则会报错:
In [40]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
---------------------------------------------------------------------------
ValueError: case and flags cannot be set when pat is a compiled regex
在Pandas中,一个字符类型的Series对象或者Index对象可以将自身的元素串联成一个整字符,也可以和其他的字符进行串联.
这些功能的实现都基于cat()方法.(既str.cat)
使用cat方法,单个的字符类型的Series对象或者Index对象可以串联成字符串:
In [41]: s = pd.Series(['a', 'b', 'c', 'd'])
In [42]: s.str.cat(sep=',')
Out[42]: 'a,b,c,d'
链接符参数默认为sep='':
In [43]: s.str.cat()
Out[43]: 'abcd'
默认情况下,缺失值是被忽略的,但也可以通过na_rep参数指定其替换值:
In [44]: t = pd.Series(['a', 'b', np.nan, 'd'])
In [45]: t.str.cat(sep=',')
Out[45]: 'a,b,d'
In [46]: t.str.cat(sep=',', na_rep='-')
Out[46]: 'a,b,-,d'
Series对象和like-list对象使用str.cat方法的串联结果就是新的Series对象.
但前提是like-list对象的长度与Series对象一致.
In [47]: s.str.cat(['A', 'B', 'C', 'D'])
Out[47]:
0 aA
1 bB
2 cC
3 dD
dtype: object
同样na_rep参数也是适用的,如果存在缺失值,且未指定缺失值的替代值的话,那么与缺失对应的串联结果也将会是缺失值.
In [48]: s.str.cat(t)
Out[48]:
0 aa
1 bb
2 NaN
3 dd
dtype: object
In [49]: s.str.cat(t, na_rep='-')
Out[49]:
0 aa
1 bb
2 c-
3 dd
dtype: object
str.cat方法不仅可以串联一维的对象,也是可以串联二维对象的,但二维对象的行数也必须和Series对象一致:
In [50]: d = pd.concat([t, s], axis=1)
In [51]: s
Out[51]:
0 a
1 b
2 c
3 d
dtype: object
In [52]: d
Out[52]:
0 1
0 a a
1 b b
2 NaN c
3 d d
In [53]: s.str.cat(d, na_rep='-')
Out[53]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: object
在进行Series对象,Index对象,或者DataFrame对象的串联时,适用join参数可以设置索引对齐的方式.
In [54]: u = pd.Series(['b', 'd', 'a', 'c'], index=[1, 3, 0, 2])
In [55]: s
Out[55]:
0 a
1 b
2 c
3 d
dtype: object
In [56]: u
Out[56]:
1 b
3 d
0 a
2 c
dtype: object
In [57]: s.str.cat(u)
Out[57]:
0 ab
1 bd
2 ca
3 dc
dtype: object
In [58]: s.str.cat(u, join='left')
Out[58]:
0 aa
1 bb
2 cc
3 dd
dtype: object
注意,如果join参数没有指定的话,那么Pandas采用将以低于0.23版本的处理方式(既不进行索引对齐),但是会抛出一个警告.
在将来新版本的Pandas中,join参数的默认值将被设置为'left'.
join参数的选项包括:left,right,inner,outer.使用索引对齐的方式,就意味着数据的长度可以不一致了.
In [59]: v = pd.Series(['z', 'a', 'b', 'd', 'e'], index=[-1, 0, 1, 3, 4])
In [60]: s
Out[60]:
0 a
1 b
2 c
3 d
dtype: object
In [61]: v
Out[61]:
-1 z
0 a
1 b
3 d
4 e
dtype: object
In [62]: s.str.cat(v, join='left', na_rep='-')
Out[62]:
0 aa
1 bb
2 c-
3 dd
dtype: object
In [63]: s.str.cat(v, join='outer', na_rep='-')
Out[63]:
-1 -z
0 aa
1 bb
2 c-
3 dd
4 -e
dtype: object
即使串联对象是DataFrame也可以使用索引对齐:
In [64]: f = d.loc[[3, 2, 1, 0], :]
In [65]: s
Out[65]:
0 a
1 b
2 c
3 d
dtype: object
In [66]: f
Out[66]:
0 1
3 d d
2 NaN c
1 b b
0 a a
In [67]: s.str.cat(f, join='left', na_rep='-')
Out[67]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: object
通过列表传递的形式,任何的一维like-list类型的对象(甚至包括生成器)都可以被串联.
In [68]: s
Out[68]:
0 a
1 b
2 c
3 d
dtype: object
In [69]: u
Out[69]:
1 b
3 d
0 a
2 c
dtype: object
In [70]: s.str.cat([u, pd.Index(u.values), ['A', 'B', 'C', 'D'], map(str, u.index)], na_rep='-')
Out[70]:
0 abbA1
1 bddB3
2 caaC0
3 dccD2
dtype: object
当然在不设置join参数的前提下,所以串联对象的长度应该一致.但如果设置了join参数,那么变启用的索引对齐.
In [71]: v
Out[71]:
-1 z
0 a
1 b
3 d
4 e
dtype: object
In [72]: s.str.cat([u, v, ['A', 'B', 'C', 'D']], join='outer', na_rep='-')
Out[72]:
-1 --z-
0 aaaA
1 bbbB
2 cc-C
3 dddD
4 --e-
dtype: object
如果使用列表传递的多个对象的索引完全不同,且使用了join='right'设置,那么这些索引的联合将用作最终连接的基础:
In [73]: u.loc[[3]]
Out[73]:
3 d
dtype: object
In [74]: v.loc[[-1, 0]]
Out[74]:
-1 z
0 a
dtype: object
In [75]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join='right', na_rep='-')
Out[75]:
-1 --z
0 a-a
3 dd-
dtype: object
.str
)可以直接使用[ ]标识法结合位置信息对对象进行索引,如果指定的位置信息越界了,那么将把缺失值作为对应的返回值.
In [76]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan,
....: 'CABA', 'dog', 'cat'])
....:
In [77]: s.str[0]
Out[77]:
0 A
1 B
2 C
3 A
4 B
5 NaN
6 C
7 d
8 c
dtype: object
In [78]: s.str[1]
Out[78]:
0 NaN
1 NaN
2 NaN
3 a
4 a
5 NaN
6 A
7 o
8 a
dtype: object
注意,从0.18版本的Pandas其,exreact方法多了一个expand参数.当expand参数设置为False时,extract方法的返回对象类型(Series,Index,DataFrame)取决于对象和匹配表达式的设置,而expand设置为True是,返回对象用于是DataFrame.但从0.23版本的Pandas起,expand参数默认设置为True.
extract方法接收至少有一个分组的正则表达式.
如果表达式是多个分组的,那么每个分组的结果都会单独成为一列.
In [79]: pd.Series(['a1', 'b2', 'c3']).str.extract('([ab])(\d)', expand=False)
Out[79]:
0 1
0 a 1
1 b 2
2 NaN NaN
没有元素被匹配到的行将被填充缺失值,因此,内容混杂的Series对象可以“转换”为类似索引的字符串Series对象或清理后的DataFrame对象或者更有用的字符串,而不需要通过get()方法去访问元组或re.match对象。提取的结果的dtype类型总是object,甚至没有匹配任何结果那么结果将只包含NaN。
命名分组:
In [80]: pd.Series(['a1', 'b2', 'c3']).str.extract('(?P[ab])(?P\d)', expand=False)
Out[80]:
letter digit
0 a 1
1 b 2
2 NaN NaN
可选分组:
In [81]: pd.Series(['a1', 'b2', '3']).str.extract('([ab])?(\d)', expand=False)
Out[81]:
0 1
0 a 1
1 b 2
2 NaN 3
注意,正则表达式中的任何分组名都将作为列名;否则将使用分组的编号.
如果expand=True,只有一个分组的正则表达式提取的返回结果将是只有一列的DataFrame。
In [82]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=True)
Out[82]:
0
0 1
1 2
2 NaN
如果expand=False,那么只有一个分组的正则表达式提取的返回结果将是Series
In [83]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=False)
Out[83]:
0 1
1 2
2 NaN
dtype: object
在Index对象上使用只有一个分组的正则表达式进行提取,在expand=True时也只返回一个只有一列的DataFrame。
In [84]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"])
In [85]: s
Out[85]:
A11 a1
B22 b2
C33 c3
dtype: object
In [86]: s.index.str.extract("(?P[a-zA-Z])", expand=True)
Out[86]:
letter
0 A
1 B
2 C
在同样的情况下,expand=False,那么返回的就是Index对象.
In [87]: s.index.str.extract("(?P[a-zA-Z])", expand=False)
Out[87]: Index(['A', 'B', 'C'], dtype='object', name='letter')
在Index对象上使用多分组的正则表达式进行提取,在expand=True时返回的也是DataFrame。
In [88]: s.index.str.extract("(?P[a-zA-Z])([0-9]+)", expand=True)
Out[88]:
letter 1
0 A 11
1 B 22
2 C 33
但在同样的情况下,将expand设置为False的话,将报错:
>>> s.index.str.extract("(?P[a-zA-Z])([0-9]+)", expand=False)
ValueError: only one regex group is supported with Index
下面这张表总结了expand=False时,不同对象在不同分组表达式情况下的返回值的类型:
对象类型 | 一个分组 | 多个分组 |
---|---|---|
Index | Index | ValueError |
Series | Series | DataFrame |
不同于extract方法,extractall方法将返回每一个匹配项,返回结果总是一个多层次索引的DataFrame对象.
MultiIndex索引的最后一层名为match,表示主对象中的匹配的顺序。
In [93]: s.str.extractall(two_groups)
Out[93]:
letter digit
match
A 0 a 1
1 a 2
B 0 b 1
C 0 c 1
当对象只有一个匹配项时, extractall(pat).xs(0,level='match')方法的结果将与extract(pat)方法的结果一致:
In [94]: s = pd.Series(['a3', 'b3', 'c2'])
In [95]: s
Out[95]:
0 a3
1 b3
2 c2
dtype: object
In [96]: extract_result = s.str.extract(two_groups, expand=True)
In [97]: extract_result
Out[97]:
letter digit
0 a 3
1 b 3
2 c 2
In [98]: extractall_result = s.str.extractall(two_groups)
In [99]: extractall_result
Out[99]:
letter digit
match
0 0 a 3
1 0 b 3
2 0 c 2
In [100]: extractall_result.xs(0, level="match")
Out[100]:
letter digit
0 a 3
1 b 3
2 c 2
Index对象同样也支持extracall方法,它返回一个DataFrame,其结果与Series.str相同。使用默认索引提取数据(从0开始)
In [101]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[101]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
In [102]: pd.Series(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[102]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
可以测试对象是否包含某个匹配模式:
In [103]: pattern = r'[0-9][a-z]'
In [104]: pd.Series(['1', '2', '3a', '3b', '03c']).str.contains(pattern)
Out[104]:
0 False
1 False
2 True
3 True
4 True
dtype: bool
或者测试是否匹配某个模式:
In [105]: pd.Series(['1', '2', '3a', '3b', '03c']).str.match(pattern)
Out[105]:
0 False
1 False
2 True
3 True
4 False
dtype: bool
这两个方法的严格性不同.match方法类似于re.match,而contain方法类似于re.search.
这些方法,如match、contains、startswith和endswith,都有一个额外的na参数,这样缺失值可以指定为True或False
In [106]: s4 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [107]: s4.str.contains('A', na=False)
Out[107]:
0 True
1 False
2 False
3 True
4 False
5 False
6 True
7 False
8 False
dtype: bool
也可以从提取虚拟变量,例如:
In [108]: s = pd.Series(['a', 'a|b', np.nan, 'a|c'])
In [109]: s.str.get_dummies(sep='|')
Out[109]:
a b c
0 1 0 0
1 1 1 0
2 0 0 0
3 1 0 1
字符串的处理Index对象也支持get_dummies方法,它将返回一个多层索引。
In [110]: idx = pd.Index(['a', 'a|b', np.nan, 'a|c'])
In [111]: idx.str.get_dummies(sep='|')
Out[111]:
MultiIndex(levels=[[0, 1], [0, 1], [0, 1]],
labels=[[1, 1, 0, 1], [0, 1, 0, 0], [0, 0, 0, 1]],
names=['a', 'b', 'c'])
方法名 | 作用描述 |
---|---|
cat() |
串联字符 |
split() |
分割字符 |
rsplit() |
由右侧起分割字符 |
get() |
通过索引获取元素 |
join() |
用指定的分隔符连接对象中每个元素中的字符串 |
get_dummies() |
使用分隔字符串,返回虚拟变量的DataFrame |
contains() |
判断是否包含指定的模式 |
replace() |
替换字符 |
repeat() |
复制重复字符(s.str.repeat(3) 相当于 x * 3 )) |
pad() |
在字符串的左、右或两边添加空格 |
center() |
类似python字符的center方法 |
ljust() |
类似python字符的ljust方法 |
rjust() |
类似python字符的rjust方法 |
zfill() |
类似python字符的zfill方法 |
wrap() |
将长字符串分割成长度小于指定宽度的行 |
slice() |
对Series对象中的每个字符进行切片 |
slice_replace() |
使用指定的值替换切片的每个值 |
count() |
统计模式匹配的次数 |
startswith() |
对每个元素使用类似python字符方法startswith |
endswith() |
对每个元素使用类似python字符方法endswith |
findall() |
列表形式返回所有能够匹配指定模式的字符串,类似re.findall() |
match() |
对每个元素级调用re.match,以列表形式返回结果 |
extract() |
对每个元素调用re.search,返回DataFrame,每个元素一行,每个正则表达式分组一列 |
extractall() |
对每个元素调用re.findall,为每个匹配返回一行数据,为每个正则表达式分组返回一列数据 |
len() |
计算字符串长度 |
strip() |
类似python字符的strip方法 |
rstrip() |
类似python字符的rstrip方法 |
lstrip() |
类似python字符的lstrip方法 |
partition() |
类似python字符的patition方法 |
rpartition() |
类似python字符的rpartition方法 |
lower() |
类似python字符的lower方法 |
upper() |
类似python字符的upper方法 |
find() |
类似python字符的find方法 |
rfind() |
类似python字符的rfind方法 |
index() |
类似python字符的index方法 |
rindex() |
类似python字符的rindex方法 |
capitalize() |
类似python字符的captalize方法 |
swapcase() |
类似python字符的swapcase方法 |
translate() |
类似python字符的translate方法 |
isalnum() |
类似python字符的isalnum方法 |
isalpha() |
类似python字符的isalpha方法 |
isdigit() |
类似python字符的isdigit方法 |
isspace() |
类似python字符的isspace方法 |
islower() |
类似python字符的islower方法 |
isupper() |
类似python字符的isupper方法 |
istitle() |
类似python字符的istitle方法 |
isnumeric() |
类似python字符的isnumeric方法 |
isdecimal() |
类似python字符的isdecimal方法 |