import numpy as np
import pandas as pd
import re

8.1 str对象
8.1.1 str对象的设计意图
str对象是定义在Index或Series上的属性,专门用于逐元素处理文本内容。对一个序列进行文本处理,首先要获取其str对象。处理方法同python相像

var = 'abcd'
str.upper(var)  #python 内置str模块
'ABCD'
s = pd.Series(['abcd', 'efg', 'hi'])
s.str
<pandas.core.strings.StringMethods at 0x225ffc08580>
# 相当于先转成str对象,再使用基于str模块的upper方法
s.str.upper()
0    ABCD
1     EFG
2      HI
dtype: object

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

var[0]
'a'
# 切片得到子串
var[-1: 0: -2]
'db'
# 对str对象使用[]索引器,可以完成完全一致的功能,如果超出范围则返回缺失值
s.str[0] # 取每个Series的第一个值
0    a
1    e
2    h
dtype: object
# 注意这里不包括第0个值
s.str[-1: 0: -2]
0    db
1     g
2     i
dtype: object
# 没有索引值,则用NaN补充
s.str[2]
0      c
1      g
2    NaN
dtype: object

8.1.3 string类型
从pandas1.0.0版本开始,字符串有同数值型或category一样,具有自己的数据存放类型,从而引入了string类型
绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:

  1. 应当尽量保证每一个序列中的值都是字符串的情况下才使用str属性,这不是必须的,其必要条件是序列中至少有一个可迭代对象,包括但不限于字符串、字典、列表。
    对于一个可迭代对象,string类型的str对象和object类型的str对象返回结果可能是不同的
s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a','b'], 0.5, 'my_string'])
# object类型的str对象
s.str[1]
0    temp_1
1         b
2       NaN
3         y
dtype: object
s.astype('string')
0    {1: 'temp_1', 2: 'temp_2'}
1                    ['a', 'b']
2                           0.5
3                     my_string
dtype: string
# 取的索引值为转换为string格式后的字符串
s.astype('string').str[1]
0    1
1    '
2    .
3    y
dtype: string

