第八章 文本数据

来源:https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch8.html

一、str对象

定义在 IndexSeries 上的属性,基本方法和python里的str类似。

s = pd.Series(['abcd', 'efg', 'hi'])
s.str.upper()
#使用索引
s.str[-1: 0: -2]
'''
0    db
1     g
2     i
'''

string类型(序列类型)

和object类型序列的区别:

  1. 对于一个可迭代对象, string 类型的 str 对象和 object 类型的 str 对象返回结果可能是不同的。
s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
s.str[1]
'''
0    temp_1
1         b
2       NaN
3         y
dtype: object
'''
s.astype('string').str[1]
'''
0    1
1    '
2    .
3    y
dtype: string
'''
  1. string 类型是 Nullable 类型,但 object 不是。所以对于nullable的操作和np.nan还是不太一样。
s = pd.Series(['a', np.nan]) # 带有缺失值
操作 结果
s.str.len() 0 1.0
1 NaN
s.astype('string').str.len() 0 1
1
s == 'a' 0 True
1 False
s.astype('string') == 'a' 0 True
1

二、正则表达式基础

1. 一般字符的匹配

import re
re.findall('Apple', 'Apple! This Is an Apple!')

2. 元字符基础

元字符 描述
. 匹配除换行符以外的任意字符
[ ] 字符类,匹配方括号中包含的任意字符。
[^ ] 否定字符类,匹配方括号中不包含的任意字符
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
{n,m} 花括号,匹配前面字符至少 n 次,但是不超过 m 次
(xyz) 字符组,按照确切的顺序匹配字符xyz。
| 分支结构,匹配符号之前的字符或后面的字符
\ 转义符,它可以还原元字符原来的含义
^ 匹配行的开始
$ 匹配行的结束
In [30]: re.findall('.', 'abc') #匹配1个除换行符以外的任意字符
Out[30]: ['a', 'b', 'c']

In [31]: re.findall('[ac]', 'abc')#匹配1个方括号中包含的任意字符
Out[31]: ['a', 'c']

In [32]: re.findall('[^ac]', 'abc')#匹配方括号中不包含的任意一个字符
Out[32]: ['b']

In [33]: re.findall('[ab]{2}', 'aaaabbbb') # {n}指匹配n次
Out[33]: ['aa', 'aa', 'bb', 'bb']

In [34]: re.findall('aaa|bbb', 'aaaabbbb')#匹配符号之前的字符或后面的字符
Out[34]: ['aaa', 'bbb']

In [35]: re.findall('a\\?|a\*', 'aa?a*a')
Out[35]: ['a?', 'a*']

In [36]: re.findall('a?.', 'abaacadaae') #匹配前面的子表达式零次或一次
Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']

3. 简写字符集

等价于一组字符的集合

简写 描述
\w 匹配所有字母、数字、下划线: [a-zA-Z0-9_]
\W 匹配非字母和数字的字符: [^\w]
\d 匹配数字: [0-9]
\D 匹配非数字: [^\d]
\s 匹配空格符: [\t\n\f\r\p{Z}]
\S 匹配非空格符: [^\s]
\B 匹配一组非空字符开头或结尾的位置,不代表具体字符

三、文本处理的五类操作

操作 函数 例子 备注
拆分 str.split s.str.split('[市区路]', n=2, expand=True) 第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand
str.rsplit s.str.rsplit('[市区路]', n=2, expand=True) 和split的区别在于使用 n 参数的时候是从右到左限制最大拆分次数
合并 str.join s.str.join('-') 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了字符串元素则返回缺失值
str.cat s1.str.cat(s2, sep='-', na_rep='?', join='outer') 合并两个序列,主要参数为连接符 sep 、连接形式 join 以及缺失值替代符号 na_rep ,其中连接形式默认为以索引为键的左连接
匹配 str.contains s.str.contains('\s\wat') 返回了每个字符串是否包含正则模式的布尔序列
str.startswith s.str.startswith('my') 不支持正则表达式
str.endswith s.str.endswith('t') 不支持正则表达式
str.match s.str.match('m|h')
等于s.str.contains('^[m|h]')
返回了每个字符串起始处是否符合给定正则模式的布尔序列(startswith)
s.str[::-1].str.match('ta[f|g]|n')
等于s.str.contains('[f|g]at|n$')
反转后匹配,达到endswith的效果
str.find s.str.find('apple') 不支持正则匹配,只能用于字符子串的匹配,返回索引
str.rfind s.str.rfind('apple')
str.findall s.str.findall(pat)
替换 str.replace s.str.replace('\d|?', 'new', regex=True)
提取 str.extract pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
s.str.extract(pat)
只匹配一次;通过子组的命名,可以直接对新生成 DataFrame 的列命名
str.extractall s.str.extractall(pat_with_name) 所有符合条件的模式全部匹配出来

