一、str对象

In [1]: import numpy as np
In [2]: import pandas as pd

1. str对象的设计意图

str 对象是定义在 IndexSeries 上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,有许多函数的用法 pandas 照搬了它的设计,例如字母转为大写的操作:

In [3]: var = 'abcd'
In [4]: str.upper(var) # Python内置str模块
Out[4]: 'ABCD'
In [5]: s = pd.Series(['abcd', 'efg', 'hi'])
In [6]: s.str
Out[6]: <pandas.core.strings.accessor.StringMethods at 0x264028543c8>
In [7]: s.str.upper() # pandas中str对象上的upper方法
Out[7]: 
0    ABCD
1     EFG
2      HI
dtype: object

根据文档 API 材料,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,这为批量处理序列提供了有力的工具。

2. []索引器

对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素:

In [8]: var[0]
Out[8]: 'a'

同时也能通过切片得到子串:

In [9]: var[-1: 0: -2]
Out[9]: 'db'

通过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:

In [10]: s.str[0]
Out[10]: 
0    a
1    e
2    h
dtype: object
In [11]: s.str[-1: 0: -2]
Out[11]: 
0    db
1     g
2     i
dtype: object
In [12]: s.str[2]
Out[12]: 
0      c
1      g
2    NaN
dtype: object

3. string类型

在上一章提到,从 pandas1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。

总体上说,绝大多数对于 objectstring 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:

首先,应当尽量保证每一个序列中的值都是字符串的情况下才使用 str 属性,但这并不是必须的,其必要条件是序列中至少有一个可迭代(Iterable)对象,包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型的 str 对象和 object 类型的 str 对象返回结果可能是不同的。

In [13]: s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
In [14]: s.str[1]
Out[14]: 
0    temp_1
1         b
2       NaN
3         y
dtype: object
In [15]: s.astype('string').str[1]
Out[15]: 
0    1
1    '
2    .
3    y
dtype: string

除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。而 string 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{“,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。

除了对于某些对象的 str 序列化方法不同之外,两者另外的一个差别在于, string 类型是 Nullable 类型,但 object 不是。这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtypeIntbooleanNullable 类型,而 object 类型则会分别返回 int/floatbool/object ,取决于缺失值的存在与否。同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。

In [16]: s = pd.Series(['a'])
In [17]: s.str.len()
Out[17]: 
0    1
dtype: int64
In [18]: s.astype('string').str.len()
Out[18]: 
0    1
dtype: Int64
In [19]: s == 'a'
Out[19]: 
0    True
dtype: bool
In [20]: s.astype('string') == 'a'
Out[20]: 
0    True
dtype: boolean
In [21]: s = pd.Series(['a', np.nan]) # 带有缺失值
In [22]: s.str.len()
Out[22]: 
0    1.0
1    NaN
dtype: float64
In [23]: s.astype('string').str.len()
Out[23]: 
0       1
1    <NA>
dtype: Int64
In [24]: s == 'a'
Out[24]: 
0     True
1    False
dtype: bool
In [25]: s.astype('string') == 'a'
Out[25]: 
0    True
1    <NA>
dtype: boolean

最后需要注意的是,对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series

In [26]: s = pd.Series([12, 345, 6789])
In [27]: s.astype('string').str[1]
Out[27]: 
0    2
1    4
2    7
dtype: string
下一节:这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考 正则表达式必知必会 一书。