text
Pandas 处理文本字符串
序列和索引包含一些列的字符操作方法,这可以使我们轻易操作数组中的各个元素。最重要的是,这些方法可以自动跳过 缺失/NA 值。这些方法可以在str属性中访问到,并且基本上和python内建的(标量)字符串方法同名:
1 | In [1]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat']) |
1 | In [5]: idx = pd.Index([' jack', 'jill ', ' jesse ', 'frank']) |
索引的字符串方法在清理或者转换数据表列的时候非常有用。例如,你的列中或许会包含首位的白空格:
1 | In [9]: df = pd.DataFrame(np.random.randn(3, 2), |
Since df.columns is an Index object, we can use the .str accessor
1 | In [11]: df.columns.str.strip() |
这些字符串方法可以被用来清理需要的列。这里,我们想清理开头和结尾的白空格,将所有的名称都换为小写,并且将其余的空格都替换为下划线:
1 | In [13]: df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_') |
::: tip 小贴士
如果你有一个序列,里面有很多重复的值
(即,序列中唯一元素的数量远小于序列的长度),将原有序列转换为一种分类类型,然后使用.str. 或者 .dt.方法,则会获得更快的速度。
速度的差异来源于,在分类类型的序列中,字符操作只是在categories中完成的,而不是针对序列中的每一个元素。
请注意,相比于字符串类型的序列,带.categories类型的 分类 类别的 序列有一些限制(例如,你不能像其中的元素追加其他的字串:s + " " + s 将不能正确工作,如果s是一个分类类型的序列。并且,.str 中,那些可以对 列表(list) 类型的元素进行操作的方法,在分类序列中也无法使用。
:::
::: danger 警告
v.0.25.0版以前, .str访问器只会进行最基本的类型检查。
从v.0.25.0起,序列的类型会被自动推断出来,并且会更为激进地使用恰当的类型。
一般来说 .str 访问器只倾向于针对字符串类型工作。只有在个别的情况下,才能对非字符串类型工作,但是这也将会在未来的版本中被逐步修正
:::
拆分和替换字符串
类似split的方法返回一个列表类型的序列:
1 | In [15]: s2 = pd.Series(['a_b_c', 'c_d_e', np.nan, 'f_g_h']) |
切分后的列表中的元素可以通过 get 方法或者 [] 方法进行读取:
1 | In [17]: s2.str.split('_').str.get(1) |
使用expand方法可以轻易地将这种返回展开为一个数据表.
1 | In [19]: s2.str.split('_', expand=True) |
同样,我们也可以限制切分的次数:
1 | In [20]: s2.str.split('_', expand=True, n=1) |
rsplit与split相似,不同的是,这个切分的方向是反的。即,从字串的尾端向首段切分:
1 | In [21]: s2.str.rsplit('_', expand=True, n=1) |
replace 方法默认使用 正则表达式:
1 | In [22]: s3 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', |
一定要时时记得,是正则表达式,因此要格外小心。例如,因为正则表达式中的*$*符号,下列代码将会导致一些麻烦:
1 | # Consider the following badly formatted financial data |
v0.23.0. 新加入
如果你只是向单纯地替换字符 (等价于python中的str.replace()),你可以将可选参数 regex 设置为 False,而不是傻傻地转义所有符号。这种情况下,pat 和 repl 就都将作为普通字符对待:
1 | # These lines are equivalent |
v0.20.0. 新加入
replace 方法也可以传入一个可调用对象作为替换值。它针对每一个 pat 通过re.sub()来调用。可调用对象应只具有一个形参(一个正则表达式对象)并且返回一个字符串。
1 | # Reverse every lowercase alphabetic word |
v0.20.0. 新加入
replace 方法也可以接受一个来自re.compile() 编译过的正则表达式对象,来做为表达式。所有的标记都应该被包含在这个已经编译好的正则表达式对象中。
1 | In [37]: import re |
如果在已经使用编译的正则对象中继续传入flags 参数,并进行替换,将会导致ValueError。
1 | In [40]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE) |
拼接
Pandas提供了不同的方法将序列或索引与他们自己或者其他的对象进行拼接,所有的方法都是基于各自的cat(),
resp. Index.str.cat.
将单个序列拼接为一个完整字符串
序列或索引的内容可以进行拼接:
1 | In [41]: s = pd.Series(['a', 'b', 'c', 'd']) |
如果没有额外声明,sep 即分隔符默认为空字串,即sep='':
1 | In [43]: s.str.cat() |
默认情况下,缺失值会被忽略。使用na_rep参数,可以对缺失值进行赋值:
1 | In [44]: t = pd.Series(['a', 'b', np.nan, 'd']) |
拼接序列和其他类列表型对象为新的序列
cat() 的第一个参数为类列表对象,但必须要确保长度与序列或索引相同.
1 | In [47]: s.str.cat(['A', 'B', 'C', 'D']) |
任何一端的缺失值都会导致之中结果为缺失值,除非使用na_rep:
1 | In [48]: s.str.cat(t) |
拼接序列与类数组对象为新的序列
v0.23.0. 新加入
others 参数可以是二维的。此时,行数需要与序列或索引的长度相同。
1 | In [50]: d = pd.concat([t, s], axis=1) |
对齐拼接序列与带索引的对象成为新的序列
v0.23.0.新加入
对于拼接序列或者数据表,我们可以使用 join关键字来对齐索引。
1 | In [54]: u = pd.Series(['b', 'd', 'a', 'c'], index=[1, 3, 0, 2]) |
::: danger 警告
如果不使用join 关键字, cat() 方法将会滚回到0.23.0版之前,即(无对齐)模式。但如果任何的索引不一致时,将会抛出一个FutureWarning 警告,因为在未来的版本中,默认行为将改为join=’left’ 。
:::
join 的选项为('left', 'outer', 'inner', 'right')中的一个。
特别的,对齐操作使得两个对象可以是不同的长度。
1 | In [59]: v = pd.Series(['z', 'a', 'b', 'd', 'e'], index=[-1, 0, 1, 3, 4]) |
当others是一个数据表时,也可以执行相同的对齐操作:
1 | In [64]: f = d.loc[[3, 2, 1, 0], :] |
将一个序列与多个对象拼接为一个新的序列
所有的一维,类列表对象都可以任意组合进一个类列表的容器(包括迭代器,dict-视图等):
1 | In [68]: s |
除了那些有索引的,所有传入没有索引的元素(如np.ndarray)必须与序列或索引有相同的长度。但是,只要禁用对齐join=None,那么序列或索引就可以是任意长度。
1 | In [71]: v |
如果在一个包含不同的索引的others列表上使用join='right',所有索引的并集将会被作为最终拼接的基础:
1 | In [73]: u.loc[[3]] |
使用.str进行索引
你可以使用 []方法来直接索引定位。如果你的索引超过了字符串的结尾,将返回NaN。
1 | In [76]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, |
提取子字符串
提取第一个匹配的对象 (extract)
::: danger 警告
在 0.18.0中,extract拥有了 expand 参数。当 expand=False时, 将返回一个序列,索引或者数据表, 这取决于原对象和正则表达式(之前的版本也是如此)。当 expand=True时,它则总是返回一个DataFrame,这样可以更加一致,并且减少用户的混淆。 Expand=True 从0.23.0版本之后成为默认值。
:::
extract 方法接受一个至少含有一个捕获组的 正则表达式。
使用超过一个捕获组的正则表达式则会提取并返回一个数据表,每一列为一个捕获组。
1 | In [79]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'([ab])(\d)', expand=False) |
没有成功匹配的元素将会返回一行NaN。因此,一个序列的混乱的字符串可以被‘转换’为一个类似索引的序列或数据表。返回的内容会更为清爽,而且不需要使用get()方法来访问元组中的成员或者re.match对象。返回的类型将总是object类,即使匹配失败,返回的全是NaN。
有名称的捕获组,如:
1 | In [80]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'(?P<letter>[ab])(?P<digit>\d)', |
可选组类似,如:
1 | In [81]: pd.Series(['a1', 'b2', '3']).str.extract(r'([ab])?(\d)', expand=False) |
也可以被使用。注意,任何有名称的捕获组,其名称都会被用做列名,否则将会直接使用数字。
如果仅使用正则表达式捕获一个组,而expand=True,那么仍然将返回一个数据表。
1 | In [82]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'[ab](\d)', expand=True) |
如果expand=False,则会返回一个序列。
1 | In [83]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'[ab](\d)', expand=False) |
在索引上使用正则表达式,并且仅捕获一个组时,将会返回一个数据表,如果expand=True。
1 | In [84]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"]) |
如果expand=False,则返回一个Index。
1 | In [87]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False) |
如果在索引上使用正则并捕获多个组,则返回一个数据表,如果expand=True。
1 | In [88]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True) |
如果 expand=False,则抛出ValueError。
1 | s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False) |
下面的表格总结了extract (expand=False)时的行为(输入对象在第一列,捕获组的数量在第一行)
| 1 group | >1 group
—|—|—
Index | Index | ValueError
Series | Series | DataFrame
提取所有的匹配 (extractall)
v0.18.0. 新加入
不同于 extract(只返回第一个匹配),
1 | In [89]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"]) |
extractall方法返回所有的匹配。extractall总是返回一个带有行多重索引的数据表,最后一级多重索引被命名为match,它指出匹配的顺序
1 | In [93]: s.str.extractall(two_groups) |
当所有的对象字串都只有一个匹配时,
1 | In [94]: s = pd.Series(['a3', 'b3', 'c2']) |
extractall(pat).xs(0, level='match') 的返回与extract(pat)相同。
1 | In [96]: extract_result = s.str.extract(two_groups, expand=True) |
索引也支持.str.extractall。 它返回一个数据表,其中包含与Series.str.estractall相同的结果,使用默认索引(从0开始)
v0.19.0. 新加入
1 | In [101]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups) |
测试匹配或包含模式的字符串
你可以检查是否一个元素包含一个可以匹配到的正则表达式:
1 | In [103]: pattern = r'[0-9][a-z]' |
或者是否元素完整匹配一个正则表达式
1 | In [105]: pd.Series(['1', '2', '3a', '3b', '03c']).str.match(pattern) |
match和contains的区别是是否严格匹配。match严格基于re.match,而contains基于re.search。
类似match, contains, startswith 和 endswith 可以传入一个额外的na参数,因此,因此缺失值在匹配时可以被认为是True或者False:
1 | In [106]: s4 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat']) |
建立一个指示变量
你从字符串列可以抽出一个哑变量。例如,是否他们由|分割:
1 | In [108]: s = pd.Series(['a', 'a|b', np.nan, 'a|c']) |
索引也支持get_dummies,它返回一个多重索引:
v0.18.1. 新加入
1 | In [110]: idx = pd.Index(['a', 'a|b', np.nan, 'a|c']) |
参见 get_dummies().
方法总览
| 方法 | 描述 |
|---|---|
| cat() | 拼接字符串 |
| split() | 基于分隔符切分字符串 |
| rsplit() | 基于分隔符,逆向切分字符串 |
| get() | 索引每一个元素(返回第i个元素) |
| join() | 使用传入的分隔符依次拼接每一个元素 |
| get_dummies() | 用分隔符切分字符串,并返回一个含有哑变量的数据表 |
| contains() | 返回一个布尔矩阵表明是每个元素包含字符串或正则表达式 |
| replace() | 将匹配到的子串或正则表达式替换为另外的字符串,或者一个可调用对象的返回值 |
| repeat() | 值复制(s.str.repeat(3)等价于x * 3) |
| pad() | 将白空格插入到字符串的左、右或者两端 |
| center() | 等价于str.center |
| ljust() | 等价于str.ljust |
| rjust() | 等价于str.rjust |
| zfill() | 等价于str.zfill |
| wrap() | 将长字符串转换为不长于给定长度的行 |
| slice() | 将序列中的每一个字符串切片 |
| slice_replace() | 用传入的值替换每一个字串中的切片 |
| count() | 对出现符合的规则进行计数 |
| startswith() | 等价于str.startswith(pat) |
| endswith() | 等价于 str.endswith(pat) |
| findall() | 返回每一个字串中出现的所有满足样式或正则的匹配 |
| match() | 素调用 re.match,并以列表形式返回匹配到的组 |
| extract() | Call 对每一个元素调用 re.search, 并以数据表的形式返回。行对应原有的一个元素,列对应所有捕获的组 |
| extractall() | 一个元素调用 re.findall, 并以数据表的形式返回。行对应原有的一个元素,列对应所有捕获的组 |
| len() | 计算字符串长度 |
| strip() | 等价于str.strip |
| rstrip() | 等价于str.rstrip |
| lstrip() | 等价于str.lstrip |
| partition() | 等价于 str.partition |
| rpartition() | 等价于 str.rpartition |
| lower() | 等价于 str.lower |
| casefold() | 等价于 str.casefold |
| upper() | 等价于 str.upper |
| find() | 等价于str.find |
| rfind() | 等价于 str.rfind |
| index() | 等价于 str.index |
| rindex() | 等价于 str.rindex |
| capitalize() | 等价于 str.capitalize |
| swapcase() | 等价于 str.swapcase |
| normalize() | 返回Unicode 标注格式。等价于 unicodedata.normalize |
| translate() | 等价于 str.translate |
| 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 |









