# 1 str对象
# 01 str对象的设计意图
# 02 []索引器
# 03 string类型
# 2 正则表达式基础
# 01 一般字符串的匹配
# findall(正则表达式,待匹配的字符串) 匹配所有出现过但不重叠的模式
# 02 元字符基础
# . 匹配除换行符以外的任意字符
# [] 字符类,匹配方括号中包含的任意字符
# [^] 否定字符类,匹配方括号中不包含的任意字符
# * 匹配前面的子表达式零/多次
# + 匹配前面的子表达式一/多次
# ? 匹配前面的子表达式零/一次
# {n,m} 花括号,匹配前面的字符至少n次,但不超过m次
# (xyz) 字符组,按照确切的匹配顺序匹配字符xyz
# | 分支结构,匹配符号之前或之后的字符
# \ 转义符,还原元字符原来的含义
# ^ 匹配行的开始
# $ 匹配行的结束
# 03 简写字符集
# \w 匹配所有字母、数字、下划线:[a-zA-Z0-9_]
# \W 匹配所有非字母和数字的字符:[^\w]
# \d 匹配数字:[0-9]
# \D 匹配非数字:[^\d]
# \s 匹配空格符:[\t\n\f\r\p{Z}]
# \S 匹配非空格符:[^\s]
# \B 匹配一组非空字符开头或结尾的位置,不代表具体字符
# 3 文本处理的五类操作
# 01 拆分
# str.split(正则表达式,从左到右的最大拆分次数,是否展开为多个列)
# 02 合并
# str.join 表示用某个连接符把Series中的字符串列表连接起来,如果列表中出现非字符串元素则返回缺失值
# s1.str.cat(s2,连接符,缺失值代替符号,连接方式) 用于合并两个序列
# 03 匹配
# str.contains 返回每个字符串是否包含正则模式的布尔序列
# str.startswith/str.endswith 返回每个字符串是否以给定模式开始或结束的布尔序列,不支持正则表达式
# str.match 返回每个字符串起始处是否符合给定正则模式的布尔序列
# str.find/str.rfind 返回从左到右/从右到左第一次匹配的位置的索引,未找到则返回-1,不支持正则匹配
# 04 替换
# 05 提取
# str.extract 匹配一个结果,str.extractall 匹配全部结果以多级索引模式存储
# str.extractall 结果为多级索引,str.findall 结果为列表
# 4 常用字符串函数
# 01 字母型函数
# 02 数值型函数
# 03 统计型函数
# 04 格式型函数
# 除空型
# 填充型
import pandas as pd
import numpy as np
# 1 str对象
# 01 str对象的设计意图
# str对象是定义在Index或Series上的属性专门用于逐元素处理文本内容
var = 'abcd'
str.upper(var) # python内置str模块
s = pd.Series(['abcd','efg','hi'])
s.str.upper() # pandas中str对象上的upper方法
# 02 []索引器
# 对于str对象而言,可理解为对字符串进行了序列化操作,通过[]可取出某位置的元素或切片得到子串
var[0]
var[-1:0:-1] # 从位置-1到0步长为2,包括位置-1的元素,不包括位置0
var[0:-1:1] # 'abc'
# 对str对象使用[]索引器,超出范围返回缺失值
s.str[0]
s.str[-1:0:-2]
s.str[2]
# 03 string类型
s = pd.Series([{1:'temp1',2:'temp2'},['a','b'],0.5,'mystring'])
s.str[0] # object类型
s.astype('string').str[0] # string类型,先把整个元素转为字面意义的字符串(包括符号)
# string类型是Nullable类型,object不是。
# string调用str方法返回Int/bollean的Nullable类型,object返回int/float和bool/object(取决于缺失值存在与否)
s = pd.Series(['a'])
s.str.len() # int64
s.astype('string').str.len() # Int64
s == 'a' # dtype: bool
s.astype('string') == 'a' # dtype: boolean
s = pd.Series(['a',np.nan])
s.str.len() # float64
s.astype('string').str.len() # Int64
s == 'a' # dtype: bool
s.astype('string') == 'a' # dtype: boolean
# 对于全体元素为数值类型的序列,即使其类型为object/category也不能直接用str属性,可用astype转换
s = pd.Series([12,456])
s.astype('string').str[1]
# 2 正则表达式基础
# 01 一般字符串的匹配
import re
# findall(正则表达式,待匹配的字符串) 匹配所有出现过但不重叠的模式
re.findall('Apple', 'Apple! This Is an Apple!')
# 02 元字符基础
# . 匹配除换行符以外的任意字符
# [] 字符类,匹配方括号中包含的任意字符
# [^] 否定字符类,匹配方括号中不包含的任意字符
# * 匹配前面的子表达式零/多次
# + 匹配前面的子表达式一/多次
# ? 匹配前面的子表达式零/一次
# {n,m} 花括号,匹配前面的字符至少n次,但不超过m次
# (xyz) 字符组,按照确切的匹配顺序匹配字符xyz
# | 分支结构,匹配符号之前或之后的字符
# \ 转义符,还原元字符原来的含义
# ^ 匹配行的开始
# $ 匹配行的结束
re.findall('.', 'abc')
re.findall('[ac]','abc')
re.findall('[^ac]','abc')
re.findall('[ab]{4}','aaaabbbb')
re.findall('aaa|bbb','aaaabbbb')
re.findall('a\\?|a\*','aa?a*a')
re.findall('a?.','abaacadaae')
# 03 简写字符集
# \w 匹配所有字母、数字、下划线:[a-zA-Z0-9_]
# \W 匹配所有非字母和数字的字符:[^\w]
# \d 匹配数字:[0-9]
# \D 匹配非数字:[^\d]
# \s 匹配空格符:[\t\n\f\r\p{Z}]
# \S 匹配非空格符:[^\s]
# \B 匹配一组非空字符开头或结尾的位置,不代表具体字符
re.findall('..s','Apple! This Is an Apple!') # ['his', ' Is']空格也算
re.findall('\w{2}','09 8? 7w c_ 9q p@')
re.findall('\w{2}','09 8? 7 w c_ 9q p@') # 匹配掉一个拿出去一个
re.findall('\w\W','09 8? 7w c_ 9q p@') #第一位是字母/数字/下划线,第二位是非字母和数字的字符 ['9 ', '8?', 'w ', '_ ', 'q ', 'p@']
re.findall('\w\W\B','09 8? 7w c_ 9q p@') # ['8?', 'p@']
re.findall('.\s.','Constant dropping wears the stone.')
re.findall('上海市(.{2,3}区)(.{2,3}路)(\d+号)','上海市黄浦区方兵中路249号 上海市宝山区密山路5号')
# 3 文本处理的五类操作
# 01 拆分
# str.split(正则表达式,从左到右的最大拆分次数,是否展开为多个列)
s = pd.Series(['上海市黄浦区方兵中路249号','上海市宝山区密山路5号'])
s.str.split('[市区路]') # [] 字符类,匹配方括号中包含的任意字符
s.str.split('[市区路]', n=2, expand=True)
# 02 合并
# str.join 表示用某个连接符把Series中的字符串列表连接起来,如果列表中出现非字符串元素则返回缺失值
s = pd.Series([['a','b'],[1,'a'],[['a','b'],'c']])
s.str.join('-')
# s1.str.cat(s2,连接符,缺失值代替符号,连接方式) 用于合并两个序列
s1 = pd.Series(['a','b'])
s2 = pd.Series(['cat','dog'])
s1.str.cat(s2, sep='-')
s2.index = [1,2]
s1.str.cat(s2, sep='-',na_rep='?', join='outer')
# 03 匹配
# str.contains 返回每个字符串是否包含正则模式的布尔序列
s = pd.Series(['my cat', 'he is cat', 'railway station'])
s.str.contains('\s\wat') #空格+字母/数字/下划线+at
# str.startswith/str.endswith 返回每个字符串是否以给定模式开始或结束的布尔序列,不支持正则表达式
s.str.startswith('my')
s.str.endswith('t')
# str.match 返回每个字符串起始处是否符合给定正则模式的布尔序列
s.str.match('m|h')
s.str[::-1].str.match('ta[f|g]|n') # s[::-1] s.str[::-1] 不一样
s.str.contains('^[m|h]') # ^ 匹配行的开始
s.str.contains('[f|g]at|n$') # $ 匹配行的结束
# str.find/str.rfind 返回从左到右/从右到左第一次匹配的位置的索引,未找到则返回-1,不支持正则匹配
s = pd.Series(['This is an apple. That is not an apple'])
s.str.find('apple')
s.str.rfind('apple')
# 04 替换
s = pd.Series(['a_1_b','c_?'])
s.str.replace('\d|\?','new') # \d 匹配数字:[0-9]
s = pd.Series(['上海市黄浦区方兵中路249号','上海市宝山区密山路5号','北京市昌平区北农路5号'])
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):
str_city = city[m.group(1)] # group(k)表示匹配到第k个子组
str_district = district[m.group(2)]
str_road = road[m.group(3)]
str_no = 'No.' + m.group(4)[:-1]
return ' '.join([str_city,str_district,str_road,str_no])
s.str.replace(pat,my_func)
pat = '(?P<市名>\w+ 市)(?P<区名>\w+ 区)(?P<路名>\w+ 路)(?P<编号>\w+ 号)'
city = {'上海市':'Shanghai','北京市':'Beijing'}
district = {'昌平区':'CP District','黄浦区':'HP District','宝山区':'BS District'}
road = {'方兵中路':'Mid Fangbin Road','密山路':'Mishan Road','北农路':'Beinong Road'}
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)
m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
m.group(0) # The entire match 整个匹配
'Isaac Newton'
m.group(1) # The first parenthesized subgroup. 第一个括号分隔的子群
'Isaac'
m.group(2) # The second parenthesized subgroup. 第二个括号分隔的子群
'Newton'
m.group(1, 2) # Multiple arguments give us a tuple. 多个参数给我们一个元组
('Isaac', 'Newton')
# match()方法用于从字符串的开始位置进行匹配,如果起始位置匹配成功,则返回Match对象,否则返回None
# [\w]+和\w+没有区别,都是匹配数字和字母下划线的多个字符;
# [\w+]表示匹配数字、字母、下划线和加号本身字符;
# 正则表达式中的两组括号把匹配结果分成两组,group()同group(0)就是匹配正则表达式整体结果,1group(1) 列出第一个括号匹配部分,group(2) 列出第二个括号匹配部分
# 05 提取
# str.extract 匹配一个结果,str.extractall 匹配全部结果以多级索引模式存储
pat = '(\w+市)(\w+区)(\w+路)(\w+号)'
s.str.extract(pat)
pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\w+号)'
s.str.extract(pat) # 重命名
# str.extractall 结果为多级索引,str.findall 结果为列表
s = pd.Series(['A123T45,A12S3','B456T78,B78T9'],index=['my_A','my_B'])
pat = '[A|B](\d+)[T|S](\d+)'
s.str.extractall(pat)
pat_withname = '[A|B](?P<AB>\d+)[T|S](?P<TS>\d+)'
s.str.extractall(pat_withname)
s.str.findall(pat)
# 4 常用字符串函数
# 01 字母型函数
s = pd.Series(['lower','CAPITALS','this is a sentence','SwApCaSe'])
s.str.upper()
s.str.lower()
s.str.title() # 单词开头大写
s.str.capitalize() # 整体第一个字母大写
s.str.swapcase() # 大小写转换
# 02 数值型函数
s = pd.Series(['1','2.2','2e','??','-2.1','0'])
pd.to_numeric(s,errors='ignore') # 对于不能转换为数值类型的,ignore保持原来的字符串
pd.to_numeric(s,errors='coerce') # coerce设为缺失
pd.to_numeric(s,errors='raise') # raise直接报错
s[pd.to_numeric(s,errors='coerce').isna()] # 查看非数值行
# 03 统计型函数
s = pd.Series(['cat rat fat at','get feed sheet heat'])
s.str.count('[r|f]at|ea') # 返回正则模式在每个元素中的出现次数
s.str.len() # 返回长度
# 04 格式型函数
# 除空型
l = pd.Index([' col1','col2 ', ' col3 '])
l.str.strip().str.len() # 除两侧空格
l.str.lstrip() # 除左侧空格
l.str.rstrip() # 除右侧空格
# 填充型
s = pd.Series(['a','b','c'])
s.str.pad(5,'left','*')
s.str.pad(5,'right','&')
s.str.pad(5,'both','#')
s.str.rjust(5,'*') # 保留右侧,填充左侧
s.str.ljust(5,'*')
s.str.center(5,'*')
s = pd.Series([7,423,303000]).astype('string')
s.str.pad(7,'left','0')
s.str.rjust(7,'0')
s.str.zfill(7)
# Ex1 房屋数据的文本提取
path = r'C:\Users\lenovo\Desktop\最近要用\pandas\joyful-pandas\data'
df = pd.read_excel('{}/house_info.xls'.format(path),usecols=['floor','year','area','price'])
df.head(3)
df.info()
df.isna().sum()
# 将year列改为整数年份存储。
df.dropna(subset='year',inplace=True)
df.year = round(df.year.str[0:4].astype('int64'))
df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
# 将floor列替换为Level、Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
df['Level'] = df.floor.str[0:2].astype('string')
df['Hightest'] = df.floor.str.extract('(\d+)').astype('int')
df = df[['Level','Hightest','area','price']]
pat = '(\w层)(共(\d+)层)'
new_cols = df.floor.str.extract(pat).rename(columns={0:'Level', 1:'Highest'})
df = pd.concat([df.drop(columns=['floor']), new_cols], axis=1)
# 计算每平米均价avg_price,以××元/平米的格式存储到表中,其中××为整数。
price = df.price.str.extract('(\d+)').astype('int64')
area = df.area.str.extract('(\d+.\d+|\d+)').astype('float')
df['avg_price'] = (price/area).astype('int').astype('string')+'元/平方米'
s_area = pd.to_numeric(df.area.str[:-1])
s_price = pd.to_numeric(df.price.str[:-1])
df['avg_price'] = ((s_price/s_area)*10000).astype('int').astype('string') + '元/平米'
df.head()
# A = 2.65, round(A) = 2 四舍五入
# A = 公式,round(A) = 2.0 会有一位小数,A.astype('int') = 2
# Ex2 《权力的游戏》据本数据集
df = pd.read_csv('{}/script.csv'.format(path))
df.head(3)
df.columns = df.columns.str.strip()
df.groupby(['Season','Episode'])['Sentence'].count()
df.set_index('Name').Sentence.str.split().str.len().groupby('Name').mean().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()