文本数据
str对象
str对象的设计意图
str
对象是定义在Index
或Series
上的属性,专门用于逐元素处理文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其str
对象。在Python标准库中也有str
模块,为了使用上的便利,有许多函数的用法pandas
照搬了它的设计
【】索引器
对于str
对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过[]
可以取出某个位置的元素,同时也能通过切片得到子串。
string类型
从pandas
的1.0.0
版本开始,引入了string
类型,其引入的动机在于:原来所有的字符串类型都会以object
类型的Series
进行存储,但object
类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或category
一样,具有自己的数据存放类型,从而引入了string
类型。
总体上说,绝大多数对于object
和string
类型的序列使用str
对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
string类型要求序列中至少有一个可迭代(Iterable)对象,包括但不限于字符串、字典、列表。但一般来说,当每一个序列中的值都是字符串的情况下使用str
属性最佳。
因为对于一个可迭代对象,string
类型的str
对象和object
类型的str
对象返回结果可能是不同的。
正则表达式基础
一般字符的匹配
正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了python
中re
模块的findall
函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。
re.findall(正则表达式,待匹配的字符串)
元字符基础
元字符 | 描述 |
---|---|
. | 匹配除换行符以外的任意字符 |
[ ] | 字符类,匹配方括号中包含的任意字符 |
[^ ] | 否定字符类,匹配方括号中不包含的任意字符 |
* | 匹配前面的子表达式零次或多次 |
+ | 匹配前面的子表达式一次或多次 |
? | 匹配前面的子表达式零次或一次 |
{n,m} | 花括号,匹配前面字符至少 n 次,但是不超过 m 次 |
(xyz) | 字符组,按照确切的顺序匹配字符xyz |
| | 分支结构,匹配符号之前的字符或后面的字符 |
\ | 转义符,它可以还原元字符原来的含义 |
^ | 匹配行的开始 |
$ | 匹配行的结束 |
简写字符集
此外,正则表达式中还有一类简写字符集,其等价于一组字符的集合:
简写 | 描述 |
---|---|
\w | 匹配所有字母、数字、下划线: [a-zA-Z0-9_] |
\W | 匹配非字母和数字的字符: [^\w] |
\d | 匹配数字: [0-9] |
\D | 匹配非数字: [^\d] |
\s | 匹配空格符: [\t\n\f\r\p{Z}] |
\S | 匹配非空格符: [^\s] |
\B | 匹配一组非空字符开头或结尾的位置,不代表具体字符 |
文本处理的五类操作
拆分
Series.str.split(正则表达式, n, expand)
- n : 从左到右的最大拆分次数
- expand:是否展开为多个列,默认False
合并
str.join
表示用某个连接符把Series
中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值
s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
s.str.join('-')
#0 a-b
#1 NaN
#2 NaN
#dtype: object
str.cat
用于合并两个序列,主要参数为连接符sep
、连接形式join
以及缺失值替代符号na_rep
,其中连接形式默认为以索引为键的左连接。
s1 = pd.Series(['a','b'])
s2 = pd.Series(['cat','dog'])
s1.str.cat(s2,sep='-')
#0 a-cat
#1 b-dog
#dtype: object
匹配
str.contains
返回了每个字符串是否包含正则模式的布尔序列
str.startswith
和str.endswith
返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:
替换
str.replace
和replace
并不是一个函数,在使用字符串替换时应当使用前者。
提取
提取既可以认为是一种返回具体元素(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split
例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract
进行提取:
s = pd.Series(['上海市黄浦区方浜中路249号',
'上海市宝山区密山路5号',
'北京市昌平区北农路2号'])
pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
s.str.extract(pat)
常用字符串函数
字母型函数
upper, lower, title, capitalize, swapcase
这五个函数主要用于字母的大小写转化。和标准库一样。
数值型函数
pd.to_numeric
方法,它虽然不是str
对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括errors
和downcast
分别代表了非数值的处理模式和转换类型。其中,对于不能转换为数值的有三种errors
选项,raise, coerce, ignore
分别表示直接报错、设为缺失以及保持原来的字符串。
统计型函数
count
和len
的作用分别是返回出现正则模式的次数和字符串的长度
格式型函数
格式型函数主要分为两类,第一种是除空型,第二种时填充型。
其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip
,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。
第二类填充型函数,pad
是最灵活的,它可以选定字符串长度、填充的方向和填充内容。还有rjust, ljust, center,zfill
,需要注意ljust
是指右侧填充而不是左侧填充。
练习
Ex1 房屋信息数据集
现有一份房屋信息数据集如下:
df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
df.head(3)
- 将
year
列改为整数年份存储。 - 将
floor
列替换为Level, Highest
两列,其中的元素分别为string
类型的层类别(高层、中层、低层)与整数类型的最高层数。 - 计算房屋每平米的均价
avg_price
,以***元/平米
的格式存储到表中,其中***
为整数。
## 1
df.year = pd.to_numeric(df.year.str[:4], errors='ignore').astype('Int64')
df.head()
## 2
import re
pat = '(?P<Level>\w层)(共(?P<Highest>\d+)层)'
df = pd.concat([df.drop(columns='floor'), df.floor.str.extract(pat)], axis=1)
df.Level = df.Level.astype('string')
df.Highest = pd.to_numeric(df.Highest).astype('Int64')
df.head()
## 3
area_series = pd.to_numeric(df.area.str[:-1])
price_series = pd.to_numeric(df.price.str[:-1])
df['avg_price'] = (round(price_series * 10000 / area_series, 2)).astype('string') + '元/平米'
df.head()
Ex2 权游剧本数据集
现有一份权力的游戏剧本数据集如下:
df = pd.read_csv('../data/script.csv')
df.head(3)
- 计算每一个
Episode
的台词条数。 - 以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
- 若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 n n n个问号,则认为回答者回答了 n n n个问题,请求出回答最多问题的前五个人。
## 1
df = df.copy()
df.columns = df.columns.str.strip()
#进行分组求和,求出每一个Episode的台词条数
gb = df.groupby('Episode')['Sentence'].count()
gb.head(2)
## 2
#求出每行单词量
df['Vocabulary'] = df['Sentence'].str.split(' ').str.len()
#分组求出平均单词量
gb = df.groupby('Name')['Vocabulary'].mean()
#根据values进行排序
gb = gb.sort_values(ascending = False)
#选取前5
gb.head(5)
## 3
#求出回答问题的数量
df['count'] = df['Sentence'].str.count('\?')
#分组求出总回答问题数
gb = df.groupby('Name')['count'].sum()
#根据values进行排序
gb = gb.sort_values(ascending = False)
#选取前5
gb.head(5)