处理文本数据#

文本数据类型#

在 pandas 中存储文本数据有两种方法

  1. object -dtype NumPy 数组。

  2. StringDtype 扩展类型。

我们建议使用 StringDtype 来存储文本数据。

在 pandas 1.0 之前,object 数据类型是唯一的选择。这有很多弊端。

  1. 你可能会不小心在一个 object 数据类型数组中存储字符串和非字符串的混合。最好有一个专门的数据类型。

  2. object 数据类型会破坏特定数据类型的操作,例如 DataFrame.select_dtypes()。没有明确的方法来选择仅文本,同时排除非文本但仍然是对象数据类型的列。

  3. 在阅读代码时,object 数据类型数组的内容不如 'string' 清晰。

目前,object 数据类型字符串数组和 arrays.StringArray 的性能大致相同。我们预计未来的增强将显著提高 StringArray 的性能并降低内存开销。

警告

StringArray 目前被认为是实验性的。实现和 API 的部分可能会在没有警告的情况下更改。

为了向后兼容,object 数据类型仍然是我们推断字符串列表的默认类型。

In [1]: pd.Series(["a", "b", "c"])
Out[1]: 
0    a
1    b
2    c
dtype: object

要显式请求 string 数据类型,请指定 dtype

In [2]: pd.Series(["a", "b", "c"], dtype="string")
Out[2]: 
0    a
1    b
2    c
dtype: string

In [3]: pd.Series(["a", "b", "c"], dtype=pd.StringDtype())
Out[3]: 
0    a
1    b
2    c
dtype: string

或者在创建 SeriesDataFrame 后使用 astype

In [4]: s = pd.Series(["a", "b", "c"])

In [5]: s
Out[5]: 
0    a
1    b
2    c
dtype: object

In [6]: s.astype("string")
Out[6]: 
0    a
1    b
2    c
dtype: string

您也可以使用 StringDtype/"string" 作为非字符串数据的 dtype,它将被转换为 string dtype

In [7]: s = pd.Series(["a", 2, np.nan], dtype="string")

In [8]: s
Out[8]: 
0       a
1       2
2    <NA>
dtype: string

In [9]: type(s[1])
Out[9]: str

或从现有的 pandas 数据转换

In [10]: s1 = pd.Series([1, 2, np.nan], dtype="Int64")

In [11]: s1
Out[11]: 
0       1
1       2
2    <NA>
dtype: Int64

In [12]: s2 = s1.astype("string")

In [13]: s2
Out[13]: 
0       1
1       2
2    <NA>
dtype: string

In [14]: type(s2[0])
Out[14]: str

行为差异#

这些是 StringDtype 对象的行为与 object dtype 不同的地方

  1. 对于 StringDtype字符串访问器方法 返回数值输出将始终返回可空整数 dtype,而不是根据 NA 值的存在返回 int 或 float dtype。返回布尔输出的方法将返回可空布尔 dtype。

    In [15]: s = pd.Series(["a", None, "b"], dtype="string")
    
    In [16]: s
    Out[16]: 
    0       a
    1    <NA>
    2       b
    dtype: string
    
    In [17]: s.str.count("a")
    Out[17]: 
    0       1
    1    <NA>
    2       0
    dtype: Int64
    
    In [18]: s.dropna().str.count("a")
    Out[18]: 
    0    1
    2    0
    dtype: Int64
    

    两种输出都是 Int64 dtype。将其与 object-dtype 进行比较

    In [19]: s2 = pd.Series(["a", None, "b"], dtype="object")
    
    In [20]: s2.str.count("a")
    Out[20]: 
    0    1.0
    1    NaN
    2    0.0
    dtype: float64
    
    In [21]: s2.dropna().str.count("a")
    Out[21]: 
    0    1
    2    0
    dtype: int64
    

    当存在 NA 值时,输出 dtype 为 float64。返回布尔值的函数也类似。

    In [22]: s.str.isdigit()
    Out[22]: 
    0    False
    1     <NA>
    2    False
    dtype: boolean
    
    In [23]: s.str.match("a")
    Out[23]: 
    0     True
    1     <NA>
    2    False
    dtype: boolean
    
  1. 一些字符串方法,如 Series.str.decode()StringArray 上不可用,因为 StringArray 只保存字符串,不保存字节。

  2. 在比较操作中,arrays.StringArraySeriesStringArray 支持将返回一个具有 BooleanDtype 的对象,而不是一个 bool dtype 对象。 StringArray 中的缺失值将在比较操作中传播,而不是像 numpy.nan 一样始终比较不相等。

