Working with Text Data

原文:http://pandas.pydata.org/pandas-docs/stable/text.html

译者:飞龙 UsyiyiCN

校对:(虚位以待)

系列和索引都配备了一组字符串处理方法,使其易于对数组的每个元素进行操作。也许最重要的是,这些方法自动排除丢失/ NA值。这些通过str属性访问,通常具有与等效(标量)内置字符串方法匹配的名称:

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([u'jack', u'jill', u'jesse', u'frank'], dtype='object')

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

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

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

In [9]: df = pd.DataFrame(randn(3, 2), columns=[' Column A ', ' Column B '],
   ...:                   index=range(3))
   ...: 

In [10]: df
Out[10]: 
    Column A    Column B 
0    0.017428    0.039049
1   -2.240248    0.847859
2   -1.342107    0.368828

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

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

In [12]: df.columns.str.lower()
Out[12]: Index([u' column a ', u' 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.017428  0.039049
1 -2.240248  0.847859
2 -1.342107  0.368828

注意

假如你有一个许多元素都重复的 Series (i.e. Series 中唯一元素的数量远小于Series的长度),将原始的 Series 转换成category 然后使用 .str.<method> or .dt.<property>将会更快.性能差异来自于对category类型的Series,字符串操作在.categories上完成,而不是在每个元素的Series

请注意,类型字符串Series的比较类型category与字符串.categoriesSeries (例如,您不能向对方添加字符串:s + 如果s是类型categorySeries,则 )。此外,对类型list的元素进行操作的.str方法在这种Series上不可用。

Splitting and Replacing Strings

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可以轻松扩展此操作以返回DataFrame。

In [19]: s2.str.split('_', expand=True)
Out[19]: 
     0     1     2
0    a     b     c
1    c     d     e
2  NaN  None  None
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  None
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  None
3  f_g     h

类似replacefindall的方法也可以使用正则表达式

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

Indexing with .str

您可以使用[]表示法直接通过位置位置索引。如果索引超过字符串的末尾,结果将是NaN

In [29]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan,
   ....:                'CABA', 'dog', 'cat'])
   ....: 

In [30]: s.str[0]
Out[30]: 
0      A
1      B
2      C
3      A
4      B
5    NaN
6      C
7      d
8      c
dtype: object

In [31]: s.str[1]
Out[31]: 
0    NaN
1    NaN
2    NaN
3      a
4      a
5    NaN
6      A
7      o
8      a
dtype: object

Extracting Substrings

Extract first match in each subject (extract)

版本0.13.0中的新功能。

警告

在版本0.18.0中,extract获得了expand参数。expand=False时,根据主题和正则表达式模式,它返回SeriesIndexDataFrame (与0.18.0之前的行为相同)。expand=True时,它始终返回一个DataFrame,这从用户的角度来看更一致,更少混淆。

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

使用多个组提取正则表达式会返回每个组一个列的DataFrame。

In [32]: pd.Series(['a1', 'b2', 'c3']).str.extract('([ab])(\d)', expand=False)
Out[32]: 
     0    1
0    a    1
1    b    2
2  NaN  NaN

不匹配的元素返回填充有NaN的行。因此,一系列乱码字符串可以被“转换”为清理过的或更有用的字符串的索引相同的系列或数据帧,而不需要get()来访问元组或re.match对象。结果的dtype始终为对象,即使未找到匹配项,结果只包含NaN

命名组喜欢

In [33]: pd.Series(['a1', 'b2', 'c3']).str.extract('(?P<letter>[ab])(?P<digit>\d)', expand=False)
Out[33]: 
  letter digit
0      a     1
1      b     2
2    NaN   NaN

和可选组

In [34]: pd.Series(['a1', 'b2', '3']).str.extract('([ab])?(\d)', expand=False)
Out[34]: 
     0  1
0    a  1
1    b  2
2  NaN  3

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

如果expand=True,则提取具有一个组的正则表达式将返回一个具有一列的DataFrame

