Pandas基础教程学习(七)

Pandas基础教程学习(七)

Pandas基础(七):文本数据的类型、拆分、拼接、替换、匹配、提取等方法

DataWhale第十四期组队学习:Joyful-Pandas

文章目录

  • Pandas基础教程学习(七)
    • 一、string类型的性质
      • 1.1 string和obiect的区别
      • 1.2 string类型的转换
    • 二、文本数据的拆分与拼接
      • 2.1 拆分方法 str.split
      • 2.2 拼接方法 str.cat
    • 三、文本数据的替换
      • 3.1 替换方法 str.replace
      • 3.2 子组与函数替换
      • 3.3 str.replace需要注意的地方(版本1.0.5)
    • 四、子串匹配与提取
      • 4.1 str.extract方法
      • 4.2 str.extractall方法
      • 4.3 str.contains和str.match
    • 五、常用字符串方法
      • 5.1 过滤型方法
      • 5.2 isnumeric方法
    • 六、问题与练习
      • 6.1 问题
        • 【问题一】 str对象方法和df/Series对象方法有什么区别?
        • 【问题二】 给出一列string类型,如何判断单元格是否是数值型数据?
        • 【问题三】 rsplit方法的作用是什么?它在什么场合下适用?
        • 【问题四】 在本章的第二到第四节分别介绍了字符串类型的5类操作,请思考它们各自应用于什么场景?
      • 6.2 练习
        • 【练习一】 现有一份关于字符串的数据集,请解决以下问题:
        • 【练习二】 现有一份半虚拟的数据集,第一列包含了新型冠状病毒的一些新闻标题,请解决以下问题:

Pandas文本数据的内容包括

  • 文本数据的两种类型,Object与String
  • 文本数据如何进行拆分与拼接
  • 文本数据如何进行替换操作
  • 文本数据的子串的匹配与提取
  • 文本数据常用的一些其他方法

一、string类型的性质

1.1 string和obiect的区别

在Pandas中,表示文本数据一般有object和string两种,在第六章缺失数据中提到过,string为Nullable类型,统一多种缺失值的相关操作。

string和object的不同之处主要有三点

  • string会返回相应数据的Nullable类型,而object会随缺失值的存在而改变返回类型
  • 某些Series方法不能在string上使用,例如: Series.str.decode(),因为存储的是字符串而不是字节
  • string类型在缺失值存储或运算时,类型会广播为pd.NA,而不是浮点型np.nan

迎合Pandas的发展模式,建议全部用string来操作字符串

1.2 string类型的转换

若是将其他类型直接转换为string类型会出错

#pd.Series([1,'1.']).astype('string') #报错
#pd.Series([1,2]).astype('string') #报错
#pd.Series([True,False]).astype('string') #报错

正确的方法如下所示,先转为str型object,再转为string类型

pd.Series([1,'1.']).astype('str').astype('string')
pd.Series([1,2]).astype('str').astype('string')
pd.Series([True,False]).astype('str').astype('string')

二、文本数据的拆分与拼接

2.1 拆分方法 str.split

# 按照指定符号进行分割
# 分割完之后类型会变为object
# 元素由a_b_c变为[a,b,c],即变为列表,缺失值不变,为
s = pd.Series(['a_b_c', 'c_d_e', np.nan, 'f_g_h'], dtype="string")
s.str.split('_')
'''
0    [a, b, c]
1    [c, d, e]
2         
3    [f, g, h]
dtype: object
'''

# 由于新的Series是列表,所以可以进行元素选择
s.str.split('_').str[1]
'''
0       b
1       d
2    
3       g
dtype: object
'''
# 特殊情况,非单一元素
#第一个元素先转为['a','_','b','_','c']
pd.Series(['a_b_c', ['a','b','c']], dtype="object").str[1]
'''
0    _
1    b
dtype: object
'''
# str.split的相关参数
# expand参数控制了是否将列拆开
s.str.split('_',expand=True) # 拆开后组成的是Dataframe
'''
	0	1	2
0	a	b	c
1	c	d	e
2   
3	f	g	h
'''

# n参数代表最多分割多少次
s.str.split('_',n=1)
'''
0    [a, b_c]
1    [c, d_e]
2        
3    [f, g_h]
dtype: object
'''

# 也可以组合起来用
s.str.split('_',expand=True,n=1)

2.2 拼接方法 str.cat

单列拼接

