Pandas处理文本数据

概述

在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

字符分割和替换字符(Splitting and Replacing Strings)

和字符串的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

字符的串联的(Concatenation)

在Pandas中,一个字符类型的Series对象或者Index对象可以将自身的元素串联成一个整字符,也可以和其他的字符进行串联.

这些功能的实现都基于cat()方法.(既str.cat)

单个对象串联成字符串(Concatenating a single Series into a string)

使用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对象的串联(Concatenating a Series and something list-like into a Series)

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

Series对象和like-array对象的串联(Concatenating a Series and something array-like into a Series)

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对象对齐串联(Concatenating a Series and an indexed object into a Series, with alignment)

在进行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

Series对象与多种类型对象的串联(Concatenating a Series and many objects into a Series)

通过列表传递的形式,任何的一维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进行索引(Indexing with .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

提取子字符串(Extracting Substrings)

提取每个对象的第一个匹配项(Extract first match in each subject)

注意,从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 all matches in each subject (extractall))

不同于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

字符匹配或包含模式的测试(Testing for Strings that Match or Contain a Pattern)

可以测试对象是否包含某个匹配模式:

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

创建指示变量(Creating Indicator Variables)

也可以从提取虚拟变量,例如:

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方法

 

 

你可能感兴趣的:(Pandas,Pandas字符处理,Pandas字符方法)