![大数据分析与应用实战:统计机器学习之数据导向编程](https://wfqqreader-1252317822.image.myqcloud.com/cover/943/44509943/b_44509943.jpg)
1.7.2 自定义函数
如同数学上的函数一样,动态程序语言R与Python的函数对象都是依据输入的对象参数(objects as arguments)进行计算与转换后再传出输出对象。以R语言为例,它运用function关键词创建函数的语法如下(Python自定义函数请参见1.6.2节Python语言面向对象):
function(arguments) {body}
其中参数值(arguments)是一个(或以上)对象名称(a set of symbol names),可用等号运算符给予对象参数默认值,传入函数主体内执行指令语句后,用return关键词传回最后一行语句,但return关键词常被省略。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P106_4327.jpg?sign=1739228109-KLQ2ucX5Rtq4izhw6YD7l7VcE845s76V-0-6029e838a8d2bb647a4c7843577e0097)
与函数相关的另一个名词是参数(parameter),它是函数内程序转换或运算所需要的固有性质(intrinsic property),须包含在函数的定义中;参数值(argument)是当调用函数时,实际传入函数程序中的值,R语言用args()函数显示函数的参数名与对应的默认值。为了方便说明,本书后续不刻意区分参数与参数值。
大部分情况下,需将关键词function定义的函数对象用用户自定义的名称存储起来,方便后续使用。但是也有不具名函数嵌套在其他函数中结合运用的情况,此时不具名函数被称为匿名函数(anonymous function),Python语言称之为Lambda函数。
上例说明R语言如何自定义函数,函数corplot有三个参数,其中参数plotit已有默认的参数值FALSE。下面调用corplot()时,根据使用者传入的参数值u、v和真假值(最后一个可传可不传,因为已经有默认值FALSE了),计算相关系数,并进行逻辑语句判断,决定是否绘制散点图。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P107_20110.jpg?sign=1739228109-Uy6gr3ZzkU3ysyBneQJYwM0QOYgpATef-0-893c5b4a0aed493472794fd05b264ea2)
plotit为真的散点图如图1.12所示。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P107_19958.jpg?sign=1739228109-rS67sfXrCOmKTqgmksEfHm012uCTZfOs-0-869f2b03ae9123242e2e57c4e4898cb6)
图1.12 corplot()函数的参数plotit设定为真时绘制的散点图
在数据处理与分析实战时,经常将重复性的工作定义为函数,便于精简代码、模块化工作流程。下例先读入新生数据集,有入学学年、学院、系所、班级、性别与毕业学校等字符串字段。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P108_4394.jpg?sign=1739228109-nXMj6z4sJe4tpsSXTohfFRUl3UIEB6Rq-0-af3bb55825be6461d1fa9f59881e926d)
除了字段学号(识别变量通常不转为因子)外,先将newbie所有字段转换为因子向量,并查看各字段摘要报表。其中可以发现性别字段有异常值,故将其再转回字符串类型,并用gsub()函数将男女异常值替换为正常值。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P108_19959.jpg?sign=1739228109-R2aegUOUWPuh2RaZdi7VuqgYepalA0tx-0-9599ed0bda3b4468f54e5d31e050e086)
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P109_19960.jpg?sign=1739228109-4KB11yp0P0V5MrlIPHnkMtbF1PeYO8wJ-0-6413e451aa391faf7bc524d1aa1b7486)
查看清理后的性别值频率分布,确定正常后再将其转为因子向量。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P109_19962.jpg?sign=1739228109-7hf731pUoZm5PeGdVCEkLFdALmy2PBOm-0-024fd782596fe621fa59d918c6a384cc)
使用lapply()隐式循环函数,对部别、学制、系所、学院与性别等类别字段,成批产生频率分布表,了解其类别数据分布状况。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P110_19963.jpg?sign=1739228109-qblF77XMbHvxX4y3PWmNqbLz9UFG3yPI-0-99722ce45efd42e61731980b8da6090d)
所谓文不如表,表不如排序后的表。因此,进一步使用lapply()隐式循环函数,结合前述匿名函数概念,将各个字段(即匿名函数中的参数u,读者请自行思考其为几维的数据对象?)依序产生频率分布表后再进行排序。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P111_4451.jpg?sign=1739228109-kUOK1K8qAbeCIszDupGT5kMEsp9EjMzO-0-fcfa220d310bfb0e68254eecb7615638)
校方需要统计各系科(dept)各学制(acasys)下生源排名前三与后三的学校,因此定义下面deptByAcaSys()的函数。函数在传入科系与学制名后,先用逻辑值索引(logical indexing)挑选子表tbl,接着对子表中的毕业学校一栏产生排序后的频率分布表top3与bottom3,最后组织成数据集df后传出。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P112_19965.jpg?sign=1739228109-MPBqsBH0ObjNq0JkwNtGhdEI3EYC65sT-0-267ea189a2e9016c77a85390eea0cff5)
以下是调用deptByAcaSys()自定义函数的两个例子,我们还须思考是否有可以改进之处。其实deptByAcaSys()缺乏输入参数的合理性检查(sanity check),好的用户自定义函数应该能够避免不当参数的输入,例如,dept是否在该校9个系所名单内,避免造成意料之外的错误。限于篇幅,请读者自行举一反三。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P112_19966.jpg?sign=1739228109-HBItyIngrgNKNnwRQlJJwOPPDN6Uj9Rs-0-bafd740b7893073d8d747ca2633c96f4)
最后,无论是内置函数、各套件中的函数,还是自行定义的函数,读者应注意引用函数时其默认的参数值与可能的参数选项,才能善用函数模块化数据处理与分析的工作流程。