s = pd.Series(['ab',None,'d'],dtype='string')
s.str.cat()   # abd
# 可选参数
# 可以选择分隔符参数,和缺失值替代字符na_rep参数
s.str.cat(sep=',')  # ab,d
s.str.cat(sep=',',na_rep='*')  # ab,*,d

双列拼接

对于两个Series合并而言,是对应索引的元素进行合并

s2 = pd.Series(['24',None,None],dtype='string')
s.str.cat(s2)
'''
0    ab24
1    
2    
dtype: string
'''
# 可选参数
# 需要注意的是两个缺失值会被同时替换
s.str.cat(s2,sep=',',na_rep='*')
'''
0    ab,24
1      *,*
2      d,*
dtype: string
'''

多列拼接

多列拼接可以分为表的拼接和多Series的拼接

# 表的拼接
s.str.cat(pd.DataFrame({
     0:['1','3','5'],
                        1:['5','b',None]},
                       dtype='string'),
          na_rep='*')
'''
0    ab15
1     *3b
2     d5*
dtype: string
'''
# 多个Series拼接
s.str.cat([s+'0',s*2])
'''
0    abab0abab
1         
2        dd0dd
dtype: string
'''

索引对齐

s2 = pd.Series(list('abc'),index=[1,2,3],dtype='string')
s.str.cat(s2,na_rep='*')
'''
0    ab*
1     *a
2     db
dtype: string
'''

三、文本数据的替换

3.1 替换方法 str.replace

s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca','', 
               np.nan, 'CABA', 'dog', 'cat'],dtype="string")
s.str.replace(r'^[AB]','***') # 用***替换A或B开头的字符串中A或B的内容

3.2 子组与函数替换

通过正整数调用子组(0返回字符本身,1代表第一个括号内子组,2同理)

s.str.replace(r'([ABC])(\w+)',lambda x:x.group(2)[0:]+'*')

# 也可以利用?P<…>表达式给子组命名调用
s.str.replace(r'(?P[ABC])(?P\w+)',
              lambda x:x.group('two')[1:]+'*')

3.3 str.replace需要注意的地方(版本1.0.5)

首先,要明确str.replace和replace并不是一个东西

str.replace针对的是object类型或string类型,默认是以正则表达式为操作,目前暂时不支持DataFrame上使用

replace针对的是任意类型的序列或数据框,如果要以正则表达式替换,需要设置regex=True,该方法通过字典可支持多列替换

但现在由于string类型的初步引入,用法上出现了一些问题,这些issue有望在以后的版本中修复

str.replace赋值参数不得为pd.NA

# pd.Series(['A','B'],dtype='string').str.replace(r'[A]',pd.NA) #报错
# pd.Series(['A','B'],dtype='O').str.replace(r'[A]',pd.NA) #报错
# 这时候就需要先转个obiect再转回来
pd.Series(['A','B'],dtype='string').astype('O').replace(r'[A]',pd.NA,regex=True).astype('string')

对于string类型Series,在使用replace函数时不能使用正则表达式替换(bug待修复)

string类型序列如果存在缺失值,不能使用replace替换

#pd.Series(['A',np.nan],dtype='string').replace('A','B') #报错
pd.Series(['A',np.nan],dtype='string').str.replace('A','B')
'''
0       B
1    
dtype: string
'''

四、子串匹配与提取

4.1 str.extract方法

pd.Series(['10-87', '10-88', '-89'],dtype="string").str.extract(r'(?P[\d]{2})?-(?P[\d]{2})')pd.Series(['10-87', '10-88', '10-89'],dtype="string").str.extract(r'([\d]{2})-([\d]{2})')
'''
	0	1
0	10	87
1	10	88
2	10	89
'''

# 也可以设定组的名字
pd.Series(['10-87', '10-88', '-89'],dtype="string").str.extract(r'(?P[\d]{2})-(?P[\d]{2})')
'''
	name_1	name_2
0	10	87
1	10	88
2		
'''

# 利用?正则标记选择部分提取
pd.Series(['10-87', '10-88', '-89'],dtype="string").str.extract(r'(?P[\d]{2})?-(?P[\d]{2})')

expand参数(默认为True)

对于一个子组的Series,如果expand设置为False,则返回Series,若大于一个子组,则expand参数无效,全部返回DataFrame