本文档其余部分中提到的所有内容都同样适用于 stringobject 数据类型。

字符串方法#

Series 和 Index 配备了一组字符串处理方法,可以轻松地对数组的每个元素进行操作。最重要的是,这些方法会自动排除缺失/NA 值。这些方法可以通过 str 属性访问,并且通常与等效的(标量)内置字符串方法具有相同的名称。

In [24]: s = pd.Series(
   ....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   ....: )
   ....: 

In [25]: s.str.lower()
Out[25]: 
0       a
1       b
2       c
3    aaba
4    baca
5    <NA>
6    caba
7     dog
8     cat
dtype: string

In [26]: s.str.upper()
Out[26]: 
0       A
1       B
2       C
3    AABA
4    BACA
5    <NA>
6    CABA
7     DOG
8     CAT
dtype: string

In [27]: s.str.len()
Out[27]: 
0       1
1       1
2       1
3       4
4       4
5    <NA>
6       4
7       3
8       3
dtype: Int64
In [28]: idx = pd.Index([" jack", "jill ", " jesse ", "frank"])

In [29]: idx.str.strip()
Out[29]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')

In [30]: idx.str.lstrip()
Out[30]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')

In [31]: idx.str.rstrip()
Out[31]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')

Index 上的字符串方法对于清理或转换 DataFrame 列特别有用。例如,您可能拥有包含前导或尾随空格的列。

In [32]: df = pd.DataFrame(
   ....:     np.random.randn(3, 2), columns=[" Column A ", " Column B "], index=range(3)
   ....: )
   ....: 

In [33]: df
Out[33]: 
   Column A   Column B 
0   0.469112  -0.282863
1  -1.509059  -1.135632
2   1.212112  -0.173215

由于 df.columns 是一个 Index 对象,我们可以使用 .str 访问器。

In [34]: df.columns.str.strip()
Out[34]: Index(['Column A', 'Column B'], dtype='object')

In [35]: df.columns.str.lower()
Out[35]: Index([' column a ', ' column b '], dtype='object')

然后可以使用这些字符串方法根据需要清理列。这里我们删除了前导和尾随空格,将所有名称转换为小写,并将任何剩余的空格替换为下划线。

In [36]: df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")

In [37]: df
Out[37]: 
   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 的每个元素上执行的。

请注意,类型为 category 且字符串为 .categoriesSeries 与类型为字符串的 Series 相比有一些限制(例如,您不能将字符串相互添加:s + " " + s 如果 s 是类型为 categorySeries 则不起作用)。此外,在这样的 Series 上不可使用对类型为 list 的元素进行操作的 .str 方法。

警告

Series 的类型是推断出来的,允许的类型(即字符串)。

一般来说,.str 访问器仅用于处理字符串。除极少数例外情况外,不支持其他用途,并且可能在以后的某个时间点被禁用。

拆分和替换字符串#

split 这样的方法返回一个列表类型的 Series

In [38]: s2 = pd.Series(["a_b_c", "c_d_e", np.nan, "f_g_h"], dtype="string")

In [39]: s2.str.split("_")
Out[39]: 
0    [a, b, c]
1    [c, d, e]
2         <NA>
3    [f, g, h]
dtype: object

可以使用 get[] 符号访问拆分列表中的元素

In [40]: s2.str.split("_").str.get(1)
Out[40]: 
0       b
1       d
2    <NA>
3       g
dtype: object

In [41]: s2.str.split("_").str[1]
Out[41]: 
0       b
1       d
2    <NA>
3       g
dtype: object

使用 expand 将其扩展为返回 DataFrame 很容易。

In [42]: s2.str.split("_", expand=True)
Out[42]: 
      0     1     2
0     a     b     c
1     c     d     e
2  <NA>  <NA>  <NA>
3     f     g     h

当原始 Series 具有 StringDtype 时,输出列也将全部为 StringDtype

