- 大数据分析与应用实战:统计机器学习之数据导向编程
- 邹庆士编著
- 1244字
- 2022-07-28 20:15:40
1.5 向量化与隐式循环
数据分析语言的有趣特征之一是函数可以应用许多不同的数据对象,如向量、矩阵、数组与数据集等,而非仅仅标量而已,此即称为向量化(vectorization),请看下面范例(Kabacoff,2015)。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P85_20096.jpg?sign=1739405912-Y1wTGeUYusP3l0W9PweKkKbT9huhsw6N-0-6d120282b178b43116a55e84f83b2d36)
上例中a为一常数(标量),而函数sqrt()如同计算机(calculator)一般执行于单值标量上。如果将函数round()与log()分别应用到一维向量或二维的矩阵,其结果如下:
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P86_19907.jpg?sign=1739405912-sRpnkBojSjGOZbm4wxCPcvtdVfP0yXAg-0-0beb350a54dfddb4d62232fa289fb969)
从上面的结果读者不难发现,sqrt()、round()与log()等函数是施加在数据中的每一个元素上,但是有些函数就并非如此执行了!例如下面常用的平均值计算函数mean():
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P86_20097.jpg?sign=1739405912-Vk3PBmEKK9Y6GkN0aC4IgxVLOkWS6P6e-0-e8b54652d2afcfbc038aeee89c0a09f0)
mean()函数计算矩阵m的12个元素的算术平均数,因此读者须经常注意输入的数据对象(此处m为3×4矩阵),经函数处理后产生的输出对象(上例传回单值),其维度是否改变?数据结构是否改变?类型是否改变?这是掌握数据驱动程序设计的重要概念(参见1.9节程序调试与效率监测)。
上例中如果要计算矩阵m三行的平均值或四列的平均值,可以运用apply()函数:
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P86_20098.jpg?sign=1739405912-GF6VpkZd7otXdEFUFQCA3jI2uS3g8tSj-0-ba8085c188312687baf324963e903b7b)
其语法为:
apply (m, MARGIN, FUN,...)
apply()函数是将FUN运算施加于矩阵或数组对象的某一维度上,其中m是数组(包括二维矩阵),MARGIN是给定运作维度的数值向量或字符串向量,FUN是欲应用的函数,而...是额外要传入FUN的参数值。m为二维的矩阵或数据集时,MARGIN=1表示逐行套用函数,MARGIN=2表示逐列套用函数。在数据驱动的程序语言中,R或Python的numpy与pandas等模块,建议避免写显式循环(explicit looping,即for循环),而以隐式循环(implicit looping)的apply()系列函数取代之,不过上例中apply()还是比rowMeans()或colMeans()等更直接的向量化函数慢。Python语言apply()方法的编程应用,请参见1.6节编程范式与面向对象概念、2.2.3节Python语言群组与摘要,以及4.2.2节在线音乐城关联规则分析等各节范例。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P87_19908.jpg?sign=1739405912-QDSiuEsB9L80scixr3w9IyKSiM7b1yy8-0-4340cbc536a32c964ccb2e946a695fbb)
若为一维的向量或列表对象,可以使用lapply()或sapply()函数,两者执行方式相同,其中"s"意指简化(simplify),此函数在必要时将简化lapply()函数返回的数据对象。以下用简例说明两者的用法:
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P88_20100.jpg?sign=1739405912-m5CyN2LlQACBo6PwirQVpUlopCNBeEw2-0-f25fe1127768212f82803db852a1516c)
R语言apply系列函数众多,mapply()可施加一个函数于多个列表或向量的对应元素上,下例中firstList与secondList均是长度为3的列表对象,mapply()将identical()函数施加在上述两列表的对应元素上,判断其是否完全相同。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P88_19909.jpg?sign=1739405912-6IkRN1wQxJHZx6q19sbEPfv4a61Rg0pO-0-ce2d7265aa455ee35da7ba39bba56a7f)
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P89_19910.jpg?sign=1739405912-oqhgQBJdKDwPpHHE8NHN8OHJypY2Fg5U-0-8e1950e7986cf6585222ac7f66f6d8f6)
mapply()函数语法中的FUN也可以是如下自定义的匿名函数(anonymous function),计算firstList与secondList对应元素的列数和,其他apply系列函数也可以调用自定义的匿名函数。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P89_19911.jpg?sign=1739405912-8qulkLnSTdLZggTYmT6nesAjJVwP06bX-0-dcd540fa4becc1c4c2bd46f1f765de36)
活用mapply()函数有时可以快速完成某些分组处理或可视化的工作,下例在mapply()函数中定义绘制各组回归直线的匿名函数后,将其添加到iris数据集的Sepal.Width对Petal.Length的散点图上,然后在适当位置标出图例(图1.9)(Verzani,2014)。
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P90_19912.jpg?sign=1739405912-BtOc78hg0plZg1N8DhF2UjQZZ8rNZgwS-0-27f28bde3907d94a86ecc4633a1e7bd9)
图1.9 鸢尾花花萼宽度与花瓣长度分布情形及分组回归直线图
![](https://epubservercos.yuewen.com/027AFF/23721555909466506/epubprivate/OEBPS/Images/Figure-P91_19914.jpg?sign=1739405912-0SPeoO0QliatEyMpFZ4HB0eUxJBtVvD8-0-43ca623973feee124f60045e1f323d7f)
其实数据驱动程序设计中输入输出的变量符号(symbol)大多是数据对象,它们可能是一维、二维或更高维的结构。因此,数据分析语言多采用向量化数据处理与计算的方式,以避免额外循环执行,提升工作效率。许多运算符(如乘方运算符^、比较运算符>、加号+)及函数(如apply(),lapply(),sapply(),scale(),rowMeans(),colMeans())都是向量化函数,也就是说函数中隐藏着循环(implicit looping)的处理方式。所以再次提醒读者注意思考下面问题:输入的数据对象经函数处理后产生的输出对象,其维度是否改变?数据结构是否改变?类型是否改变?(参见1.9节程序调试与效率监测)反复思索上述问题可以掌握数据驱动程序设计背后的运行逻辑。