最近在知乎上看到这样一个问题:题主说pandas用起来很乱。真的是这样吗?本文将先介绍如何使用pandas进行数据转换/编码的十种方案,最后再回答这个问题。事实上,这种操作在机器学习中很常见。很多算法需要我们对分类特征进行转换(编码),即根据某列的值添加(修改)一列。为了方便理解,下面创建一个示例DataFrame:数值型数据先来讨论一下连续数据的转换,即根据Score列的值添加一列新的labels,也就是如果score更大90分以上记为A,80-90分记为B,以此类推。自定义函数+循环遍历首先,当然是最简单也是最笨的方法。自己写一个函数,用循环遍历。它必须是def加上for:df1=df.copy()defmyfun(x):ifx>90:return'A'elifx>=80andx<90:return'B'elifx>=70andx<80:return'C'elifx>=60andx<70:return'D'else:return'E'df1['Score_Label']=Noneforiinrange(len(df1)):df1.iloc[i,3]=myfun(df1.iloc[i,2])这段代码,相信大家我都能看懂,想起来容易但是比较麻烦。有更容易的方法吗?当然pandas提供了很多高效的操作功能,继续往下看。Customfunction+map现在,你可以使用map来杀死循环(虽然它本质上也是一个循环):df2=df.copy()defmapfun(x):ifx>90:return'A'elifx>=80andx<90:return'B'elifx>=70andx<80:return'C'elifx>=60andx<70:return'D'else:return'E'df2['Score_Label']=df2['Score'].map(mapfun)结果是一样的:customfunction+apply如果还是想简化代码,可以用customfunction+applykill掉自定义函数:df3=df.copy()df3['Score_Label']=df3['Score'].apply(lambdax:'A'ifx>90else('B'if90>x>=80else('C'if80>x>=70else('D'if70>x>=60else'E'))))结果和上面一致,但是容易被打出来。使用pd.cut下面继续了解pandas更高级的功能,依旧是对Score进行编码,使用pd.cut,指定划分的区间,可以直接给你分组:df4=df.copy()bins=[0,59,70,80,100]df4['Score_Label']=pd.cut(df4['Score'],bins)也可以直接用labels参数修改对应组的名字,是不是现在更方便了:df4['Score_Label_new']=pd.cut(df4['Score'],bins,labels=['low','middle','good','perfect'])使用sklearn二值化,因为它是和关于机器学习,sklearn肯定是跑不掉的。如果需要添加新列并判断成绩是否及格,可以使用Binarizer函数。代码也简单易懂:df5=df.copy()binerize=Binarizer(threshold=60)trans=binerize.fit_transform(np.array(df1['Score']).reshape(-1,1))df5['Score_Label']=transtextdata下面介绍比较常用的文本数据转换和标注方法。比如新增一列,将gendermale和female分别标记为0和1,使用replace先引入replace,但需要注意的是,上面提到的自定义函数相关的方法还是可行的:df6=df.copy()df6['Sex_Label']=df6['Sex'].replace(['Male','Female'],[0,1])上面是性别操作,因为只有男女,所以可以手动指定0、1,但是如果类别比较多,也可以使用pd.value_counts()自动指定标签,比如分组CourseName列:df6=df.copy()value=df6['CourseName'].value_counts()value_map=dict((v,i)fori,vinenumerate(value.index))df6['CourseName_Label']=df6.replace({'CourseName':value_map})['CourseName']用map来强调new要增加一列,你一定能想到mapdf7=df.copy()Map={elem:indexforindex,eleminenumerate(set(df["CourseName"]))}df7['CourseName_Label']=df7['CourseName'].map(Map)使用astype方法d,很多人不知道。这属于上面提到的知乎问题。实现方法太多了:df8=df.copy()value=df8['课程名称'].astype('category')df8['课程名称_标签']=value.cat.codes使用sklearn是一样的asnumeric类型,这个机器学习中的经典操作,sklearn一定有办法,使用LabelEncoder进行分类数据编码:fromsklearn.preprocessingimportLabelEncoderdf9=df.copy()le=LabelEncoder()le.fit(df9['Sex'])df9['Sex_Label']=le.transform(df9['Sex'])le.fit(df9['课程名称'])df9['课程名称_标签']=le.transform(df9['课程名称'])也可以一次转换两列:df9=df.copy()le=OrdinalEncoder()le.fit(df9[['Sex','CourseName']])df9[['Sex_Label','CourseName_Label']]=le.transform(df9[['Sex','CourseName']])使用factorize最后,再介绍一个小众但好用的pandas方法。我们需要注意的是,在上面的方法中,自动生成的CourseName_Label列,虽然一个数据对应一种语言,但是避免了写自定义函数或者字典,这样就可以自动生成了,所以大部分都是无序的。如果我们希望它是有序的,即Python对应0,Java对应1,除了自己指定,还有什么优雅的办法吗?这时候可以使用factorize,会按照出现的先后顺序进行编码:df10=df.copy()df10['CourseName_Label']=pd.factorize(df10['CourseName'])[0]结合匿名函数,我们可以对多列进行顺序编码转换df10=df.copy()cat_columns=df10.select_dtypes(['object']).columnsdf10[['Sex_Label','CourseName_Label']]=df10[cat_columns].apply(lambdax:pd.factorize(x)[0])至此,我要介绍的十种pandas数据编码方式就分享完了,代码去掉变量名即可使用。现在回到文章开头的问题,如果你觉得pandas使用起来比较混乱,那说明你对pandas的了解可能还不够全面透彻。其实就像本文介绍的数据编码转换,实现方式确实有很多,看起来比较乱,但是学习pandas的正确姿势是把它当作字典来学。你不必记住所有的方法和细节。你只需要知道有这样一个函数就可以完成这样的操作,需要的时候想起来,想起来的时候去查。