In [35]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=True)
Out[35]: 
     0
0    1
1    2
2  NaN

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

In [36]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=False)
Out[36]: 
0      1
1      2
2    NaN
dtype: object

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

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

In [38]: s
Out[38]: 
A11    a1
B22    b2
C33    c3
dtype: object

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

如果expand=False,则返回Index

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

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

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

如果expand=False,则会引发ValueError

>>> s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
ValueError: only one regex group is supported with Index

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

  1组 > 1组
指数 指数 ValueError
系列 系列 DataFrame

Extract all matches in each subject (extractall)

版本0.18.0中的新功能。

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

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

In [43]: s
Out[43]: 
A    a1a2
B      b1
C      c1
dtype: object

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

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

extractall方法返回每个匹配。extractall的结果始终是其行上具有MultiIndexDataFrameMultiIndex的最后一个级别命名为match,并指示主题中的顺序。

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

当系列中的每个主题字符串完全匹配一个时,

In [47]: s = pd.Series(['a3', 'b3', 'c2'])

In [48]: s
Out[48]: 
0    a3
1    b3
2    c2
dtype: object

then extractall(pat).xs(0, level='match') gives the same result as extract(pat).

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

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

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

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

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

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

版本0.19.0中的新功能。

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

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

Testing for Strings that Match or Contain a Pattern

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

In [56]: pattern = r'[a-z][0-9]'

In [57]: pd.Series(['1', '2', '3a', '3b', '03c']).str.contains(pattern)
Out[57]: 
0    False
1    False
2    False
3    False
4    False
dtype: bool

或匹配模式:

In [58]: pd.Series(['1', '2', '3a', '3b', '03c']).str.match(pattern, as_indexer=True)
Out[58]: 
0    False
1    False
2    False
3    False
4    False
dtype: bool

matchcontains是strictness:match依赖于strict re.match,而contains依赖于re.search

警告

在先前版本中,match用于提取组,返回不那么方便的系列元组。现在优选新方法extract(在上一部分中描述)。

match的旧的,已弃用的行为仍是默认行为。如上所述,通过设置as_indexer=True来使用新的行为。在此模式下,match类似于contains,返回一个布尔系列。新行为将成为未来版本中的默认行为。

matchcontainsstartswithendswith take
额外的na参数,因此缺少的值可以被视为True或False:
In [59]: s4 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])

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

Creating Indicator Variables

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

In [61]: s = pd.Series(['a', 'a|b', np.nan, 'a|c'])

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

字符串Index还支持get_dummies,它返回MultiIndex

版本0.18.1中的新功能。

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

In [64]: idx.str.get_dummies(sep='|')
Out[64]: 
MultiIndex(levels=[[0, 1], [0, 1], [0, 1]],
           labels=[[1, 1, 0, 1], [0, 1, 0, 0], [0, 0, 0, 1]],
           names=[u'a', u'b', u'c'])

另请参见get_dummies()

Method Summary

方法 描述
cat() 串联字符串
split() 拆分分隔符上的字符串
rsplit() 从字符串末尾拆分分隔符上的字符串
get() 索引到每个元素(检索第i个元素)
join() 使用传递的分隔符在系列的每个元素中连接字符串
get_dummies() 在分隔符上分割字符串,返回虚拟变量的DataFrame
contains() 如果每个字符串包含pattern / regex,则返回布尔数组
replace() 用一些其他字符串替换模式/正则表达式的出现
repeat() 重复的值(s.str.repeat(3)等效于x * 3 t2 >)
pad() 向字符串的左侧,右侧或两侧添加空格
center() 等效于str.center
ljust() 等效于str.ljust
rjust() 等效于str.rjust
zfill() 等效于str.zfill
wrap() 将长字符串拆分成长度小于给定宽度的行
slice() 切割系列中的每个字符串
slice_replace() 使用传递的值替换每个字符串中的slice
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
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