也可以限制拆分的次数。

In [43]: s2.str.split("_", expand=True, n=1)
Out[43]: 
      0     1
0     a   b_c
1     c   d_e
2  <NA>  <NA>
3     f   g_h

rsplitsplit 类似,但它以相反的方向工作,即从字符串的末尾到字符串的开头。

In [44]: s2.str.rsplit("_", expand=True, n=1)
Out[44]: 
      0     1
0   a_b     c
1   c_d     e
2  <NA>  <NA>
3   f_g     h

replace 可选地使用 正则表达式

In [45]: s3 = pd.Series(
   ....:     ["A", "B", "C", "Aaba", "Baca", "", np.nan, "CABA", "dog", "cat"],
   ....:     dtype="string",
   ....: )
   ....: 

In [46]: s3
Out[46]: 
0       A
1       B
2       C
3    Aaba
4    Baca
5        
6    <NA>
7    CABA
8     dog
9     cat
dtype: string

In [47]: s3.str.replace("^.a|dog", "XX-XX ", case=False, regex=True)
Out[47]: 
0           A
1           B
2           C
3    XX-XX ba
4    XX-XX ca
5            
6        <NA>
7    XX-XX BA
8      XX-XX 
9     XX-XX t
dtype: string

在版本 2.0 中更改。

带有 regex=True 的单个字符模式也将被视为正则表达式。

In [48]: s4 = pd.Series(["a.b", ".", "b", np.nan, ""], dtype="string")

In [49]: s4
Out[49]: 
0     a.b
1       .
2       b
3    <NA>
4        
dtype: string

In [50]: s4.str.replace(".", "a", regex=True)
Out[50]: 
0     aaa
1       a
2       a
3    <NA>
4        
dtype: string

如果您想要对字符串进行字面替换(等效于 str.replace()),您可以将可选的 regex 参数设置为 False,而不是对每个字符进行转义。在这种情况下,patrepl 都必须是字符串。

In [51]: dollars = pd.Series(["12", "-$10", "$10,000"], dtype="string")

# These lines are equivalent
In [52]: dollars.str.replace(r"-\$", "-", regex=True)
Out[52]: 
0         12
1        -10
2    $10,000
dtype: string

In [53]: dollars.str.replace("-$", "-", regex=False)
Out[53]: 
0         12
1        -10
2    $10,000
dtype: string

replace 方法还可以接受可调用对象作为替换。它使用 re.sub() 对每个 pat 进行调用。可调用对象应期望一个位置参数(一个正则表达式对象)并返回一个字符串。

# Reverse every lowercase alphabetic word
In [54]: pat = r"[a-z]+"

In [55]: def repl(m):
   ....:     return m.group(0)[::-1]
   ....: 

In [56]: pd.Series(["foo 123", "bar baz", np.nan], dtype="string").str.replace(
   ....:     pat, repl, regex=True
   ....: )
   ....: 
Out[56]: 
0    oof 123
1    rab zab
2       <NA>
dtype: string

# Using regex groups
In [57]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"

In [58]: def repl(m):
   ....:     return m.group("two").swapcase()
   ....: 

In [59]: pd.Series(["Foo Bar Baz", np.nan], dtype="string").str.replace(
   ....:     pat, repl, regex=True
   ....: )
   ....: 
Out[59]: 
0     bAR
1    <NA>
dtype: string

replace 方法还接受来自 re.compile() 的已编译正则表达式对象作为模式。所有标志都应包含在已编译的正则表达式对象中。

In [60]: import re

In [61]: regex_pat = re.compile(r"^.a|dog", flags=re.IGNORECASE)

In [62]: s3.str.replace(regex_pat, "XX-XX ", regex=True)
Out[62]: 
0           A
1           B
2           C
3    XX-XX ba
4    XX-XX ca
5            
6        <NA>
7    XX-XX BA
8      XX-XX 
9     XX-XX t
dtype: string

在使用已编译的正则表达式对象调用 replace 时,如果包含 flags 参数,将会引发 ValueError 错误。

In [63]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
---------------------------------------------------------------------------
ValueError: case and flags cannot be set when pat is a compiled regex