s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="string")
s.str.extract(r'([\w])') # 返回DataFrame
s.str.extract(r'([\w])',expand=False) # 返回Series
s.index.str.extract(r'([\w])([\d])')  # 返回DataFrame
# s.index.str.extract(r'([\w])([\d])',expand=False) #报错

4.2 str.extractall方法

与extract只匹配第一个符合条件的表达式不同,extractall会找出所有符合条件的字符串,并建立多级索引(即使只找到一个)

s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"],dtype="string")
two_groups = '(?P[a-z])(?P[0-9])'
s.str.extractall(two_groups)
letter digit
match
A 0 a 1
1 a 2
B 0 b 1
C 0 c 1

如果想查看第i层匹配,可使用xs方法

s = pd.Series(["a1a2", "b1b2", "c1c2"], 
              index=["A", "B", "C"],dtype="string")
s.str.extractall(two_groups).xs(1,level='match')
'''
	letter	digit
A	a		2
B	b		2
C	c		2
'''

4.3 str.contains和str.match

# str.contains用于检测是否包含某种正则模式
pd.Series(['1', None, '3a', '3b', '03c'], 
          dtype="string").str.contains(r'[0-9][a-z]')


# 可选参数为na,把None从判定为False
pd.Series(['1', None, '3a', '3b', '03c'], 
          dtype="string").str.contains('a', na=False)
# str.match与其区别在于,match依赖于python的re.match
# 检测内容为是否从头开始包含该正则模式
# 3、4为True
pd.Series(['1', None, '3a_', '3b', '03c'], 
          dtype="string").str.match(r'[0-9][a-z]',na=False)
# 4为True
pd.Series(['1', None, '_3a', '3b', '03c'], 
          dtype="string").str.match(r'[0-9][a-z]',na=False)

五、常用字符串方法

5.1 过滤型方法

# str.strip 去除两侧空格
pd.Series(list('abc'),index=[' space1  ','space2  ','  space3'],
          dtype="string").index.str.strip()
# str.;ower和str.upper大小写控制
pd.Series('A',dtype="string").str.lower()
pd.Series('a',dtype="string").str.upper()
# str.swapcase和str.capitalize
# 交换字母大小写
pd.Series('abCD',dtype="string").str.swapcase()
# 大写首字母
pd.Series('abCD',dtype="string").str.capitalize()

5.2 isnumeric方法

这里的数字应该指的是纯数字

# 检查每一位是否都是数字
pd.Series(['1.2','1','-0.3','a',np.nan],dtype="string").str.isnumeric()
'''
0    False
1     True
2    False
3    False
4     
dtype: boolean
'''

六、问题与练习

6.1 问题

【问题一】 str对象方法和df/Series对象方法有什么区别?

我的理解是,就像是一些语言中需要单独设置string类一样,可能是为了在做一些字符串操作的时候可以更加方便,比如查找字符串中是否包含特定字符串,或者判断字符串长度是否为多少小于多少等等。

使用内置的str对象方法可能在做一些字符处理的时候效率更高也更有针对性,而对象方法的对象应该是Series和DataFrame,而不是每一列中的string

【问题二】 给出一列string类型,如何判断单元格是否是数值型数据?

我想的是利用正则表达式来进行判断,但是自己觉得不是合理解答,健壮性不强

str.contains(r'([+-]?)(\d+)(\.)?(\d+)?')

【问题三】 rsplit方法的作用是什么?它在什么场合下适用?

同样是对字符串进行分割,但是返回的是一个列表,且从字符串末尾进行分割

  • 参数sep,设置分隔符
  • 参数count,设置分隔的次数

可以删除什么数据的特定尾缀?比如保留邮箱名字,那就以@为分隔符,取列表0索引

【问题四】 在本章的第二到第四节分别介绍了字符串类型的5类操作,请思考它们各自应用于什么场景?

拆分方法:应该比较常用,用于提取数据中有用的部分,或者是按照一定的格式输入的数据进行有效提取

拼接方法:输出的时候用得到?把相关信息拼接在一起构成一句话然后做成DataFrame表

替换方法:那应该就是需要替换数据的时候使用了

匹配方法:应该是查看数据中是否有满足所需正则模式的数据吧

提取方法:从教程中来看不知道是不是比较适合一些数字类的数据,用于分割这类数据,不太了解

6.2 练习

【练习一】 现有一份关于字符串的数据集,请解决以下问题:

