使用文本数据#
版本 3.0 中已更改: 字符串的推断和行为在 pandas 3.0 中发生了重大变化。请参阅 新字符串数据类型迁移指南(pandas 3.0)。
文本数据类型#
pandas 中存储文本数据有两种方法
StringDtype扩展类型。NumPy
object数据类型。
我们建议使用 StringDtype 来存储文本数据,使用别名 dtype="str"(字符串数据类型推断时的默认值),有关更多详细信息,请参阅下文。
在 pandas 1.0 之前,object 数据类型是唯一的选择。这由于多种原因而不幸
您可能会不小心将字符串和非字符串的混合体存储在
object数据类型数组中。最好有一个专门的数据类型。object数据类型会破坏特定数据类型操作,例如DataFrame.select_dtypes()。没有明确的方法可以选择仅文本而排除非文本但仍然是 object 数据类型的列。在阅读代码时,
object数据类型数组的内容不如'string'清晰。
当使用 StringDtype 并以 PyArrow 作为存储时(见下文),用户将看到与 object 数据类型数组相比,内存使用和某些操作的时间方面都有很大的性能提升。当不使用 PyArrow 作为存储时,StringDtype 的性能与 object 大致相同。我们预计未来的增强功能将显著提高 StringDtype 在此情况下的性能并降低内存开销。
版本 3.0 中已更改: 当 pandas 推断一组字符串的数据类型时,默认使用 dtype='str'。这将使用 np.nan 作为其 NA 值,并在安装了 PyArrow 时由 PyArrow 字符串数组支持,或者在未安装 PyArrow 时由 NumPy object 数组支持。
In [1]: pd.Series(["a", "b", "c"])
Out[1]:
0 a
1 b
2 c
dtype: str
显式指定 StringDtype#
当希望显式指定数据类型时,如果您希望将 np.nan 作为 NA 值,我们通常建议使用别名 dtype="str";如果您希望将 pd.NA 作为 NA 值,则建议使用别名 dtype="string"。
In [2]: pd.Series(["a", "b", None], dtype="str")
Out[2]:
0 a
1 b
2 NaN
dtype: str
In [3]: pd.Series(["a", "b", None], dtype="string")
Out[3]:
0 a
1 b
2 <NA>
dtype: string
指定任一别名还将把非字符串数据转换为字符串
In [4]: s = pd.Series(["a", 2, np.nan], dtype="str")
In [5]: s
Out[5]:
0 a
1 2
2 NaN
dtype: str
In [6]: type(s[1])
Out[6]: str
或从现有 pandas 数据进行转换
In [7]: s1 = pd.Series([1, 2, pd.NA], dtype="Int64")
In [8]: s1
Out[8]:
0 1
1 2
2 <NA>
dtype: Int64
In [9]: s2 = s1.astype("string")
In [10]: s2
Out[10]:
0 1
1 2
2 <NA>
dtype: string
In [11]: type(s2[0])
Out[11]: str
但是,有四种不同的 StringDtype 变体可供使用。有关详细信息,请参阅下文的 四种 StringDtype 变体 部分。
字符串方法#
Series 和 Index 装备了一组字符串处理方法,可以轻松地对数组的每个元素进行操作。最重要的是,这些方法会自动排除缺失/NA 值。这些方法通过 str 属性访问,通常名称与等效的(标量)内置字符串方法匹配。
In [12]: s = pd.Series(
....: ["A", "B", "C", "Aaba", np.nan, "dog", "cat"],
....: dtype="str",
....: )
....:
In [13]: s.str.lower()
Out[13]:
0 a
1 b
2 c
3 aaba
4 NaN
5 dog
6 cat
dtype: str
In [14]: s.str.upper()
Out[14]:
0 A
1 B
2 C
3 AABA
4 NaN
5 DOG
6 CAT
dtype: str
In [15]: s.str.len()
Out[15]:
0 1.0
1 1.0
2 1.0
3 4.0
4 NaN
5 3.0
6 3.0
dtype: float64
In [16]: idx = pd.Index([" jack", "jill ", " jesse ", "frank"])
In [17]: idx.str.strip()
Out[17]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='str')
In [18]: idx.str.lstrip()
Out[18]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='str')
In [19]: idx.str.rstrip()
Out[19]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='str')
Index 上的字符串方法对于清理或转换 DataFrame 列特别有用。例如,您可能有一些列带有前导或尾随空格
In [20]: df = pd.DataFrame(
....: np.random.randn(3, 2),
....: columns=[" Column A ", " Column B "],
....: index=range(3),
....: )
....:
In [21]: df
Out[21]:
Column A Column B
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
由于 df.columns 是一个 Index 对象,我们可以使用 .str 访问器
In [22]: df.columns.str.strip()
Out[22]: Index(['Column A', 'Column B'], dtype='str')
In [23]: df.columns.str.lower()
Out[23]: Index([' column a ', ' column b '], dtype='str')
然后可以使用这些字符串方法按需清理列。在这里,我们正在删除前导和尾随空格,将所有名称转换为小写,并将任何剩余的空格替换为下划线。
In [24]: df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
In [25]: df
Out[25]:
column_a column_b
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
注意
如果您有一个 Series,其中许多元素是重复的(即,Series 中唯一元素的数量远小于 Series 的长度),那么最好先将原始 Series 转换为 category 类型,然后在其上使用 .str.<method> 或 .dt.<property>。性能差异来自于这样一个事实:对于 category 类型的 Series,字符串操作是在 .categories 上执行的,而不是在 Series 的每个元素上执行的。
请注意,具有字符串 .categories 的 category 类型 Series 与字符串类型的 Series 相比存在一些限制(例如,您无法将字符串相加:如果 s 是 category 类型的 Series,则 s + " " + s 将不起作用)。此外,在这样的 Series 上,无法使用作用于 list 类型元素的 .str 方法。
警告
Series 的类型被推断,并且是允许的类型之一(即字符串)。
总的来说,.str 访问器旨在仅用于字符串。除极少数例外,其他用法不受支持,并可能在以后禁用。
拆分和替换字符串#
像 split 这样的方法返回一个列表的 Series
In [26]: s2 = pd.Series(["a_b_c", "c_d_e", np.nan, "f_g_h"], dtype="str")
In [27]: s2.str.split("_")
Out[27]:
0 [a, b, c]
1 [c, d, e]
2 NaN
3 [f, g, h]
dtype: object
可以使用 get 或 [] 符号访问拆分列表中的元素。
In [28]: s2.str.split("_").str.get(1)
Out[28]:
0 b
1 d
2 NaN
3 g
dtype: object
In [29]: s2.str.split("_").str[1]
Out[29]:
0 b
1 d
2 NaN
3 g
dtype: object
使用 expand,可以轻松地将其扩展为返回 DataFrame。
In [30]: s2.str.split("_", expand=True)
Out[30]:
0 1 2
0 a b c
1 c d e
2 NaN NaN NaN
3 f g h
当原始 Series 具有 StringDtype 时,输出列也将全部为 StringDtype。
也可以限制拆分的数量
In [31]: s2.str.split("_", expand=True, n=1)
Out[31]:
0 1
0 a b_c
1 c d_e
2 NaN NaN
3 f g_h
rsplit 类似于 split,只是它以相反的方向工作,即从字符串的末尾到字符串的开头。
In [32]: s2.str.rsplit("_", expand=True, n=1)
Out[32]:
0 1
0 a_b c
1 c_d e
2 NaN NaN
3 f_g h
replace 可选使用 正则表达式。
In [33]: s3 = pd.Series(
....: ["A", "B", "C", "Aaba", "Baca", "", np.nan, "CABA", "dog", "cat"],
....: dtype="str",
....: )
....:
In [34]: s3
Out[34]:
0 A
1 B
2 C
3 Aaba
4 Baca
5
6 NaN
7 CABA
8 dog
9 cat
dtype: str
In [35]: s3.str.replace("^.a|dog", "XX-XX ", case=False, regex=True)
Out[35]:
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: str
版本 2.0 中已更改: 带有 regex=True 的单字符模式也将被视为正则表达式。
In [36]: s4 = pd.Series(["a.b", ".", "b", np.nan, ""], dtype="str")
In [37]: s4
Out[37]:
0 a.b
1 .
2 b
3 NaN
4
dtype: str
In [38]: s4.str.replace(".", "a", regex=True)
Out[38]:
0 aaa
1 a
2 a
3 NaN
4
dtype: str
如果您想进行字面字符串替换(等同于 str.replace()),您可以将可选的 regex 参数设置为 False,而不是转义每个字符。在这种情况下,pat 和 repl 都必须是字符串。
In [39]: dollars = pd.Series(["12", "-$10", "$10,000"], dtype="str")
# These lines are equivalent
In [40]: dollars.str.replace(r"-\$", "-", regex=True)
Out[40]:
0 12
1 -10
2 $10,000
dtype: str
In [41]: dollars.str.replace("-$", "-", regex=False)
Out[41]:
0 12
1 -10
2 $10,000
dtype: str
replace 方法还可以将一个可调用对象作为替换。它将使用 re.sub() 对每个 pat 调用。该可调用对象应该接受一个位置参数(一个正则表达式对象)并返回一个字符串。
# Reverse every lowercase alphabetic word
In [42]: pat = r"[a-z]+"
In [43]: def repl(m):
....: return m.group(0)[::-1]
....:
In [44]: pd.Series(["foo 123", "bar baz", np.nan], dtype="str").str.replace(
....: pat, repl, regex=True
....: )
....:
Out[44]:
0 oof 123
1 rab zab
2 NaN
dtype: str
# Using regex groups
In [45]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"
In [46]: def repl(m):
....: return m.group("two").swapcase()
....:
In [47]: pd.Series(["Foo Bar Baz", np.nan], dtype="str").str.replace(
....: pat, repl, regex=True
....: )
....:
Out[47]:
0 bAR
1 NaN
dtype: str
replace 方法还接受来自 re.compile() 的已编译正则表达式对象作为模式。所有标志都应包含在已编译的正则表达式对象中。
In [48]: import re
In [49]: regex_pat = re.compile(r"^.a|dog", flags=re.IGNORECASE)
In [50]: s3.str.replace(regex_pat, "XX-XX ", regex=True)
Out[50]:
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: str
当使用已编译的正则表达式对象调用 replace 时包含 flags 参数将引发 ValueError。
In [51]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
---------------------------------------------------------------------------
ValueError: case and flags cannot be set when pat is a compiled regex
removeprefix 和 removesuffix 的效果与 Python 3.9 中添加的 str.removeprefix 和 str.removesuffix 相同。
In [52]: s = pd.Series(["str_foo", "str_bar", "no_prefix"])
In [53]: s.str.removeprefix("str_")
Out[53]:
0 foo
1 bar
2 no_prefix
dtype: str
In [54]: s = pd.Series(["foo_str", "bar_str", "no_suffix"])
In [55]: s.str.removesuffix("_str")
Out[55]:
0 foo
1 bar
2 no_suffix
dtype: str
连接#
有几种方法可以连接一个 Series 或 Index(与自身或其他对象),这些方法都基于 cat() 或 Index.str.cat。
将单个 Series 连接成一个字符串#
可以连接 Series(或 Index)的内容
In [56]: s = pd.Series(["a", "b", "c", "d"], dtype="str")
In [57]: s.str.cat(sep=",")
Out[57]: 'a,b,c,d'
如果未指定分隔符的 sep 关键字参数,则默认为空字符串 sep=''。
In [58]: s.str.cat()
Out[58]: 'abcd'
默认情况下,忽略缺失值。使用 na_rep,可以为它们指定一个表示。
In [59]: t = pd.Series(["a", "b", np.nan, "d"], dtype="str")
In [60]: t.str.cat(sep=",")
Out[60]: 'a,b,d'
In [61]: t.str.cat(sep=",", na_rep="-")
Out[61]: 'a,b,-,d'
将 Series 和类列表对象连接成 Series#
cat() 的第一个参数可以是一个类列表对象,前提是它与调用 Series(或 Index)的长度匹配。
In [62]: s.str.cat(["A", "B", "C", "D"])
Out[62]:
0 aA
1 bB
2 cC
3 dD
dtype: str
任一侧的缺失值都将在结果中产生缺失值,除非指定了 na_rep。
In [63]: s.str.cat(t)
Out[63]:
0 aa
1 bb
2 NaN
3 dd
dtype: str
In [64]: s.str.cat(t, na_rep="-")
Out[64]:
0 aa
1 bb
2 c-
3 dd
dtype: str
将 Series 和类数组对象连接成 Series#
参数 others 也可以是二维的。在这种情况下,行数必须与调用 Series(或 Index)的长度匹配。
In [65]: d = pd.concat([t, s], axis=1)
In [66]: s
Out[66]:
0 a
1 b
2 c
3 d
dtype: str
In [67]: d
Out[67]:
0 1
0 a a
1 b b
2 NaN c
3 d d
In [68]: s.str.cat(d, na_rep="-")
Out[68]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: str
将 Series 和带索引的对象连接成 Series,并进行对齐#
对于与 Series 或 DataFrame 的连接,可以通过设置 join 关键字来在连接前对齐索引。
In [69]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="str")
In [70]: s
Out[70]:
0 a
1 b
2 c
3 d
dtype: str
In [71]: u
Out[71]:
1 b
3 d
0 a
2 c
dtype: str
In [72]: s.str.cat(u)
Out[72]:
0 aa
1 bb
2 cc
3 dd
dtype: str
In [73]: s.str.cat(u, join="left")
Out[73]:
0 aa
1 bb
2 cc
3 dd
dtype: str
join 的常用选项('left', 'outer', 'inner', 'right' 之一)可用。特别是,对齐也意味着不同长度不必再完全一致。
In [74]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="str")
In [75]: s
Out[75]:
0 a
1 b
2 c
3 d
dtype: str
In [76]: v
Out[76]:
-1 z
0 a
1 b
3 d
4 e
dtype: str
In [77]: s.str.cat(v, join="left", na_rep="-")
Out[77]:
0 aa
1 bb
2 c-
3 dd
dtype: str
In [78]: s.str.cat(v, join="outer", na_rep="-")
Out[78]:
-1 -z
0 aa
1 bb
2 c-
3 dd
4 -e
dtype: str
当 others 是 DataFrame 时,可以使用相同的对齐方式。
In [79]: f = d.loc[[3, 2, 1, 0], :]
In [80]: s
Out[80]:
0 a
1 b
2 c
3 d
dtype: str
In [81]: f
Out[81]:
0 1
3 d d
2 NaN c
1 b b
0 a a
In [82]: s.str.cat(f, join="left", na_rep="-")
Out[82]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: str
将 Series 和多个对象连接成 Series#
可以将多个类数组项(具体来说:Series、Index 和 np.ndarray 的一维变体)组合在一个类列表容器中(包括迭代器、dict 视图等)。
In [83]: s
Out[83]:
0 a
1 b
2 c
3 d
dtype: str
In [84]: u
Out[84]:
1 b
3 d
0 a
2 c
dtype: str
In [85]: s.str.cat([u, u.to_numpy()], join="left")
Out[85]:
0 aab
1 bbd
2 cca
3 ddc
dtype: str
传递的列表类中的所有无索引元素(例如 np.ndarray)必须与调用 Series(或 Index)的长度匹配,但 Series 和 Index 可以具有任意长度(只要通过 join=None 禁用了对齐)。
In [86]: v
Out[86]:
-1 z
0 a
1 b
3 d
4 e
dtype: str
In [87]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
Out[87]:
-1 -z--
0 aaab
1 bbbd
2 c-ca
3 dddc
4 -e--
dtype: str
如果在具有不同索引的 others 列表类上使用 join='right',则这些索引的并集将用作最终连接的基础。
In [88]: u.loc[[3]]
Out[88]:
3 d
dtype: str
In [89]: v.loc[[-1, 0]]
Out[89]:
-1 z
0 a
dtype: str
In [90]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
Out[90]:
3 dd-
-1 --z
0 a-a
dtype: str
使用 .str 进行索引#
您可以使用 [] 符号直接按位置索引。如果您索引超出字符串末尾,结果将是 NaN。
In [91]: s = pd.Series(
....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="str"
....: )
....:
In [92]: s.str[0]
Out[92]:
0 A
1 B
2 C
3 A
4 B
5 NaN
6 C
7 d
8 c
dtype: str
In [93]: s.str[1]
Out[93]:
0 NaN
1 NaN
2 NaN
3 a
4 a
5 NaN
6 A
7 o
8 a
dtype: str
提取子字符串#
提取每个主题中的第一个匹配项(extract)#
extract 方法接受一个至少有一个捕获组的 正则表达式。
提取具有多个组的正则表达式会返回一个 DataFrame,其中每组一列。
In [94]: pd.Series(
....: ["a1", "b2", "c3"],
....: dtype="str",
....: ).str.extract(r"([ab])(\d)", expand=False)
....:
Out[94]:
0 1
0 a 1
1 b 2
2 NaN NaN
不匹配的元素将返回一个用 NaN 填充的行。因此,可以“转换”一堆混乱的字符串为具有相同索引的、已清理或更有用的字符串的 Series 或 DataFrame,而无需使用 get() 来访问元组或 re.match 对象。结果的数据类型始终是 object,即使没有找到匹配项并且结果仅包含 NaN。
命名组,如
In [95]: pd.Series(["a1", "b2", "c3"], dtype="str").str.extract(
....: r"(?P<letter>[ab])(?P<digit>\d)", expand=False
....: )
....:
Out[95]:
letter digit
0 a 1
1 b 2
2 NaN NaN
和可选组,如
In [96]: pd.Series(
....: ["a1", "b2", "3"],
....: dtype="str",
....: ).str.extract(r"([ab])?(\d)", expand=False)
....:
Out[96]:
0 1
0 a 1
1 b 2
2 NaN 3
也可以使用。请注意,正则表达式中的任何捕获组名称都将用于列名;否则将使用捕获组编号。
提取具有一个组的正则表达式会在 expand=True 时返回一个单列的 DataFrame。
In [97]: pd.Series(["a1", "b2", "c3"], dtype="str").str.extract(r"[ab](\d)", expand=True)
Out[97]:
0
0 1
1 2
2 NaN
在 expand=False 时,它返回一个 Series。
In [98]: pd.Series(["a1", "b2", "c3"], dtype="str").str.extract(r"[ab](\d)", expand=False)
Out[98]:
0 1
1 2
2 NaN
dtype: str
使用具有正好一个捕获组的正则表达式调用 Index 时,如果 expand=True,则返回一个单列的 DataFrame。
In [99]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="str")
In [100]: s
Out[100]:
A11 a1
B22 b2
C33 c3
dtype: str
In [101]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=True)
Out[101]:
letter
0 A
1 B
2 C
在 expand=False 时,它返回一个 Index。
In [102]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False)
Out[102]: Index(['A', 'B', 'C'], dtype='str', name='letter')
使用具有多个捕获组的正则表达式调用 Index 时,如果 expand=True,则返回一个 DataFrame。
In [103]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True)
Out[103]:
letter 1
0 A 11
1 B 22
2 C 33
如果 expand=False,它将引发 ValueError。
In [104]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[104], line 1
----> 1 s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:142, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
137 msg = (
138 f"Cannot use .str.{func_name} with values of "
139 f"inferred dtype '{self._inferred_dtype}'."
140 )
141 raise TypeError(msg)
--> 142 return func(self, *args, **kwargs)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:3420, in StringMethods.extract(self, pat, flags, expand)
3417 raise ValueError("pattern contains no capture groups")
3419 if not expand and regex.groups > 1 and isinstance(self._data, ABCIndex):
-> 3420 raise ValueError("only one regex group is supported with Index")
3422 obj = self._data
3423 result_dtype = _result_dtype(obj)
ValueError: only one regex group is supported with Index
下表总结了 extract(expand=False) 的行为(第一列为输入主题,第一行为正则表达式的组数)。
1 组 |
>1 组 |
|
索引 |
索引 |
ValueError |
Series |
Series |
DataFrame |
提取每个主题中的所有匹配项(extractall)#
与 extract(只返回第一个匹配项)不同,
In [105]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"], dtype="str")
In [106]: s
Out[106]:
A a1a2
B b1
C c1
dtype: str
In [107]: two_groups = "(?P<letter>[a-z])(?P<digit>[0-9])"
In [108]: s.str.extract(two_groups, expand=True)
Out[108]:
letter digit
A a 1
B b 1
C c 1
extractall 方法返回所有匹配项。extractall 的结果始终是一个 DataFrame,其行上有一个 MultiIndex。 MultiIndex 的最后一个级别被命名为 match,表示在主题中的顺序。
In [109]: s.str.extractall(two_groups)
Out[109]:
letter digit
match
A 0 a 1
1 a 2
B 0 b 1
C 0 c 1
当 Series 中的每个主题字符串只有一个匹配项时,
In [110]: s = pd.Series(["a3", "b3", "c2"], dtype="str")
In [111]: s
Out[111]:
0 a3
1 b3
2 c2
dtype: str
那么 extractall(pat).xs(0, level='match') 的结果与 extract(pat) 相同。
In [112]: extract_result = s.str.extract(two_groups, expand=True)
In [113]: extract_result
Out[113]:
letter digit
0 a 3
1 b 3
2 c 2
In [114]: extractall_result = s.str.extractall(two_groups)
In [115]: extractall_result
Out[115]:
letter digit
match
0 0 a 3
1 0 b 3
2 0 c 2
In [116]: extractall_result.xs(0, level="match")
Out[116]:
letter digit
0 a 3
1 b 3
2 c 2
Index 也支持 .str.extractall。它返回一个 DataFrame,其结果与具有默认索引(从 0 开始)的 Series.str.extractall 相同。
In [117]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[117]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
In [118]: pd.Series(["a1a2", "b1", "c1"], dtype="str").str.extractall(two_groups)
Out[118]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
测试与模式匹配或包含字符串#
您可以检查元素是否包含模式
In [119]: pattern = r"[0-9][a-z]"
In [120]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="str",
.....: ).str.contains(pattern)
.....:
Out[120]:
0 False
1 False
2 True
3 True
4 True
5 True
dtype: bool
或者元素是否匹配模式
In [121]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="str",
.....: ).str.match(pattern)
.....:
Out[121]:
0 False
1 False
2 True
3 True
4 False
5 True
dtype: bool
In [122]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="str",
.....: ).str.fullmatch(pattern)
.....:
Out[122]:
0 False
1 False
2 True
3 True
4 False
5 False
dtype: bool
注意
match、fullmatch 和 contains 之间的区别在于严格性:fullmatch 测试整个字符串是否与正则表达式匹配;match 测试正则表达式是否有匹配项,该匹配项从字符串的第一个字符开始;而 contains 测试字符串中的任何位置是否有匹配项。
Python 的 re 包中这三种匹配模式的对应函数分别是 re.fullmatch、re.match 和 re.search。
像 match、fullmatch、contains、startswith 和 endswith 这样的方法接受一个额外的 na 参数,因此缺失值可以被视为 True 或 False。
In [123]: s4 = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="str"
.....: )
.....:
In [124]: s4.str.contains("A", na=False)
Out[124]:
0 True
1 False
2 False
3 True
4 False
5 False
6 True
7 False
8 False
dtype: bool
创建指示变量#
您可以从字符串列中提取虚拟变量。例如,如果它们由 '|' 分隔
In [125]: s = pd.Series(["a", "a|b", np.nan, "a|c"], dtype="str")
In [126]: s.str.get_dummies(sep="|")
Out[126]:
a b c
0 1 0 0
1 1 1 0
2 0 0 0
3 1 0 1
字符串 Index 也支持 get_dummies,它返回一个 MultiIndex。
In [127]: idx = pd.Index(["a", "a|b", np.nan, "a|c"])
In [128]: idx.str.get_dummies(sep="|")
Out[128]:
MultiIndex([(1, 0, 0),
(1, 1, 0),
(0, 0, 0),
(1, 0, 1)],
names=['a', 'b', 'c'])
另请参阅 get_dummies()。
行为差异#
行为上的差异主要归因于 NA 值的种类。
StringDtype 搭配 np.nan NA 值#
与
dtype="object"类似,返回整数输出的 字符串访问器方法 将返回一个 NumPy 数组,该数组根据 NA 值的存在情况,可以是 int 或 float 数据类型。返回布尔值输出的方法将返回一个布尔值数据类型的 NumPy 数组,当遇到 NA 值时,其值为False。In [129]: s = pd.Series(["a", None, "b"], dtype="str") In [130]: s Out[130]: 0 a 1 NaN 2 b dtype: str In [131]: s.str.count("a") Out[131]: 0 1.0 1 NaN 2 0.0 dtype: float64 In [132]: s.dropna().str.count("a") Out[132]: 0 1 2 0 dtype: int64
当存在 NA 值时,输出数据类型为 float64。但是布尔值输出的结果对于 NA 值是
False。In [133]: s.str.isdigit() Out[133]: 0 False 1 False 2 False dtype: bool In [134]: s.str.match("a") Out[134]: 0 True 1 False 2 False dtype: bool
某些字符串方法,例如
Series.str.decode(),不可用,因为底层数组只能包含字符串,而不能包含字节。比较运算将返回一个布尔值数据类型的 NumPy 数组。缺失值将始终比较为不等,就像
np.nan一样。
StringDtype 搭配 pd.NA NA 值#
返回整数输出的 字符串访问器方法 将始终返回一个可为空的整数数据类型,而不是 int 或 float 数据类型(取决于 NA 值的存在情况)。返回布尔值输出的方法将返回一个可为空的布尔值数据类型。
In [135]: s = pd.Series(["a", None, "b"], dtype="string") In [136]: s Out[136]: 0 a 1 <NA> 2 b dtype: string In [137]: s.str.count("a") Out[137]: 0 1 1 <NA> 2 0 dtype: Int64 In [138]: s.dropna().str.count("a") Out[138]: 0 1 2 0 dtype: Int64
这两个输出都是
Int64数据类型。返回布尔值的方法也是如此。In [139]: s.str.isdigit() Out[139]: 0 False 1 <NA> 2 False dtype: boolean In [140]: s.str.match("a") Out[140]: 0 True 1 <NA> 2 False dtype: boolean
由于底层数组只能包含字符串,而不能包含字节,因此像
Series.str.decode()这样的某些字符串方法不可用。比较运算将返回一个具有
BooleanDtype的对象,而不是一个bool数据类型的对象。缺失值将在比较运算中传播,而不是像numpy.nan那样总是比较为不等。
重要提示
本文档其余部分后续内容同样适用于 'str'、'string' 和 object 数据类型。
四种 StringDtype 变体#
用户可以使用四种 StringDtype 变体,这些变体由 StringDtype 的 storage 和 na_value 参数控制。在运行时,可以通过 StringDtype.storage 和 StringDtype.na_value 属性检查这些。
Python 存储搭配 np.nan 值#
注意
这与 dtype='str' 在未安装 PyArrow 时相同。
实现使用 NumPy 对象数组,它直接存储 Python 字符串对象,因此这里的存储被称为 'python'。此数组中的 NA 值被表示为 np.nan 并以此行为。
In [141]: pd.Series(
.....: ["a", "b", None, np.nan, pd.NA],
.....: dtype=pd.StringDtype(storage="python", na_value=np.nan)
.....: )
.....:
Out[141]:
0 a
1 b
2 NaN
3 NaN
4 NaN
dtype: str
请注意,最后三个值都被 pandas 推断为 NA 值,因此存储为 np.nan。
PyArrow 存储搭配 np.nan 值#
注意
这与 dtype='str' 在安装了 PyArrow 时相同。
实现使用 PyArrow 数组,但是此数组中的 NA 值被表示为 np.nan 并以此行为。
In [142]: pd.Series(
.....: ["a", "b", None, np.nan, pd.NA],
.....: dtype=pd.StringDtype(storage="pyarrow", na_value=np.nan)
.....: )
.....:
Out[142]:
0 a
1 b
2 NaN
3 NaN
4 NaN
dtype: str
请注意,最后三个值都被 pandas 推断为 NA 值,因此存储为 np.nan。
Python 存储搭配 pd.NA 值#
注意
这与 dtype='string' 在未安装 PyArrow 时相同。
实现使用 NumPy 对象数组,它直接存储 Python 字符串对象,因此这里的存储被称为 'python'。此数组中的 NA 值被表示为 np.nan 并以此行为。
In [143]: pd.Series(
.....: ["a", "b", None, np.nan, pd.NA],
.....: dtype=pd.StringDtype(storage="python", na_value=pd.NA)
.....: )
.....:
Out[143]:
0 a
1 b
2 <NA>
3 <NA>
4 <NA>
dtype: string
请注意,最后三个值都被 pandas 推断为 NA 值,因此存储为 pd.NA。
PyArrow 存储搭配 pd.NA 值#
注意
这与 dtype='string' 在安装了 PyArrow 时相同。
实现使用 PyArrow 数组。此数组中的 NA 值被表示为 pd.NA 并以此行为。
In [144]: pd.Series(
.....: ["a", "b", None, np.nan, pd.NA],
.....: dtype=pd.StringDtype(storage="python", na_value=pd.NA)
.....: )
.....:
Out[144]:
0 a
1 b
2 <NA>
3 <NA>
4 <NA>
dtype: string
请注意,最后三个值都被 pandas 推断为 NA 值,因此存储为 pd.NA。
方法摘要#
方法 |
描述 |
|---|---|
连接字符串 |
|
按分隔符拆分字符串 |
|
按分隔符拆分字符串,从字符串末尾开始 |
|
索引到每个元素(检索第 i 个元素) |
|
用指定的分隔符连接 Series 中每个元素的字符串 |
|
按分隔符拆分字符串,返回虚拟变量 DataFrame |
|
如果每个字符串包含模式/正则表达式,则返回布尔数组 |
|
用另一个字符串或给定出现的函数返回的字符串替换模式/正则表达式/字符串的出现 |
|
从字符串中删除前缀,即仅当字符串以该前缀开头时才删除。 |
|
从字符串中删除后缀,即仅当字符串以该后缀结尾时才删除。 |
|
重复值 ( |
|
在字符串的两侧添加空格 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
将长字符串拆分成长度小于给定宽度的行 |
|
切片 Series 中的每个字符串 |
|
用提供的值替换每个字符串中的切片 |
|
计算模式的出现次数 |
|
对每个元素等同于 |
|
对每个元素等同于 |
|
计算每个字符串中模式/正则表达式的所有匹配项列表 |
|
对每个元素调用 |
|
对每个元素调用 |
|
对每个元素调用 |
|
计算字符串长度 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
返回 Unicode 规范形式。等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |
|
等同于 |