removeprefixremovesuffix 的效果与 Python 3.9 中添加的 str.removeprefixstr.removesuffix 相同 <https://docs.pythonlang.cn/3/library/stdtypes.html#str.removeprefix>`__

版本 1.4.0 中的新增功能。

In [64]: s = pd.Series(["str_foo", "str_bar", "no_prefix"])

In [65]: s.str.removeprefix("str_")
Out[65]: 
0          foo
1          bar
2    no_prefix
dtype: object

In [66]: s = pd.Series(["foo_str", "bar_str", "no_suffix"])

In [67]: s.str.removesuffix("_str")
Out[67]: 
0          foo
1          bar
2    no_suffix
dtype: object

连接#

有几种方法可以连接 SeriesIndex,无论是与自身还是与其他对象连接,所有方法都基于 cat(),或者 Index.str.cat

将单个 Series 连接成字符串#

可以连接 Series(或 Index)的内容

In [68]: s = pd.Series(["a", "b", "c", "d"], dtype="string")

In [69]: s.str.cat(sep=",")
Out[69]: 'a,b,c,d'

如果未指定,分隔符的关键字 sep 默认为空字符串,sep=''

In [70]: s.str.cat()
Out[70]: 'abcd'

默认情况下,缺失值会被忽略。使用 na_rep,可以为它们指定一个表示形式

In [71]: t = pd.Series(["a", "b", np.nan, "d"], dtype="string")

In [72]: t.str.cat(sep=",")
Out[72]: 'a,b,d'

In [73]: t.str.cat(sep=",", na_rep="-")
Out[73]: 'a,b,-,d'

将 Series 和类似列表的对象连接成 Series#

cat() 的第一个参数可以是类似列表的对象,前提是它与调用 Series(或 Index)的长度匹配。

In [74]: s.str.cat(["A", "B", "C", "D"])
Out[74]: 
0    aA
1    bB
2    cC
3    dD
dtype: string

除非指定了 na_rep,否则任一方的缺失值都会导致结果中出现缺失值。

In [75]: s.str.cat(t)
Out[75]: 
0      aa
1      bb
2    <NA>
3      dd
dtype: string

In [76]: s.str.cat(t, na_rep="-")
Out[76]: 
0    aa
1    bb
2    c-
3    dd
dtype: string

将 Series 和类似数组的对象连接成 Series#

参数 others 也可以是二维的。在这种情况下,行数必须与调用 Series(或 Index)的长度匹配。

In [77]: d = pd.concat([t, s], axis=1)

In [78]: s
Out[78]: 
0    a
1    b
2    c
3    d
dtype: string

In [79]: d
Out[79]: 
      0  1
0     a  a
1     b  b
2  <NA>  c
3     d  d

In [80]: s.str.cat(d, na_rep="-")
Out[80]: 
0    aaa
1    bbb
2    c-c
3    ddd
dtype: string

将 Series 和索引对象连接成 Series,并进行对齐#

对于与 SeriesDataFrame 的连接,可以通过设置 join 关键字在连接之前对索引进行对齐。

In [81]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="string")

In [82]: s
Out[82]: 
0    a
1    b
2    c
3    d
dtype: string

In [83]: u
Out[83]: 
1    b
3    d
0    a
2    c
dtype: string

In [84]: s.str.cat(u)
Out[84]: 
0    aa
1    bb
2    cc
3    dd
dtype: string

In [85]: s.str.cat(u, join="left")
Out[85]: 
0    aa
1    bb
2    cc
3    dd
dtype: string

对于 join,可以使用常见的选项(其中之一为 'left', 'outer', 'inner', 'right')。特别是,对齐也意味着不同的长度不再需要一致。

In [86]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="string")

In [87]: s
Out[87]: 
0    a
1    b
2    c
3    d
dtype: string

In [88]: v
Out[88]: 
-1    z
 0    a
 1    b
 3    d
 4    e
dtype: string

In [89]: s.str.cat(v, join="left", na_rep="-")
Out[89]: 
0    aa
1    bb
2    c-
3    dd
dtype: string

In [90]: s.str.cat(v, join="outer", na_rep="-")
Out[90]: 
-1    -z
 0    aa
 1    bb
 2    c-
 3    dd
 4    -e
dtype: string

othersDataFrame 时,可以使用相同的对齐方式。