命名子组:pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'

?P:给group命名;m.group('value')可以取出group的值

四、常用字符串函数

1. 字母型函数upper, lower, title, capitalize, swapcase

upper, lower, title, capitalize, swapcase

2. 数值型函数pd.to_numeric

pd.to_numeric

参数:

errors:

  • raise:直接报错
  • coerce:设为缺失
  • ignore:保持原来的字符串

3. 统计型函数count,len

count:返回出现正则模式的次数

len :返回字符串的长度

s.str.count('[r|f]at|ee')
s.str.len()

4. 格式型函数

  1. 除空型
  • strip:去除两侧空格
  • rstrip:去除右侧空格
  • lstrip:去除左侧空格
  1. 填充型
  • pad:可以选定字符串长度、填充的方向和填充内容
  • rjust, ljust, center
  • zfill: 在前面补零
s.str.pad(5,'left','*')  #****a
s.str.pad(5,'right','*') #a****
s.str.pad(5,'both','*')  #**a**
s.str.rjust(5, '*') #****a
s.str.ljust(5, '*') #a****
s.str.center(5, '*')#**a**
s.str.zfill(6) #00000a

五、练习

Ex1:房屋信息数据集

现有一份房屋信息数据集如下:

In [114]: df = pd.read_excel('data/house_info.xls', usecols=[
   .....:                 'floor','year','area','price'])
   .....: 

In [115]: df.head(3)
Out[115]: 
      floor    year    area price
0   高层(共6层)  1986年建  58.23㎡  155万
1  中层(共20层)  2020年建     88㎡  155万
2  低层(共28层)  2010年建  89.33㎡  365万
  1. year 列改为整数年份存储。

注意:pd.to_numeric里的downcast的‘integer’ 最多只有int8,不能转换float,会自动转成float64

df.year = pd.to_numeric(df.year.str[:4]).astype('Int64')
  1. floor 列替换为 Level, Highest 两列,其中的元素分别为 string 类型的层类别(高层、中层、低层)与整数类型的最高层数。
new_cols = df.floor.str.extract('(?P.+)(共(?P\d+)层)')
df = pd.concat([df, new_cols], axis=1).drop(columns='floor')
df.head(3)
'''
    year    area    price   Level   Highest
0   1986    58.23㎡  155万    高层  6
1   2020    88㎡ 155万    中层  20
2   2010    89.33㎡  365万    低层  28
'''
  1. 计算房屋每平米的均价 avg_price ,以 ***元/平米 的格式存储到表中,其中 ***为整数。

float转换为int列:().astype(int)

df.area = df.area.str[:-1]
df.price = df.price.str[:-1]
df['avg_price'] = (pd.to_numeric(df.price)/pd.to_numeric(df.area)*10000).astype(int).astype('string')+"元/平米"
df.head(3)
'''
    year    area    price   Level   Highest avg_price
0   1986    58.23   155 高层  6   26618元/平米
1   2020    88  155 中层  20  17613元/平米
2   2010    89.33   365 低层  28  40859元/平米
'''

Ex2:《权力的游戏》剧本数据集

现有一份权力的游戏剧本数据集如下:

In [116]: df = pd.read_csv('data/script.csv')

In [117]: df.head(3)
Out[117]: 
  Release Date    Season   Episode      Episode Title          Name                                           Sentence
0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...
2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce                             How close did you get?
  1. 计算每一个Episode的台词条数。
df.columns = df.columns.str.strip()
df.groupby(['Season','Episode']).size()
  1. 以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
df['Words Number'] = df['Sentence'].str.count(' ')+1
df.groupby('Name')['Words Number'].mean().sort_values(ascending=False).head()

答案:df.set_index('Name').Sentence.str.split()之后还要重新使用str.len(),不能直接使用len()

df.set_index('Name').Sentence.str.split().str.len().groupby('Name').mean().sort_values(ascending=False).head()
  1. 若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有nn个问号,则认为回答者回答了nn个问题,请求出回答最多问题的前五个人。
df['question'] = df.Sentence.str.count('\?')
df['answer'] = df['question'].shift(1)
df.groupby('Name')['answer'].sum().sort_values(ascending=False).head()

答案:

s = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()

你可能感兴趣的:(第八章 文本数据)