2.8 筛选
反映R函数式语言特性的另一个特征是“筛选”(filtering)。这使我们可以提取向量中满足一定条件的元素。筛选是R中常用的运算之一,因为统计分析往往关注满足一定条件的数据。
2.8.1 生成筛选索引
我们先看一个简单的例子:
查看这段代码,凭直觉想想 “我们的目的是什么?”。可以看出我们要求R提取z中平方大于8的所有元素,然后将这些元素构成的子向量赋值给w。
筛选是R中很关键的运算,因此我们有必要从技术细节上探究一下R是怎样实现上述意图的。我们来逐步研究:
表达式z*z > 8得出的是布尔值向量。对你而言,弄清楚这个结果如何产生是非常重要的。
首先,注意表达式z*z > 8中所有东西都是向量或向量运算符:
因为z是向量,所以z*z同样是向量(并且长度与z一致)。
通过循环补齐,这里的数字8(长度为1的向量)补齐为向量(8,8,8,8)。
运算符>,像+一样,实际上是个函数。
对于最后这一点,我们看个例子:
换句话说,我们对向量使用函数—它也是向量化的另一个例子,与你看到的其他向量化一样。在本例中,结果是一个布尔值向量。然后用得到的布尔值向量筛选出z中所需的元素:
下一个例子将更有针对性。在这里,我们将再次用z定义提取条件,但接着用该结果从另一个向量y,而不是从z中提取子向量,如下所示:
或者,可以像下面这样写更简洁:
再次强调,这个例子要说的是,我们使用向量z决定筛选另一个向量y的索引。相反,前面的例子是使用z筛选它自身。
下面是另一个例子,其中涉及赋值。设我们有一个向量x,要将其中所有比3大的元素替换为0。事实上,我们可以非常简洁地使用一行代码。
2.8.2 使用subset()函数筛选
也可以使用subset()函数做筛选。当对向量使用该函数时,它与普通的筛选方法的区别在于处理NA值的方式上。
我们使用前一节提到的普通筛选方法,R会认为“x[5]是未知的,因此其平方是否大于5同样是未知的。”但也许你不希望NA出现在结果中。当你希望在结果中剔除NA值时,使用subset()将免去自己移除NA的麻烦。
2.8.3 选择函数which()
正如你所看到的,筛选是从向量z中提取满足一定条件的元素。但是,在某些情况下,我们希望找到z中满足条件元素所在的位置。此时可以使用which(),如下所示:
结果表明z中的第一、第三和第四元素平方大于8。
和筛选一样,了解前面的代码到底发生了什么是很重要的。下面的表达式:
计算得到(TRUE,FALSE,TRUE,TRUE)。which()函数简单地报告出在后面的表达式中哪些元素为TRUE。
which()有一个非常方便(尽管有点浪费)的用法,是在一个向量中找出满足一定条件的元素首次出现的位置。例如,回顾本书2.1.2节代码,找出向量中的第一个1。
调用which()产生x中所有1的索引。这些索引将以向量形式给出,然后我们取该向量中的第一个元素,即是第一个1的索引。
这一代码更加简洁。但另一方面,它也比较浪费,因为它找出了x中所有的1,而我们只需要第一个。因此,尽管它是向量化方法,可能更快,但如果x中第一个1出现在靠前的位置,则此方法实际上要慢一些。