In [91]: f = d.loc[[3, 2, 1, 0], :]

In [92]: s
Out[92]: 
0    a
1    b
2    c
3    d
dtype: string

In [93]: f
Out[93]: 
      0  1
3     d  d
2  <NA>  c
1     b  b
0     a  a

In [94]: s.str.cat(f, join="left", na_rep="-")
Out[94]: 
0    aaa
1    bbb
2    c-c
3    ddd
dtype: string

将 Series 和多个对象连接成 Series#

可以将多个类似数组的项目(具体来说:SeriesIndexnp.ndarray 的一维变体)组合到类似列表的容器中(包括迭代器、dict 视图等)。

In [95]: s
Out[95]: 
0    a
1    b
2    c
3    d
dtype: string

In [96]: u
Out[96]: 
1    b
3    d
0    a
2    c
dtype: string

In [97]: s.str.cat([u, u.to_numpy()], join="left")
Out[97]: 
0    aab
1    bbd
2    cca
3    ddc
dtype: string

传递的类似列表中所有没有索引的元素(例如 np.ndarray)的长度必须与调用 Series(或 Index)匹配,但 SeriesIndex 可以具有任意长度(只要没有使用 join=None 禁用对齐)。

In [98]: v
Out[98]: 
-1    z
 0    a
 1    b
 3    d
 4    e
dtype: string

In [99]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
Out[99]: 
-1    -z--
0     aaab
1     bbbd
2     c-ca
3     dddc
4     -e--
dtype: string

如果对包含不同索引的 others 的类似列表使用 join='right',则将使用这些索引的并集作为最终连接的基础。

In [100]: u.loc[[3]]
Out[100]: 
3    d
dtype: string

In [101]: v.loc[[-1, 0]]
Out[101]: 
-1    z
 0    a
dtype: string

In [102]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
Out[102]: 
 3    dd-
-1    --z
 0    a-a
dtype: string

使用 .str 进行索引#

可以使用 [] 符号直接按位置索引。如果索引超出字符串的末尾,结果将为 NaN

In [103]: s = pd.Series(
   .....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   .....: )
   .....: 

In [104]: s.str[0]
Out[104]: 
0       A
1       B
2       C
3       A
4       B
5    <NA>
6       C
7       d
8       c
dtype: string

In [105]: s.str[1]
Out[105]: 
0    <NA>
1    <NA>
2    <NA>
3       a
4       a
5    <NA>
6       A
7       o
8       a
dtype: string

提取子字符串#

提取每个主题中的第一个匹配项(提取)#

extract 方法接受至少包含一个捕获组的 正则表达式

提取包含多个分组的正则表达式将返回一个 DataFrame,每个分组对应一列。

In [106]: pd.Series(
   .....:     ["a1", "b2", "c3"],
   .....:     dtype="string",
   .....: ).str.extract(r"([ab])(\d)", expand=False)
   .....: 
Out[106]: 
      0     1
0     a     1
1     b     2
2  <NA>  <NA>

不匹配的元素将返回一行,其中填充为 NaN。因此,可以将杂乱无章的字符串 Series “转换为”具有相同索引的 Series 或 DataFrame,其中包含清理后的或更有用的字符串,而无需使用 get() 访问元组或 re.match 对象。结果的 dtype 始终为 object,即使没有找到匹配项并且结果仅包含 NaN

也可以使用命名分组,例如

In [107]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(
   .....:     r"(?P<letter>[ab])(?P<digit>\d)", expand=False
   .....: )
   .....: 
Out[107]: 
  letter digit
0      a     1
1      b     2
2   <NA>  <NA>

以及可选分组,例如

In [108]: pd.Series(
   .....:     ["a1", "b2", "3"],
   .....:     dtype="string",
   .....: ).str.extract(r"([ab])?(\d)", expand=False)
   .....: 
Out[108]: 
      0  1
0     a  1
1     b  2
2  <NA>  3

请注意,正则表达式中的任何捕获组名称都将用作列名;否则将使用捕获组编号。

提取包含一个分组的正则表达式将返回一个 DataFrame,如果 expand=True

In [109]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"[ab](\d)", expand=True)
Out[109]: 
      0
0     1
1     2
2  <NA>