解释:
除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回 temp_1 字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。而 string 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即“{“,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
除了对于某些对象的 str 序列化方法不同之外,两者另外的一个差别在于,string 类型是 Nullable 类型,但object 不是。这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分
别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object,取决于缺失值的存在与否。同时,字符串的比较操作,也具有相似的特性,string 返回 Nullable 类型,但object 不会。

s = pd.Series(['a'])
s.str.len()
0    1
dtype: int64
s.astype('string').str.len()
0    1
dtype: Int64
s == 'a'
0    True
dtype: bool
s.astype('string') == 'a'
0    True
dtype: boolean
s = pd.Series(['a', np.nan]) # 带有缺失值
# object缺失值用NaN
s.str.len()
0    1.0
1    NaN
dtype: float64
# string用缺失值NA
s.astype('string').str.len()
0       1
1    <NA>
dtype: Int64
# object如果不匹配用False
s == 'a'
0     True
1    False
dtype: bool
# string缺失值用NA(Nullable类型)
s.astype('string') == 'a'
0    True
1    <NA>
dtype: boolean

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

s = pd.Series([12,345,6789])
s.astype('string').str[1]
0    2
1    4
2    7
dtype: string

8.2 正则表达式
8.2.1 一般字符的匹配
正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。

import re
# 返回匹配正则表达式的子字符串
re.findall('Apple','Apple! This is an Apple!')
['Apple', 'Apple']

元字符 描述
. 匹配除换行符以外的任意字符
[ ] 字符类,匹配方括号中包含的任意字符。
[^ ] 否定字符类,匹配方括号中不包含的任意字符

  • 匹配前面的子表达式零次或多次
  • 匹配前面的子表达式一次或多次
    ? 匹配前面的子表达式零次或一次
    {n,m} 花括号,匹配前面字符至少 n 次,但是不超过 m 次
    (xyz) 字符组,按照确切的顺序匹配字符 xyz。
    | 分支结构,匹配符号之前的字符或后面的字符
    \ 转义符,它可以还原元字符原来的含义
    ^ 匹配行的开始
    $ 匹配行的结束
# . 匹配除换行符以外的任意字符,把字符串拆解为一个个字符
re.findall('.','abc')
['a', 'b', 'c']
# [ ] 字符类,匹配方括号中包含的任意字符。
# 取出的是字符
re.findall('[ac]', 'abc')
['a', 'c']
# [^ ] 否定字符类,匹配方括号中不包含的任意字符
re.findall('[^ac]','abc')
['b']
# 因为用的是中括号,所以匹配的是从左到右依次匹配字符串中包含a和b的子字符串(长度为2)
re.findall('[ab]{2}', 'aaaabbbb') #{n}指匹配n次
['aa', 'aa', 'bb', 'bb']
re.findall('[ab]{2}', 'aaaaabbbb') #{n}指匹配n次
['aa', 'aa', 'ab', 'bb']
re.findall('aaa|bbb', 'aaaaabbbb')
['aaa', 'bbb']
# 两个\和一个\都是转义字符,用|隔开or关系的两个子匹配表达式
# 左侧的匹配a? 右侧的匹配a*
re.findall(r'a\?|a\*', 'aa?a*a')
['a?', 'a*']
# a后面匹配0次或1次\
re.findall(r'a\\?|a\*', 'aa?a*a')
['a', 'a', 'a', 'a']
re.findall(r'a\\', 'a\\a?a*a')
['a\\']
# ? 匹配前面的子表达式零次或一次
# . 匹配除换行符以外的任意字符,限定只匹配一个字符
re.findall('a?.', 'abaacadaae')
['ab', 'aa', 'c', 'ad', 'aa', 'e']

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

# 匹配任意字符+s
re.findall('.s', 'Apple!This is an Apple!')
['is', 'is']
# 匹配所有字母、数字、下划线: [a-zA-Z0-9_],限定每个包含两个字符
re.findall('\w{2}', '09 8? 7w c_ 9q p@')
['09', '7w', 'c_', '9q']
# \w匹配所有字母、数字、下划线
# \W 匹配非字母和数字的字符
# \B 匹配一组非空字符开头或结尾的位置,不代表具体字符
# 筛取上一个正则表达式没有提取出来的子字符串(不带空字符)
re.findall('\w\W\B', '09 8? 7w c_ 9q p@')
['8?', 'p@']
# \s 匹配空格符: [\t\n\f\r\p{Z}]
# 任意字符,1个空字符,任意字符,即提取空字符及其前后的字符
re.findall('.\s.', 'Constant dropping wears the stone.')
['t d', 'g w', 's t', 'e s']
# "区"字前有2-3个字符,"路"字前有2-3个字符,"号"字前是数字
re.findall('上海市(.{2,3}区)(.{2,3}路)(\d+号)','上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
[('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]

8.3 文本处理的五类操作
8.3.1 拆分
str.split 能够把字符串的列进行拆分,第一个参数是正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。

s = pd.Series(['上海市黄浦区方滨中路249号','上海市宝山区密山路5号'])
# 匹配中括号中包含的任意字符,根据匹配的字符进行拆分
s.str.split('[市区路]')
0    [上海, 黄浦, 方滨中, 249号]
1       [上海, 宝山, 密山, 5号]
dtype: object
# 最大拆分为3个,展开为多个列expand
s.str.split('[市区路]', n=2, expand=True)
0 1 2
0 上海 黄浦 方滨中路249号
1 上海 宝山 密山路5号

类似的函数str.rsplit,区别在于使用n参数的时候是从右往左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:

s.str.rsplit('[市区路]', n=2, expand=True)
0
0 上海市黄浦区方滨中路249号
1 上海市宝山区密山路5号

8.3.2 合并
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
s2.index = [1,2]
# outer 并集
s1.str.cat(s2, sep='-',na_rep = '?',join = 'outer')
0      a-?
1    b-cat
2    ?-dog
dtype: object

8.3.3 匹配
str.contains返回了每个字符串是否包含正则模式的布尔序列

s = pd.Series(['my_cat','he is fat', 'railway station'])
# \s空格符,\w字母, 'at'。限定一定是一个空格,一个字母+at
s.str.contains('\s\wat')
0    False
1     True
2    False
dtype: bool

str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式

s.str.startswith('my')
0     True
1    False
2    False
dtype: bool
s.str.endswith('t')
0     True
1     True
2    False
dtype: bool

如果需要用正则表达式来检测开始或结束字符串模式,可以用str.match,其返回了每个字符串起始处是否符合给定正则模式的布尔序列:

s.str.match('m|h')
0     True
1     True
2    False
dtype: bool
# 反转后匹配,|左侧的匹配到taf, |右侧的匹配到n
s.str[::-1].str.match('ta[f|g]|n')
0    False
1     True
2     True
dtype: bool
# 也能通过在str.contains的正则中使用^和$来实现
# ^匹配开头字母
s.str.contains('^[m|h]')
0     True
1     True
2    False
dtype: bool
# 包含fat or gat的 或 末尾包含n的
s.str.contains('[f|g]at|n$')
0    False
1     True
2     True
dtype: bool

返回索引的匹配函数,str.find,str.rfind,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。这两个函数不支持正则匹配,只能用于字符子串的匹配

s = pd.Series(['This is an apple.That is not an apple.'])
s.str.find('apple')
0    11
dtype: int64
# 索引值依旧是从左往右进行匹配的索引结果
s.str.rfind('apple')
0    32
dtype: int64

8.3.4 替换
str.replace 和 replace 并不是一个函数,在使用字符串替换时应当使用前者。

s = pd.Series(['a_1_b', 'c_?'])
# 替换数字或者?,为new
s.str.replace('\d|\?', 'new')
0    a_new_b
1      c_new
dtype: object

当需要对不同部分进行有差别的替换时,可以利用 子组 的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意 group(k) 代表匹配到的第 k 个子组(圆括号之间的内容):

# 注意原s的Series值不能有多余的空格,否则无法匹配正确
s = pd.Series(['上海市黄浦区方浜中路249号', '上海市宝山区密山路5号', '北京市昌平区北农路2号'])
# 匹配数字一次到多次
pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
district = {'昌平区': 'CP District', '黄浦区': 'HP District', '宝山区': 'BS District'}
road = {'方浜中路': 'Mid Fangbin Road','密山路': 'Mishan Road','北农路': 'Beinong Road'}
def my_func(m):# m.group(1) :上海市 上海市 北京市str_city = city[m.group(1)]# m.group(2) :黄浦区 宝山区 昌平区  str_district = district[m.group(2)]# m.group(3): 方浜中路 密山路 北农路str_road = road[m.group(3)]   # m.group(4)[:-1]: 249 5 2# 数字前用No.连接str_no = 'No.'+m.group(4)[:-1]return ' '.join([str_city,str_district, str_road, str_no])
# 把pat按确切顺序,从左到右匹配包含“市、区、路、号” 及其前面的内容,换成my_func定义的内容
s.str.replace(pat, my_func)
0    Shanghai HP District Mid Fangbin Road No.249
1           Shanghai BS District Mishan Road No.5
2           Beijing CP District Beinong Road No.2
dtype: object
# 可以使用命名子组更加清晰地写出子组代表的含义:
# ?P<value>的意思就是命名一个名字为value的组,匹配规则符合后面的\w+,因此可以由m.group('市名')访问到这个结果
pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
def my_func(m):str_city = city[m.group('市名')]str_district = district[m.group('区名')]str_road = road[m.group('路名')]str_no = 'No.' +m.group('编号')[:-1]return ' '.join([str_city, str_district, str_road, str_no])
s.str.replace(pat, my_func)
0    Shanghai HP District Mid Fangbin Road No.249
1           Shanghai BS District Mishan Road No.5
2           Beijing CP District Beinong Road No.2
dtype: object

8.3.5 提取
提取既可以认为是一种返回具体元素(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split 例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用 str.extract 进行提取:

pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
s.str.extract(pat)
0 1 2 3
0 上海市 黄浦区 方浜中路 249号
1 上海市 宝山区 密山路 5号
2 北京市 昌平区 北农路 2号
# 通过子组的命名,可以直接对新生成DataFrame 的列命名:
pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
s.str.extract(pat)
市名 区名 路名 编号
0 上海市 黄浦区 方浜中路 249号
1 上海市 宝山区 密山路 5号
2 北京市 昌平区 北农路 2号

str.extractall 不同于 str.extract 只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:

s = pd.Series(['A135T15,A26S5', 'B674S2,B25T6'],index=['my_A','my_B'])
pat = '(A|B)(\d+)(T|S)(\d+)'
# 筛取从左到右A多个数字或B多个数字(顺序连接)T多个数字或S多个数字
# 每个index都匹配到了两组结果,因此,以多级索引方式存储
# 发现正则是以每一组()进行分组,而对一组[]匹配到的结果不进行返回
s.str.extractall(pat)
0 1 2 3
match
my_A 0 A 135 T 15
1 A 26 S 5
my_B 0 B 674 S 2
1 B 25 T 6
# 该结果表明,用extractall得到的是分组后的结果,即一组()匹配的结果
pat_t = '[A|B](\d+)[T|S](\d+)'
s.str.extractall(pat_t)
0 1
match
my_A 0 135 15
1 26 5
my_B 0 674 2
1 25 6
# 注意要用大写的P
pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
s.str.extractall(pat_with_name)
name1 name2
match
my_A 0 135 15
1 26 5
my_B 0 674 2
1 25 6

str.findall 的功能类似于 str.extractall ,区别在于前者把结果存入列表,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。

s.str.findall(pat_t)
my_A    [(135, 15), (26, 5)]
my_B     [(674, 2), (25, 6)]
dtype: object

8.4 常用字符串函数
8.4.1 字母型函数
upper, lower, title, capitalize, swapcase 这五个函数主要用于字母的大小写转化

s = pd.Series(['lower', 'CAPTIALS','this is a sentence', 'SwApCaSe'])
s.str.upper()
0                 LOWER
1              CAPTIALS
2    THIS IS A SENTENCE
3              SWAPCASE
dtype: object
s.str.lower()
0                 lower
1              captials
2    this is a sentence
3              swapcase
dtype: object
# 每个词首字母大写
s.str.title()
0                 Lower
1              Captials
2    This Is A Sentence
3              Swapcase
dtype: object
# 句子或词的首字母大写,区别于title的用法
s.str.capitalize()
0                 Lower
1              Captials
2    This is a sentence
3              Swapcase
dtype: object
# 小写->大写,大写->小写。逐字符
s.str.swapcase()
0                 LOWER
1              captials
2    THIS IS A SENTENCE
3              sWaPcAsE
dtype: object

8.4.2 数值型函数
pd.to_numeric 方法,它虽然不是 str 对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括 errors 和 downcast 分别代表了非数值的处理模式和转换类型。其中,对于不能转换为数值的有三种 errors 选项,raise, coerce, ignore 分别表示直接报错、设为缺失以及保持原来的字符串。

s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])
# 保持原来的字符串
pd.to_numeric(s,errors='ignore')
0       1
1     2.2
2      2e
3      ??
4    -2.1
5       0
dtype: object
# 非只要带有非数值的字符,直接将该字符串设为NaN
# 原本int型数值转换成float型
pd.to_numeric(s, errors='coerce')
0    1.0
1    2.2
2    NaN
3    NaN
4   -2.1
5    0.0
dtype: float64

在数据清洗时,可以利用 coerce 的设定,快速查看非数值型的行:

pd.to_numeric(s, errors='coerce').isna()
0    False
1    False
2     True
3     True
4    False
5    False
dtype: bool
# 判断缺失值的布尔序列:2,3
s[pd.to_numeric(s, errors='coerce').isna()]
2    2e
3    ??
dtype: object

8.4.3 统计型函数
count和len作用分别是返回出现正则模式的次数和字符串的长度:

# rat和fat, 两个带ee的
s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
s.str.count('[r|f]at|ee')
0    2
1    2
dtype: int64
s.str.len()
0    14
1    19
dtype: int64

8.4.4格式型函数
第一种除空型,第二种填充型。
除空型有3种:strip 去除两侧空格, rstrip去除右侧空格, lstrip去除左侧空格
应用于数据清洗,特别是列名含有非法空格的时候

my_index = pd.Index([' col1', 'col2 ',' col3 '])
# 左右两侧空格都去掉
my_index.str.strip().str.len()
Int64Index([4, 4, 4], dtype='int64')
# 去掉右空格
my_index.str.rstrip().str.len()
Int64Index([5, 4, 5], dtype='int64')
# 去掉右侧空格
my_index.str.rstrip().str.len()
Int64Index([5, 4, 5], dtype='int64')

对于填充型函数而言,pad是最灵活的,可以选定字符串长度、填充的方向和填充内容

s = pd.Series(['a', 'b', 'c'])
# 第一个值为Series中每一个元素总的字符串长度,第二个参数为向左填充,第三个参数为重复填充的内容
s.str.pad(5,'left','*')
0    ****a
1    ****b
2    ****c
dtype: object
s.str.pad(5,'right','*')
0    a****
1    b****
2    c****
dtype: object
s.str.pad(5,'both','*')
0    **a**
1    **b**
2    **c**
dtype: object

上述可以分别用rjust, ljust, center来等效完成,注意ljust是右侧填充而不是左侧填充

s.str.rjust(5,'*')
0    ****a
1    ****b
2    ****c
dtype: object
s.str.ljust(5,'*')
0    a****
1    b****
2    c****
dtype: object
s.str.center(5,'*')
0    **a**
1    **b**
2    **c**
dtype: object

读取excel文件时,会出现数字前补0的需求,例如证券代码读入的时候会把“000007”作为数值7来处理,pandas还可以用zfill来实现

s = pd.Series([7,155,303000]).astype('string')
# 如果第一个参数小于6,则第0和1个数补齐到对应位数,第2个数不变
s.str.pad(6,'left','0')
0    000007
1    000155
2    303000
dtype: string
s.str.rjust(6,'0')
0    000007
1    000155
2    303000
dtype: string
s.str.zfill(6)
0    000007
1    000155
2    303000
dtype: string

8.5.1 练习1:房屋信息数据集

df=pd.read_excel('house_info.xls', usecols=['floor', 'year', 'area', 'price'])
df.head()
floor year area price
0 高层(共6层) 1986年建 58.23㎡ 155万
1 中层(共20层) 2020年建 88㎡ 155万
2 低层(共28层) 2010年建 89.33㎡ 365万
3 低层(共20层) 2014年建 82㎡ 308万
4 高层(共1层) 2015年建 98㎡ 117万
# 1. 将year列改为整数年份存储
# 要对用to_numeric将字符串转为数值型,然后再转为Int64
df.year = pd.to_numeric(df.year.str[0:3]).astype('Int64')
df.head()
floor year area price
0 高层(共6层) 198 58.23㎡ 155万
1 中层(共20层) 202 88㎡ 155万
2 低层(共28层) 201 89.33㎡ 365万
3 低层(共20层) 201 82㎡ 308万
4 高层(共1层) 201 98㎡ 117万

将floor列替换为Level,Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数

# 注意pad中因为原始数据的共xx层是用中文的小括号括住,所以写正则时也要用中文的括号
pat = '(\w层)(共(\d+)层)'
# 提取值用extract
add_cols = df.floor.str.extract(pat).rename(columns = {0:'Level', 1:'Highest'})
add_cols.head()
Level Highest
0 高层 6
1 中层 20
2 低层 28
3 低层 20
4 高层 1
df = pd.concat([df.drop(columns = ['floor']),add_cols], 1)
df.head()
year area price Level Highest
0 198 58.23㎡ 155万 高层 6
1 202 88㎡ 155万 中层 20
2 201 89.33㎡ 365万 低层 28
3 201 82㎡ 308万 低层 20
4 201 98㎡ 117万 高层 1
# 计算房屋每平米的均价avg_price, 以**元/平米 的格式存储到表中,其中 ***为整数
# 先要把area的数值取出,和price的数值取出,同时要把price的数值转换类型*10000
num_area = pd.to_numeric(df.area.str[:-1])
num_price = pd.to_numeric(df.price.str[:-1])
df['avg'] = ((num_price/num_area)*10000).astype('int').astype('string') + '元/平米'
df.head()
year area price Level Highest avg
0 198 58.23㎡ 155万 高层 6 26618元/平米
1 202 88㎡ 155万 中层 20 17613元/平米
2 201 89.33㎡ 365万 低层 28 40859元/平米
3 201 82㎡ 308万 低层 20 37560元/平米
4 201 98㎡ 117万 高层 1 11938元/平米

8.5.2 《权利的游戏》剧本数据集

df = pd.read_csv('script.csv')
df.head()
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?
3 2011-04-17 Season 1 Episode 1 Winter is Coming will Close as any man would.
4 2011-04-17 Season 1 Episode 1 Winter is Coming gared We should head back to the wall.
# 计算每一个Episode 的台词条数
df = pd.read_csv('script.csv')
# 要对列进行分隔
df.columns = df.columns.str.strip()
# 以同一个Season下的同一个Episode进行groupby,然后筛选出Sentence的数量
df.groupby(['Season', 'Episode'])['Sentence'].count().head()
Season    Episode
Season 1  Episode 1     327Episode 10    266Episode 2     283Episode 3     353Episode 4     404
Name: Sentence, dtype: int64

2.以空格为单词的分割符号, 请求出每句台词平均单词量最多的前五人

df.set_index('Name').Sentence.str.split().str.len().groupby('Name').mean().sort_values(ascending=False).head()
# 以Name分组,取出Sentence, 对Sentence以空格进行分割,得到每个Sentence的词的列表,求得词的数量
# 再以Name进行分组求每个人的平均单词数量,再用sort_values进行排序
df.set_index('Name').Sentence.str.split().str.len().groupby('Name').mean().sort_values(ascending=False).head(5)
Name
male singer          109.000000
slave owner           77.000000
manderly              62.000000
lollys stokeworth     62.000000
dothraki matron       56.666667
Name: Sentence, dtype: float64

3.若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n个问号,则认为回答者回答了n个问题,请求出回答最多问题的前五人

df.Name.shift(-1).head()
0            will
1    waymar royce
2            will
3           gared
4           royce
Name: Name, dtype: object
# Name的向下移一位,表示下一位是上一位(问问题的)的回答
# 即这个人回答的是对应的Sentence问题
s = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
s
Name
will                What do you expect? They're savages. One lot s...
waymar royce        I've never seen wildlings do a thing like this...
will                                           How close did you get?
gared                                         Close as any man would.
royce                                We should head back to the wall....
bronn               I think we can all agree that ships take prece...
tyrion lannister        I think that's a very presumptuous statement.
man                 I once brought a jackass and a honeycomb into ...
all                                           The Queen in the North!
NaN                 The Queen in the North! The Queen in the North...
Length: 23911, dtype: object
# 求一个Sentence有几个问号,然后求每个人回答问题的问号数量并排序取前5个
s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head(5)
Name
tyrion lannister    527
jon snow            374
jaime lannister     283
arya stark          265
cersei lannister    246
dtype: int64

最新文章

  1. portal商品展示功能逻辑
  2. 【jquery模仿net控件】简单的dropdownlist与datalist
  3. 下载人脸认证助手_认证助手最新版
  4. 文件用户如何将一个有界面的正常app和一个或多个越狱插件.deb同时安装到手机上...
  5. live555 接收rtsp视频流流程分析
  6. C / C++ 之整体知识总结,点进来,不后悔!
  7. 10 - java 权限修饰符
  8. Java并发——线程中断学习
  9. 因代码不规范,码农枪击4名同事,一人情况危急
  10. Hershell:跨平台反向Shell生成器
  11. RegCleanPro (微软认证-注册表清理软件)
  12. 多维数据库概述之一---多维数据库的选择
  13. WebGIS第一课:测试高德API并通过
  14. 盘点程序员的那些常用网站
  15. 阿里云云安全中心提供基础版、高级版和企业版有什么区别?
  16. 内网渗透(五十)之域控安全和跨域攻击-使用其他工具导出域账号和散列值
  17. compat-mysql安装_Centos7 rpm方式安装Percona Mysql 8
  18. 这才不是我想看的《时间简史》
  19. IBM云对象存储 - Linux主机通过rclone和COS API上传大文件
  20. 台湾各个大学硕博论文链接,很全,有的可以全文下载。

热门文章

  1. 软件测试的技术(互联网篇)
  2. 服务器部署方案文档,IBM目录服务器部署方案
  3. IPv4编址;A类、B类、C类、D类、E类IP地址(IP地址;网络地址和主机地址;子网掩码;网关;广播地址;)
  4. Linux 实现OpenSSL 服务器端客户端通信
  5. GBASE 8s UDR内存管理_01_mi_alloc
  6. 快速幂计算x的n次幂,递归版本、迭代版本、python实现
  7. 软件设计-设计说明书图表
  8. 王者荣耀在android目录下的名字,王者荣耀手q区有哪些 王者荣耀安卓手Q区名称...
  9. C/C++实现的游戏角色名称名字随机生成代码
  10. 《拥抱机器人时代——Servo杂志中文精华合集》——4.3 理解智能设备