![大数据分析与应用实战:统计机器学习之数据导向编程](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=1739963708-3LaDf8NVR7IAp8Ef4lQxDE7feKGpIASA-0-477df09a57f653767bf4d2547deab7c1)
与函数相关的另一个名词是参数(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=1739963708-CtXa691JomjBNc9V9ufYfQHzX2TDJSVu-0-31bb232e580eded2f4adf5f611e3e16e)
plotit为真的散点图如图1.12所示。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P107_19958.jpg?sign=1739963708-ZRrGCCr5e5xuQR4CVCTDVSg5njC21QPC-0-a589c8397402b488b05a29db200498c9)
图1.12 corplot()函数的参数plotit设定为真时绘制的散点图
在数据处理与分析实战时,经常将重复性的工作定义为函数,便于精简代码、模块化工作流程。下例先读入新生数据集,有入学学年、学院、系所、班级、性别与毕业学校等字符串字段。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P108_4394.jpg?sign=1739963708-CxJRi6q1CxFygKw5rNTEW402NHG1Kuq9-0-8e6163c2b2ca3ab7f1f3cc66c39cb7f2)
除了字段学号(识别变量通常不转为因子)外,先将newbie所有字段转换为因子向量,并查看各字段摘要报表。其中可以发现性别字段有异常值,故将其再转回字符串类型,并用gsub()函数将男女异常值替换为正常值。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P108_19959.jpg?sign=1739963708-nTinC9ZzcCAqRbqjzIuBo9EikoK7foqe-0-55415fea47aad22b78b8928b301f3d84)
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P109_19960.jpg?sign=1739963708-BgvaxfWB8Dqs31nxqvFqbw0doHHRK7SL-0-298323790a22b296cd667523358150cd)
查看清理后的性别值频率分布,确定正常后再将其转为因子向量。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P109_19962.jpg?sign=1739963708-yGfJcuCRzwSaRwmpHQjJv2DasAX1v4P7-0-68ad94f16b154324c81f369cfb48bde4)
使用lapply()隐式循环函数,对部别、学制、系所、学院与性别等类别字段,成批产生频率分布表,了解其类别数据分布状况。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P110_19963.jpg?sign=1739963708-iZFFP2orAPlI6FrhzRrxm7iryfnRHy0n-0-d1504479e43672cd794891f6557a9a0d)
所谓文不如表,表不如排序后的表。因此,进一步使用lapply()隐式循环函数,结合前述匿名函数概念,将各个字段(即匿名函数中的参数u,读者请自行思考其为几维的数据对象?)依序产生频率分布表后再进行排序。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P111_4451.jpg?sign=1739963708-gNZVM7xHnWSWS8mXI2ZnMjtkQ3JR3hBb-0-55a8d745491ad69bd2f79c6ad9daa63e)
校方需要统计各系科(dept)各学制(acasys)下生源排名前三与后三的学校,因此定义下面deptByAcaSys()的函数。函数在传入科系与学制名后,先用逻辑值索引(logical indexing)挑选子表tbl,接着对子表中的毕业学校一栏产生排序后的频率分布表top3与bottom3,最后组织成数据集df后传出。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P112_19965.jpg?sign=1739963708-z8KRhe79c7VQetClMcvgGZpw90RvnhaG-0-a5984631ccd228221cebdd021f9f2ab2)
以下是调用deptByAcaSys()自定义函数的两个例子,我们还须思考是否有可以改进之处。其实deptByAcaSys()缺乏输入参数的合理性检查(sanity check),好的用户自定义函数应该能够避免不当参数的输入,例如,dept是否在该校9个系所名单内,避免造成意料之外的错误。限于篇幅,请读者自行举一反三。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P112_19966.jpg?sign=1739963708-o89WQgFEs6WsKvnKkcEBltZlXPAw15Pf-0-1f66c484fd77535eb3097a95b0d2320c)
最后,无论是内置函数、各套件中的函数,还是自行定义的函数,读者应注意引用函数时其默认的参数值与可能的参数选项,才能善用函数模块化数据处理与分析的工作流程。