这篇随笔主要介绍如何利用 Pandas 对数据进行清洗 Pt.1 部分主要介绍利用 Pandas 处理缺失数据 Pt.2 部分详细介绍利用 Pandas 处理重复数据、替换数据和划分数据 Pt.3 部分主要介绍利用 Pandas 与 正则表达式的结合
1 2 import pandas as pdimport numpy as np
随机采样 np.random.permutation( x ) 1 2 3 df = pd.DataFrame(np.arange(5 * 4 ).reshape((5 , 4 ))) sampler = np.random.permutation(5 ) df.iloc[sampler]
0 1 2 3 4 16 17 18 19 3 12 13 14 15 2 8 9 10 11 0 0 1 2 3 1 4 5 6 7
obj.sample( n ) 1 2 df = pd.DataFrame(np.arange(5 * 4 ).reshape((5 , 4 ))) df.sample(n=5 )
0 1 2 3 3 12 13 14 15 2 8 9 10 11 0 0 1 2 3 1 4 5 6 7 4 16 17 18 19
将 分类变量 转换为 向量变量 pd.get_dummies( series, prefix ) 1 2 3 df = pd.DataFrame({'key' : ['b' , 'b' , 'a' , 'c' , 'a' , 'b' ], 'data' : range (6 )}) df
key data 0 b 0 1 b 1 2 a 2 3 c 3 4 a 4 5 b 5
1 pd.get_dummies(df['key' ])
a b c 0 0 1 0 1 0 1 0 2 1 0 0 3 0 0 1 4 1 0 0 5 0 1 0
prefix : 给指标 DataFrame 的列加上一个前缀1 pd.get_dummies(df['key' ], prefix='key' )
key_a key_b key_c 0 0 1 0 1 0 1 0 2 1 0 0 3 0 0 1 4 1 0 0 5 0 1 0
1 pd.get_dummies(df['key' ], prefix='key' ).join(df['data' ])
key_a key_b key_c data 0 0 1 0 0 1 0 1 0 1 2 1 0 0 2 3 0 0 1 3 4 1 0 0 4 5 0 1 0 5
字符串的处理方法 str.split( sep, maxsplit ) : 根据 sep 拆分字符串, str → list1 2 val = ' a ,b, guido ' val.split(',' )
str.strip( ) : 去除字符串两边空白符(包括换行符)str.lstrip( ), str.rstrip( ) : 去除字符串 左或右 的空白符(包括换行符)
1 [x.strip() for x in val.split(',' )]
sep.join( list ) : 去除字符串首尾空白符(包括换行符)1 '::' .join( val.split(',' ) )
str.index( sep ), str.find( sep ) : 返回 sep 在 str 中第一次出现的位置区别 : 如果 sep 在 str 中不存在, sep.find 返回 -1 , sep.index 会引发异常
str.rfind( sep ) : 返回 sep 在 str 中最后一次出现的位置str.count( sep ) : 返回 sep 在 str 中出现的次数str.replace( old, new ) : 替换str.endswith( sep ), str.startswith( sep ) : 判断 str 是否以 sep 结尾或开始1 val.strip().endswith('a' )
1 val.strip().startswith('a' )
str.lower( ), str.upper( ), str.title( ) : 控制大小写1 val.title(), val.upper(), val.lower()
1 (' A ,B, Guido ', ' A ,B, GUIDO ', ' a ,b, guido ')
正则表达式 re.split( pattern, str ) : 根据 sep 拆分字符串( str 中的分隔符 sep 数量不定 )1 2 text = "foo bar\t baz \tqux" re.split('\s+' , text)
1 ['foo', 'bar', 'baz', 'qux']
re.compile( pattern ) : 根据 pattern 返回一个正则表达式类( regex )的对象1 2 regex = re.compile ('\s+' ) regex.split(text)
1 ['foo', 'bar', 'baz', 'qux']
regex.split( str ) : 根据 regex 拆分字符串
re.findall( pattern, str ), regex.findall( str ) : 返回字符串中的正则表达式匹配项1 2 3 4 5 6 7 8 9 text = """WANG wangbj27@mail2.sysu.edu.cn Dave dave@google.com Steve steve@gmail.com Rob rob@gmail.com Ryan ryan@yahoo.com""" pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}' regex = re.compile (pattern, flags=re.IGNORECASE) regex.findall(text)
1 2 3 4 5 ['wangbj27@mail2.sysu.edu.cn', 'dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']
re.finditer( pattern, str ), regex.finditer( str ) : 以迭代器的形式返回字符串中的正则表达式匹配项1 2 for x in regex.finditer(text): print (x.group())
1 2 3 4 5 wangbj27@mail2.sysu.edu.cn dave@google.com steve@gmail.com rob@gmail.com ryan@yahoo.com
re.sub( pattern, repl, str ), regex.sub( repl, str ) : 替换字符串中的正则表达式匹配项1 print (regex.sub(repl='E-mail' , string=text))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 WANG E-mail Dave E-mail Steve E-mail Rob E-mail Ryan E-mail ··· # # ```python pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})' regex = re.compile(pattern, flags=re.IGNORECASE) regex.findall(text)
1 2 3 4 5 [('wangbj27', 'mail2.sysu.edu', 'cn'), ('dave', 'google', 'com'), ('steve', 'gmail', 'com'), ('rob', 'gmail', 'com'), ('ryan', 'yahoo', 'com')]
sub 还能通过 \1 、\2 之类的特殊符号访问各匹配项中的分组, 符号 \1 对应第一个匹配的组 1 print (regex.sub(r'Username: \1, Domain: \2, Suffix: \3' , text))
1 2 3 4 5 WANG Username: wangbj27, Domain: mail2.sysu.edu, Suffix: cn Dave Username: dave, Domain: google, Suffix: com Steve Username: steve, Domain: gmail, Suffix: com Rob Username: rob, Domain: gmail, Suffix: com Ryan Username: ryan, Domain: yahoo, Suffix: com
pandas 的矢量化字符串方法 obj.str.func( ... ) 将字符串的方法应用于 series 的各个单元里去
series.str.contains( pattern ) : 检查各行是否含有字符串 string1 2 3 4 data = {'Dave' : 'dave@google.com' , 'Steve' : 'steve@gmail.com' , 'Rob' : 'rob@gmail.com' , 'Wes' : np.nan} data = pd.Series(data) data
1 2 3 4 5 Dave dave@google.com Steve steve@gmail.com Rob rob@gmail.com Wes NaN dtype: object
1 data.str .contains('gmail' )
1 2 3 4 5 Dave False Steve True Rob True Wes NaN dtype: object
series.str.findall( pattern, flags ) 1 2 pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})' data.str .findall(pattern, flags=re.IGNORECASE)
1 2 3 4 5 Dave [(dave, google, com)] Steve [(steve, gmail, com)] Rob [(rob, gmail, com)] Wes NaN dtype: object
series.str.match( pattern, flags ) 1 2 pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})' data.str .match (pattern, flags=re.IGNORECASE)
1 2 3 4 5 Dave True Steve True Rob True Wes NaN dtype: object
series.str.get( i ), series.str.slice( start, stop ), series.str[ start : stop ] : 切片1 2 3 4 5 Dave d Steve s Rob r Wes NaN dtype: object
1 2 3 4 5 Dave dave@ Steve steve Rob rob@g Wes NaN dtype: object
series.str.len( ) 1 2 3 4 5 Dave 15.0 Steve 15.0 Rob 13.0 Wes NaN dtype: float64
series1.str.cat( series2, sep ) : 根据索引实现元素级字符串连接1 2 name = pd.Series({'Dave' :'Dave' , 'Steve' :'Steve' , 'Rob' :'Rob' , 'Wes' :'Wes' }) name.str .cat(data, '----' )
1 2 3 4 5 Dave Dave----dave@google.com Steve Steve----steve@gmail.com Rob Rob----rob@gmail.com Wes NaN dtype: object
series.str.len( ) series.str.lower( ), series.str.upper( ), series.str.title( ) series.str.strip( ), series.str.lstrip( ), series.str.rstrip( ) : 去除两边/左/右的空格
series.str.endswith( sep ), series.str.startswith( sep ) series.str.find( sep ), series.str.rfind( sep ) : 返回 sep 在字符串中的位置series.str.count( sep ) : 计数 sep 在字符串中出现的次数series.str.split( sep ) : 根据分隔符 sep 对字符串进行划分, str→listseries.str.join( sep ) : 利用分隔符 sep 将 list 连接起来, liat→strseries.str.replace( old, new ) : 替换
series1.str.cat( series2, sep ) : 根据索引实现元素级字符串连接
... ...