概述
# Pandas 处理文本字符串
序列和索引包含一些列的字符操作方法,这可以使我们轻易操作数组中的各个元素。最重要的是,这些方法可以自动跳过 缺失/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')
索引的字符串方法在清理或者转换数据表列的时候非常有用。例如,你的列中或许会包含首位的白空格:
In [9]: df = pd.DataFrame(np.random.randn(3, 2),
...: columns=[' Column A ', ' Column B '], index=range(3))
...:
In [10]: df
Out[10]:
Column A Column B
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
Since df.columns is an Index object, we can use the .str accessor
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 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
小贴士
如果你有一个序列,里面有很多重复的值
(即,序列中唯一元素的数量远小于序列的长度),将原有序列转换为一种分类类型,然后使用.str. 或者 .dt.方法,则会获得更快的速度。
速度的差异来源于,在分类类型的序列中,字符操作只是在categories中完成的,而不是针对序列中的每一个元素。
请注意,相比于字符串类型的序列,带.categories类型的 分类 类别的 序列有一些限制(例如,你不能像其中的元素追加其他的字串:s + " " + s 将不能正确工作,如果s是一个分类类型的序列。并且,.str 中,那些可以对 列表(list) 类型的元素进行操作的方法,在分类序列中也无法使用。
警告
v.0.25.0版以前, .str访问器只会进行最基本的类型检查。
从v.0.25.0起,序列的类型会被自动推断出来,并且会更为激进地使用恰当的类型。
一般来说 .str 访问器只倾向于针对字符串类型工作。只有在个别的情况下,才能对非字符串类型工作,但是这也将会在未来的版本中被逐步修正
# 拆分和替换字符串
类似split的方法返回一个列表类型的序列:
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
切分后的列表中的元素可以通过 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方法可以轻易地将这种返回展开为一个数据表.
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
同样,我们也可以限制切分的次数:
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
rsplit与split相似,不同的是,这个切分的方向是反的。即,从字串的尾端向首段切分:
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
一定要时时记得,是正则表达式,因此要格外小心。例如,因为正则表达式中的*$*符号,下列代码将会导致一些麻烦:
# 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
v0.23.0. 新加入
如果你只是向单纯地替换字符 (等价于python中的
str.replace()),你可以将可选参数 regex 设置为 False,而不是傻傻地转义所有符号。这种情况下,pat 和 repl 就都将作为普通字符对待:
# 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
v0.20.0. 新加入
replace 方法也可以传入一个可调用对象作为替换值。它针对每一个 pat 通过re.sub()来调用。可调用对象应只具有一个形参(一个正则表达式对象)并且返回一个字符串。
# Reverse every lowercase alphabetic word
In [31]: pat = r'[a-z]+'
In [32]: def repl(m):
....: return 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"(?Pw+) (?Pw+) (?Pw+)"
In [35]: def repl(m):
....: return 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
v0.20.0. 新加入
replace 方法也可以接受一个来自re.compile() 编译过的正则表达式对象,来做为表达式。所有的标记都应该被包含在这个已经编译好的正则表达式对象中。
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
如果在已经使用编译的正则对象中继续传入flags 参数,并进行替换,将会导致ValueError。
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提供了不同的方法将序列或索引与他们自己或者其他的对象进行拼接,所有的方法都是基于各自的cat(),
resp. Index.str.cat.
# 将单个序列拼接为一个完整字符串
序列或索引的内容可以进行拼接:
In [41]: s = pd.Series(['a', 'b', 'c', 'd'])
In [42]: s.str.cat(sep=',')
Out[42]: 'a,b,c,d'
如果没有额外声明,sep 即分隔符默认为空字串,即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'
# 拼接序列和其他类列表型对象为新的序列
cat() 的第一个参数为类列表对象,但必须要确保长度与序列或索引相同.
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
# 拼接序列与类数组对象为新的序列
v0.23.0. 新加入
others 参数可以是二维的。此时,行数需要与序列或索引的长度相同。
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
# 对齐拼接序列与带索引的对象成为新的序列
v0.23.0.新加入
对于拼接序列或者数据表,我们可以使用 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 关键字, cat() 方法将会滚回到0.23.0版之前,即(无对齐)模式。但如果任何的索引不一致时,将会抛出一个
FutureWarning 警告,因为在未来的版本中,默认行为将改为join='left' 。
join 的选项为('left', 'outer', 'inner', 'right')中的一个。
特别的,对齐操作使得两个对象可以是不同的长度。
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
当others是一个数据表时,也可以执行相同的对齐操作:
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
# 将一个序列与多个对象拼接为一个新的序列
所有的一维,类列表对象都可以任意组合进一个类列表的容器(包括迭代器,dict-视图等):
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, u.to_numpy()], join='left')
Out[70]:
0 aab
1 bbd
2 cca
3 ddc
dtype: object
除了那些有索引的,所有传入没有索引的元素(如np.ndarray)必须与序列或索引有相同的长度。但是,只要禁用对齐join=None,那么序列或索引就可以是任意长度。
In [71]: v
Out[71]:
-1 z
0 a
1 b
3 d
4 e
dtype: object
In [72]: s.str.cat([v, u, u.to_numpy()], join='outer', na_rep='-')
Out[72]:
-1 -z--
0 aaab
1 bbbd
2 c-ca
3 dddc
4 -e--
dtype: object
如果在一个包含不同的索引的others列表上使用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进行索引
你可以使用 []方法来直接索引定位。如果你的索引超过了字符串的结尾,将返回NaN。
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
# 提取子字符串
# 提取第一个匹配的对象 (extract)
警告
在 0.18.0中,extract拥有了 expand 参数。当 expand=False时, 将返回一个序列,索引或者数据表, 这取决于原对象和正则表达式(之前的版本也是如此)。当 expand=True时,它则总是返回一个DataFrame,这样可以更加一致,并且减少用户的混淆。 Expand=True 从0.23.0版本之后成为默认值。
extract 方法接受一个至少含有一个捕获组的 正则表达式。
使用超过一个捕获组的正则表达式则会提取并返回一个数据表,每一列为一个捕获组。
In [79]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'([ab])(d)', expand=False)
Out[79]:
0 1
0 a 1
1 b 2
2 NaN NaN
没有成功匹配的元素将会返回一行NaN。因此,一个序列的混乱的字符串可以被‘转换’为一个类似索引的序列或数据表。返回的内容会更为清爽,而且不需要使用get()方法来访问元组中的成员或者re.match对象。返回的类型将总是object类,即使匹配失败,返回的全是NaN。
有名称的捕获组,如:
In [80]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'(?P[ab])(?Pd)',
....: expand=False)
....:
Out[80]:
letter digit
0 a 1
1 b 2
2 NaN NaN
可选组类似,如:
In [81]: pd.Series(['a1', 'b2', '3']).str.extract(r'([ab])?(d)', expand=False)
Out[81]:
0 1
0 a 1
1 b 2
2 NaN 3
也可以被使用。注意,任何有名称的捕获组,其名称都会被用做列名,否则将会直接使用数字。
如果仅使用正则表达式捕获一个组,而expand=True,那么仍然将返回一个数据表。
In [82]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'[ab](d)', expand=True)
Out[82]:
0
0 1
1 2
2 NaN
如果expand=False,则会返回一个序列。
In [83]: pd.Series(['a1', 'b2', 'c3']).str.extract(r'[ab](d)', expand=False)
Out[83]:
0 1
1 2
2 NaN
dtype: object
在索引上使用正则表达式,并且仅捕获一个组时,将会返回一个数据表,如果expand=True。
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')
如果在索引上使用正则并捕获多个组,则返回一个数据表,如果expand=True。
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,则抛出ValueError。
>>> s.index.str.extract("(?P[a-zA-Z])([0-9]+)", expand=False)
ValueError: only one regex group is supported with Index
下面的表格总结了extract (expand=False)时的行为(输入对象在第一列,捕获组的数量在第一行) 1 group >1 group Index Index
Series Series
# 提取所有的匹配 (extractall)
v0.18.0. 新加入
不同于 extract(只返回第一个匹配),
In [89]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"])
In [90]: s
Out[90]:
A a1a2
B b1
C c1
dtype: object
In [91]: two_groups = '(?P[a-z])(?P[0-9])'
In [92]: s.str.extract(two_groups, expand=True)
Out[92]:
letter digit
A a 1
B b 1
C c 1
extractall方法返回所有的匹配。extractall总是返回一个带有行多重索引的数据表,最后一级多重索引被命名为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
当所有的对象字串都只有一个匹配时,
In [94]: s = pd.Series(['a3', 'b3', 'c2'])
In [95]: s
Out[95]:
0 a3
1 b3
2 c2
dtype: object
extractall(pat).xs(0, level='match') 的返回与extract(pat)相同。
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
索引也支持.str.extractall。 它返回一个数据表,其中包含与Series.str.estractall相同的结果,使用默认索引(从0开始)
v0.19.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和contains的区别是是否严格匹配。match严格基于re.match,而contains基于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
索引也支持get_dummies,它返回一个多重索引:
v0.18.1. 新加入
In [110]: idx = pd.Index(['a', 'a|b', np.nan, 'a|c'])
In [111]: idx.str.get_dummies(sep='|')
Out[111]:
MultiIndex([(1, 0, 0),
(1, 1, 0),
(0, 0, 0),
(1, 0, 1)],
names=['a', 'b', 'c'])
# 方法总览 方法 描述 拼接字符串
基于分隔符切分字符串
基于分隔符,逆向切分字符串
索引每一个元素(返回第i个元素)
使用传入的分隔符依次拼接每一个元素
用分隔符切分字符串,并返回一个含有哑变量的数据表
返回一个布尔矩阵表明是每个元素包含字符串或正则表达式
将匹配到的子串或正则表达式替换为另外的字符串,或者一个可调用对象的返回值
值复制(s.str.repeat(3)等价于x * 3)
将白空格插入到字符串的左、右或者两端
等价于str.center
等价于str.ljust
等价于str.rjust
等价于str.zfill
将长字符串转换为不长于给定长度的行
将序列中的每一个字符串切片
用传入的值替换每一个字串中的切片
对出现符合的规则进行计数
等价于str.startswith(pat)
等价于 str.endswith(pat)
返回每一个字串中出现的所有满足样式或正则的匹配
素调用 re.match,并以列表形式返回匹配到的组
Call 对每一个元素调用 re.search, 并以数据表的形式返回。行对应原有的一个元素,列对应所有捕获的组
一个元素调用 re.findall, 并以数据表的形式返回。行对应原有的一个元素,列对应所有捕获的组
计算字符串长度
等价于str.strip
等价于str.rstrip
等价于str.lstrip
等价于 str.partition
等价于 str.rpartition
等价于 str.lower
等价于 str.casefold
等价于 str.upper
等价于str.find
等价于 str.rfind
等价于 str.index
等价于 str.rindex
等价于 str.capitalize
等价于 str.swapcase
返回Unicode 标注格式。等价于 unicodedata.normalize
等价于 str.translate
等价于 str.isalnum
等价于 str.isalpha
等价于 str.isdigit
等价于 str.isspace
等价于 str.islower
等价于 str.isupper
等价于 str.istitle
等价于 str.isnumeric
等价于 str.isdecimal
最后
以上就是独特西牛为你收集整理的pandas 字符串切片后保存_Pandas 处理文本字符串的全部内容,希望文章能够帮你解决pandas 字符串切片后保存_Pandas 处理文本字符串所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复