如果 expand=False,则返回一个 Series。

In [110]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"[ab](\d)", expand=False)
Out[110]: 
0       1
1       2
2    <NA>
dtype: string

Index 上调用具有一个捕获组的正则表达式将返回一个 DataFrame,如果 expand=True

In [111]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="string")

In [112]: s
Out[112]: 
A11    a1
B22    b2
C33    c3
dtype: string

In [113]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=True)
Out[113]: 
  letter
0      A
1      B
2      C

如果 expand=False,则返回一个 Index

In [114]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False)
Out[114]: Index(['A', 'B', 'C'], dtype='object', name='letter')

Index 上调用具有多个捕获组的正则表达式将返回一个 DataFrame,如果 expand=True

In [115]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True)
Out[115]: 
  letter   1
0      A  11
1      B  22
2      C  33

如果 expand=False,则会引发 ValueError

In [116]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[116], 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:137, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
    132     msg = (
    133         f"Cannot use .str.{func_name} with values of "
    134         f"inferred dtype '{self._inferred_dtype}'."
    135     )
    136     raise TypeError(msg)
--> 137 return func(self, *args, **kwargs)

File ~/work/pandas/pandas/pandas/core/strings/accessor.py:2743, in StringMethods.extract(self, pat, flags, expand)
   2740     raise ValueError("pattern contains no capture groups")
   2742 if not expand and regex.groups > 1 and isinstance(self._data, ABCIndex):
-> 2743     raise ValueError("only one regex group is supported with Index")
   2745 obj = self._data
   2746 result_dtype = _result_dtype(obj)

ValueError: only one regex group is supported with Index

下表总结了 extract(expand=False) 的行为(第一列中的输入主题,第一行中的正则表达式中的分组数)

1 个分组

>1 个分组

Index

Index

ValueError

Series

Series

DataFrame

提取每个主题中的所有匹配项(extractall)#

extract(仅返回第一个匹配项)不同,

In [117]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"], dtype="string")

In [118]: s
Out[118]: 
A    a1a2
B      b1
C      c1
dtype: string

In [119]: two_groups = "(?P<letter>[a-z])(?P<digit>[0-9])"

In [120]: s.str.extract(two_groups, expand=True)
Out[120]: 
  letter digit
A      a     1
B      b     1
C      c     1

extractall 方法返回所有匹配项。 extractall 的结果始终是 DataFrame,其行具有 MultiIndexMultiIndex 的最后一级名为 match,表示主题中的顺序。

In [121]: s.str.extractall(two_groups)
Out[121]: 
        letter digit
  match             
A 0          a     1
  1          a     2
B 0          b     1
C 0          c     1

当 Series 中的每个主题字符串恰好有一个匹配项时,

In [122]: s = pd.Series(["a3", "b3", "c2"], dtype="string")

In [123]: s
Out[123]: 
0    a3
1    b3
2    c2
dtype: string

extractall(pat).xs(0, level='match') 给出的结果与 extract(pat) 相同。

In [124]: extract_result = s.str.extract(two_groups, expand=True)

In [125]: extract_result
Out[125]: 
  letter digit
0      a     3
1      b     3
2      c     2

In [126]: extractall_result = s.str.extractall(two_groups)

In [127]: extractall_result
Out[127]: 
        letter digit
  match             
0 0          a     3
1 0          b     3
2 0          c     2

In [128]: extractall_result.xs(0, level="match")
Out[128]: 
  letter digit
0      a     3
1      b     3
2      c     2

Index 也支持 .str.extractall。它返回一个 DataFrame,其结果与具有默认索引(从 0 开始)的 Series.str.extractall 相同。

In [129]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[129]: 
        letter digit
  match             
0 0          a     1
  1          a     2
1 0          b     1
2 0          c     1

In [130]: pd.Series(["a1a2", "b1", "c1"], dtype="string").str.extractall(two_groups)
Out[130]: 
        letter digit
  match             
0 0          a     1
  1          a     2
1 0          b     1
2 0          c     1

测试匹配或包含模式的字符串#

您可以检查元素是否包含模式

In [131]: pattern = r"[0-9][a-z]"

