7.1 string类型的性质
-
string与object的区别
① 字符存取方法(如str.count)会返回相应数据的Nullable类型,而object会随缺失值的存在而改变返回类型
② 因为存储的是字符串而不是字节,某些Series方法不能在string上使用,例如: Series.str.decode()
③ string类型在缺失值存储或运算时,类型会广播为pd.NA,而不是浮点型np.nan -
string类型的转换
如果将一个其他类型的容器直接转换string类型可能会出错,当下正确的方法是分两部转换,先转为str型object,在转为string类型。pd.Series([1,'Hello']).astype('str').astype('string')
-
元素的选择
如果该单元格元素是列表,那么str[i]表示取出第i个元素;如果是单个元素,则先把元素转为列表在取出s.str.split('_').str[1] #object类型,直接取 pd.Series(['a_b_c', ['a', 'b', 'c']],dtype=object).str[1] #先把第一个元素转换成列表再取
7.2 拆分与拼接
-
str.split() 拆分 从左往右拆分
s.str.split('_', expand=True, n=2)
第一个参数:根据某一个元素分割,默认为空格
expand参数:控制了是否将列拆开
n参数:代表最多分割多少次
[注]:注意split后的类型是object,因为现在Series中的元素已经不是string,而包含了list,string类型只能含有字符串。 -
str.rsplit() 从右往左拆分
-
str.cat() 拼接
s.str.cat(s2, sep=',', na_rep='*',join=‘left’)
第一个参数:需要合并的列
sep参数:连接中间的分隔符
na_rep参数:缺失值替代字符
join参数:索引对齐,默认为左连接① 单列拼接,就是指所有的元素进行字符合并为一个字符串
输入:s = pd.Series(['ab',None,'d'],dtype='string') s.str.cat() 输出:'abc'
② 多列拼接,可以分为表的拼接和多Series拼接
表的拼接s.str.cat(pd.DataFrame({0:['1','3','5'],1:['5','b',None]},dtype='string'),na_rep='*')
多列拼接
s.str.cat([s+'0',s*2])
7.3 替换
-
基础知识:正则表达式.
-
str.replace() 针对的是object类型或string类型,默认是以正则表达式为操作,目前暂时不支持DataFrame上使用
① 对于string类型Series,如果要替换的类型为非缺失值,直接替换pd.Series(['a', 'b']).str.replace('a', 'c') #把a替换为c
② 如果要替换的类型为缺失值,先转为object再转回来
pd.Series(['A', 'B'],dtype='string').astype('object').replace('A', pd.NA).astype('string')
[注]:对于string类型Series在使用replace函数时不能使用正则表达式替换
-
replace() 针对的是任意类型的序列或数据框
df.replace({5:6, 6:7}, inplace=True, regex=False)
第一个参数:替换
inplace参数:是否修改原数据表
regex参数:以正则表达式的方式替换① 多对一替换
df = pd.DataFrame({'col1': list('abcde'), 'col2': range(5,10), 'col3': [1.3,2.5,3.6,4.6,5.8]}, index=list('一二三四五')) df.replace(['a', 'b'], 'c')
② 多对多替换
输入:df.replace({5:6, 6:7})
7.4 子串匹配与提取
-
str.extract() 只提取第一个符合条件的字符串
输入:s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"],dtype="string") two_groups = '(?P<letter>[a-z])(?P<digit>[0-9])' s.str.extract(two_groups, expand=True) 输出:letter digit A a 1 B b 1 C c 1
expand参数:默认为Ture。对于一个子组的Series,如果expand设置为False,则返回Series,若大于一个子组,则expand参数无效,全部返回DataFrame;对于一个子组的Index,如果expand设置为False,则返回提取后的Index,若大于一个子组且expand为False,报错
-
str.extractall() 会找出所有符合条件的字符串,并建立多级索引(即使只找到一个)
输入:s.str.extractall(two_groups,expand=True) 输出: letter digit match A 0 a 1 1 a 2 B 0 b 1 C 0 c 1
如果想查看第i层匹配,可使用xs方法
输入:s.str.extractall(two_groups).xs(0, level='match') 输出: letter digit A a 1 B b 1 C c 1
-
str.contains() 检测是否包含某种正则模式
输入:pd.Series(['1', None, '3a', '3b', '03c'], dtype="string").str.contains('a', na=False) 输出: 0 False 1 False #na=True返回<NA> 2 True 3 False 4 False dtype: boolean
第一个参数:要匹配的字符串
na参数:设置缺失值匹配结果为False -
str,match() match依赖于python的re.match,检测内容为是否从头开始包含该正则模式
输入:pd.Series(['1', None, '_3a', '3b', '03c'], dtype="string").str.match(r'[0-9][a-z]',na=False) 输出: 0 False 1 False 2 False #若是‘3a_’返回True 3 True 4 False dtype: boolean
7.5 常用字符串方法
-
str.strip() 常用于过滤空格
pd.Series(list('abc'),index=[' space1 ','space2 ',' space3'],dtype="string").index.str.strip()
-
str.lower()和str.upper() 转换大小写
输入:pd.Series('A',dtype="string").str.lower() 输出: 0 a dtype: string
输入:pd.Series('a',dtype="string").str.upper() 输出: 0 A dtype: string
-
str.swapcase() 交换大小写
输入:d.Series('abCD',dtype="string").str.swapcase() 输出: 0 ABcd dtype: string
-
str.capitalize() 大写首字母
输入:pd.Series('abCD',dtype="string").str.capitalize() 输出: 0 Abcd dtype: string
-
str.isnumeric() 检查每一位是否都是数字
输入:pd.Series(['1.2','1','-0.3','a',np.nan],dtype="string").str.isnumeric() 输出: 0 False 1 True 2 False 3 False 4 <NA> dtype: boolean
问题与练习
1(a)
df = pd.read_csv(r'D:\python\python3.6\pysl\Pre_\data\String_data_one.csv', index_col='人员编号').astype('str')
temp = df['姓名'] + ":" + df['国籍'] + "国人,性别"\
+ df['性别'] + "生于" + df['出生年'] + "年" \
+ df['出生月'] + "月" + df['出生日'] + "日"
temp.to_frame().rename(columns={0:'ID'})
#1(b)
L_year = list('零一二三四五六七八九')
L_one = [s.strip() for s in list(' 二三四五六七八九')]
L_two = [s.strip() for s in list(' 一二三四五六七八九')]
df_new = (df['姓名']+':'+df['国籍']+'国人,性别'+df['性别']+',生于'
+df['出生年'].str.replace(r'\d',lambda x:L_year[int(x.group(0))])+'年'
+df['出生月'].apply(lambda x:x if len(x)==2 else '0'+x)\
.str.replace(r'(?P<one>[\d])(?P<two>\d?)',lambda x:L_one[int(x.group('one'))]
+bool(int(x.group('one')))*'十'+L_two[int(x.group('two'))])+'月'
+df['出生日'].apply(lambda x:x if len(x)==2 else '0'+x)\
.str.replace(r'(?P<one>[\d])(?P<two>\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'})
1(c)
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:''}
df_res = df_new['ID'].str.extract(r'(?P<姓名>[a-zA-Z]+):(?P<国籍>[\d])国人,性别(?P<性别>[\w]),生于(?P<出生年>[\w]{4})年(?P<出生月>[\w]+)月(?P<出生日>[\w]+)日')
df_res['出生年'] = df_res['出生年'].str.replace(r'(\w)+',lambda x:''.join([dic_year[x.group(0)[i]] for i in range(4)]))
df_res['出生月'] = df_res['出生月'].str.replace(r'(?P<one>\w?十)?(?P<two>[\w])',lambda x:dic_one[x.group('one')]+dic_two[x.group('two')]).str.replace(r'0','10')
df_res['出生日'] = df_res['出生日'].str.replace(r'(?P<one>\w?十)?(?P<two>[\w])',lambda x:dic_one[x.group('one')]+dic_two[x.group('two')]).str.replace(r'^0','10')
df_res.equals(df)
2(a)
df1 = pd.read_csv(r'D:\python\python3.6\pysl\Pre_\data\String_data_two.csv')
df1[df1['col1'].str.contains(r'[北京]{2}|[上海]{2}')]
2(b)
df['col2'][~(df['col2'].str.replace(r'-?\d+','True')=='True')] #这三行有问题
df.loc[[309,396,485],'col2'] = [0,9,7]
df['col2'].astype('int').mean()
2(c)
df.columns = df.columns.str.strip()
df['col3'].head()
df['col3'][~(df['col3'].str.replace(r'-?\d+\.?\d+','True')=='True')]
df.loc[[28,122,332],'col3'] = [355.3567,9056.2253, 3534.6554]
df['col3'].astype('float').mean()