详细说明timcdlucas(以及r2evans 的cmets)的答案,这里的问题是各种形式的提取运算符的行为,而不是tibble 的行为。为什么? tibble 实际上是 data.frame 的一种,正如我们在 tibble 上使用 str() 函数时所示。
> library(dplyr)
> aTibble <- tibble(f1 = factor(rep(letters[1:3],5)),
+ c1 = rnorm(15))
>
> # illustrate that aTibble is actually a type of data frame
> str(aTibble)
tibble [15 × 2] (S3: tbl_df/tbl/data.frame)
$ f1: Factor w/ 3 levels "a","b","c": 1 2 3 1 2 3 1 2 3 1 ...
$ c1: num [1:15] -0.5829 0.3682 1.1854 -0.6309 -0.0268 ...
R中有四种形式的提取操作符:[、[[、$和@;如What is the meaning of the dollar sign $ in R function? 中所述。
第一种形式[ 可用于提取内容形式的向量、列表、矩阵或数据框。当与数据框(或 tidyverse 中的 tibble)一起使用时,它返回类型为 data.frame 或 tibble 的对象,除非包含 drop = TRUE 参数,如 r2evans 的问题 cmets 中所述。
由于[ 函数中drop= 的默认设置是FALSE,因此df[,"f1"] 会为原始问题发布的代码产生意外或“错误”的结果。
library(dplyr)
aTibble <- tibble(f1 = factor(rep(letters[1:3],5)),
c1 = rnorm(15))
# produces unexpected answer
nlevels(aTibble[,"f1"])
> nlevels(aTibble[,"f1"])
[1] 0
drop = 参数用于从矩阵或数组(即任何具有dim 属性的对象中提取,如drop() 函数的帮助中所述。
> dim(aTibble)
[1] 15 2
>
当我们设置drop = TRUE 时,extract 函数返回一个可用的最低类型的对象,即所有长度为 1 的范围都被删除。对于原始问题,带有提取运算符的drop = TRUE 返回一个因子,这是nlevels() 的正确输入类型。
> nlevels(aTibble[,"f1",drop=TRUE])
[1] 3
提取运算符的[[ 和$ 形式提取单个对象,因此它们返回factor 类型的对象,这是nlevels() 所需的输入。
> str(aTibble$f1)
Factor w/ 3 levels "a","b","c": 1 2 3 1 2 3 1 2 3 1 ...
> nlevels(aTibble$f1)
[1] 3
>
> # produces expected answer
> str(aTibble[["f1"]])
Factor w/ 3 levels "a","b","c": 1 2 3 1 2 3 1 2 3 1 ...
> nlevels(aTibble[["f1"]])
[1] 3
>
提取运算符的第四种形式@(称为槽运算符)用于使用 S4 对象系统构建的正式定义的对象,与此问题无关。
结论:使用 Tidyverse 时 Base R 仍然适用
根据tidyverse.org,tidyverse 是 R 包的集合,共享基本哲学、语法和数据结构。当人们熟悉 tidyverse 包系列时,就可以做到在不了解 Base R 工作原理的情况下,R 中有很多东西。
也就是说,当将 Base R 函数或 tidyverse 外部包中的函数合并到 tidyverse 风格的代码中时,了解关键的 Base R 概念非常重要。