In [132]: pd.Series(
   .....:     ["1", "2", "3a", "3b", "03c", "4dx"],
   .....:     dtype="string",
   .....: ).str.contains(pattern)
   .....: 
Out[132]: 
0    False
1    False
2     True
3     True
4     True
5     True
dtype: boolean

或者元素是否匹配模式

In [133]: pd.Series(
   .....:     ["1", "2", "3a", "3b", "03c", "4dx"],
   .....:     dtype="string",
   .....: ).str.match(pattern)
   .....: 
Out[133]: 
0    False
1    False
2     True
3     True
4    False
5     True
dtype: boolean
In [134]: pd.Series(
   .....:     ["1", "2", "3a", "3b", "03c", "4dx"],
   .....:     dtype="string",
   .....: ).str.fullmatch(pattern)
   .....: 
Out[134]: 
0    False
1    False
2     True
3     True
4    False
5    False
dtype: boolean

注意

matchfullmatchcontains 之间的区别在于严格性:fullmatch 测试整个字符串是否与正则表达式匹配;match 测试正则表达式是否在字符串的第一个字符处开始匹配;contains 测试正则表达式是否在字符串中的任何位置匹配。

re 包中对应于这三种匹配模式的函数分别是 re.fullmatchre.matchre.search

matchfullmatchcontainsstartswithendswith 这样的方法接受一个额外的 na 参数,以便可以将缺失值视为 True 或 False

In [135]: s4 = pd.Series(
   .....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   .....: )
   .....: 

In [136]: s4.str.contains("A", na=False)
Out[136]: 
0     True
1    False
2    False
3     True
4    False
5    False
6     True
7    False
8    False
dtype: boolean

创建指示变量#

您可以从字符串列中提取虚拟变量。例如,如果它们用 '|' 分隔

In [137]: s = pd.Series(["a", "a|b", np.nan, "a|c"], dtype="string")

In [138]: s.str.get_dummies(sep="|")
Out[138]: 
   a  b  c
0  1  0  0
1  1  1  0
2  0  0  0
3  1  0  1

字符串 Index 也支持 get_dummies,它返回一个 MultiIndex

In [139]: idx = pd.Index(["a", "a|b", np.nan, "a|c"])

In [140]: idx.str.get_dummies(sep="|")
Out[140]: 
MultiIndex([(1, 0, 0),
            (1, 1, 0),
            (0, 0, 0),
            (1, 0, 1)],
           names=['a', 'b', 'c'])

另请参阅 get_dummies()

方法摘要#

方法

描述

cat()

连接字符串

split()

根据分隔符拆分字符串

rsplit()

根据分隔符拆分字符串,从字符串末尾开始

get()

索引到每个元素(检索第 i 个元素)

join()

使用传递的分隔符连接 Series 中每个元素的字符串

get_dummies()

根据分隔符拆分字符串,返回包含虚拟变量的 DataFrame

contains()

如果每个字符串包含模式/正则表达式,则返回布尔数组

replace()

用其他字符串或给定事件的调用函数的返回值替换模式/正则表达式/字符串的出现

removeprefix()

从字符串中删除前缀,即仅在字符串以前缀开头时删除。

removesuffix()

从字符串中删除后缀,即仅在字符串以后缀结尾时删除。

repeat()

重复值(s.str.repeat(3) 等效于 x * 3

pad()

在字符串的左侧、右侧或两侧添加空格

center()

等效于 str.center

ljust()

等效于 str.ljust

rjust()

等效于 str.rjust

zfill()

等效于 str.zfill

wrap()

将长字符串拆分为长度小于给定宽度的行

slice()

切片 Series 中的每个字符串

slice_replace()

用传递的值替换每个字符串中的切片

count()

计算模式出现的次数

startswith()

等效于每个元素的 str.startswith(pat)

endswith()

等效于每个元素的 str.endswith(pat)

findall()

计算每个字符串中模式/正则表达式的所有出现次数的列表

match()

对每个元素调用 re.match,将匹配的组作为列表返回

extract()

对每个元素调用 re.search,返回一个 DataFrame,其中包含每个元素的一行和每个正则表达式捕获组的一列

extractall()

对每个元素调用 re.findall,返回一个 DataFrame,其中包含每个匹配项的一行和每个正则表达式捕获组的一列

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