pd.read_csv('data/String_data_one.csv',index_col='人员编号').head()
姓名 国籍 性别 出生年 出生月 出生日
人员编号
1 aesfd 2 1942 8 10
2 fasefa 5 1985 10 4
3 aeagd 4 1946 10 15
4 aef 4 1999 5 13
5 eaf 1 2010 6 24

(a)现对字符串编码存储人员信息(在编号后添加ID列),使用如下格式:“×××(名字):×国人,性别×,生于×年×月×日”

先改个数据类型,再就可以正常拼接在一起了

q1 = pd.read_csv('data/String_data_one.csv',index_col='人员编号').astype('str').astype('string')
(q1['姓名'] + ':' + q1['国籍'] + '国人,性别' + 
        q1['性别'] + ',生于' + q1['出生年'] + '年'
        + q1['出生月'] + '月' + q1['出生日']+'日').to_frame().rename(columns={
     0:'ID'}).head()

(b)将(a)中的人员生日信息部分修改为用中文表示(如一九七四年十月二十三日),其余返回格式不变

L_year = list('零一二三四五六七八九')
L_one = [s.strip() for s in list('  二三四五六七八九')]
L_two = [s.strip() for s in list(' 一二三四五六七八九')]
q1_new = (q1['姓名']+':'+q1['国籍']+'国人,性别'+q1['性别']+',生于'
          +q1['出生年'].str.replace(r'\d',lambda x:L_year[int(x.group(0))])+'年'
          +q1['出生月'].apply(lambda x:x if len(x)==2 else '0'+x)\
                      .str.replace(r'(?P[\d])(?P\d?)',lambda x:L_one[int(x.group('one'))]
                      +bool(int(x.group('one')))*'十'+L_two[int(x.group('two'))])+'月'
          +q1['出生日'].apply(lambda x:x if len(x)==2 else '0'+x)\
                      .str.replace(r'(?P[\d])(?P\d?)',lambda x:L_one[int(x.group('one'))]
                      +bool(int(x.group('one')))*'十'+L_two[int(x.group('two'))])+'日')\
          .to_frame().rename(columns={
     0:'ID'})
q1_new.head()

(c)将(b)中的ID列结果拆分为原列表相应的5列,并使用equals检验是否一致

dic_year = {
     i[0]:i[1] for i in zip(list('零一二三四五六七八九'),list('0123456789'))}
dic_two = {
     i[0]:i[1] for i in zip(list('十一二三四五六七八九'),list('0123456789'))}
dic_one = {
     '十':'1','二十':'2','三十':'3',None:''}
q1_res = q1_new['ID'].str.extract(r'(?P<姓名>[a-zA-Z]+):(?P<国籍>[\d])国人,性别(?P<性别>[\w]),生于(?P<出生年>[\w]{4})年(?P<出生月>[\w]+)月(?P<出生日>[\w]+)日')
q1_res['出生年'] = q1_res['出生年'].str.replace(r'(\w)+',lambda x:''.join([dic_year[x.group(0)[i]] for i in range(4)]))
q1_res['出生月'] = q1_res['出生月'].str.replace(r'(?P\w?十)?(?P[\w])',lambda x:dic_one[x.group('one')]+dic_two[x.group('two')]).str.replace(r'0','10')
q1_res['出生日'] = q1_res['出生日'].str.replace(r'(?P\w?十)?(?P[\w])',lambda x:dic_one[x.group('one')]+dic_two[x.group('two')]).str.replace(r'^0','10')
q1_res.head()

【练习二】 现有一份半虚拟的数据集,第一列包含了新型冠状病毒的一些新闻标题,请解决以下问题:

pd.read_csv('data/String_data_two.csv').head()
col1 col2 col3
0 鄂尔多斯市第2例确诊患者治愈出院 19 363.6923
1 云南新增2例,累计124例 -67 -152.281
2 武汉协和医院14名感染医护出院 -86 325.6221
3 山东新增9例,累计307例 -74 -204.9313
4 上海开学日期延至3月 -95 4.05

(a)选出所有关于北京市和上海市新闻标题的所在行

q2[q2['col1'].str.contains(r'[北京]{2}|[上海]{2}')].head()

(b)求col2的均值

q2.loc[[309,396,485],'col2'] = [0,9,7]
q2['col2'].astype('int').mean()

(c)求col3的均值

q2.loc[[28,122,332],'col3  '] = [355.3567, 9056.2253, 3534.6554]
q2['col3  '].astype('float').mean()

你可能感兴趣的:(Pandas,python,正则表达式,数据分析)