解决问题需要首先了解问题所在,对症下药。tibble包的存在就是为了给数据清理及后续的分析提供一个最佳的起点。tibble既是R包的名字也是数据在R中的一种存储格式。可以将tibbleq包理解为R中最常见的data.frame(数据框)格式的升级版。像下列代码所示,如果使用read.csv读取数据,那么数据会被存储在data.frame(数据框)格式中。但是当调用read_csv时,数据就会存在三种适用格式:tbl_df、tbl和data.frame。因为tibble和readr包都源自于Hadley的tidy系列,所以使用readr包时自动植入了tibble(以下简称tbl)的数据格式。那么,问题来了,为什么非要使用这个格式呢?
> iris <- read.csv("./RawData/iris.csv",stringsAsFactors = F) > class(iris) ## [1] "data.frame" > iris <- read_csv("./RawData/iris.csv") > class(iris) ## [1] "tbl_df" "tbl" "data.frame"
tbl格式作为老旧的data.frame升级版,主要包含如下三点优势。
1)稳定性更好,可完整保存变量名称及属性。
2)更多的信息展示、警示提醒,有利于及时发现错误。
3)新的输出方式使得浏览数据时,屏幕的利用率极佳。
因为R语言已经诞生了将近20年,很多早期的函数都是围绕data.frame格式写就的,当调用这类函数时,“新兴”的tbl格式可能会出现不兼容的情况,这也是tbl格式目前被发现的唯一缺陷。
tbl格式的优势第一条需要读者在使用过程中对比两种格式的差异才会有直观感受,简单来讲就是,传统的data.frame在处理变量名称时,有时会悄悄改动名称以满足自身要求,这往往会给用户带来一些意料之外的错误。请看以下的例子。
> data.frame('x + y' = 1∶5) > tibble('x + y' = 1∶5)
两行代码分别使用函数data.frame和tibble创建了一个传统的数据框格式(见表2-6),以及一个tbl格式的数据框(见表2-7)。代码中定义的数据变量名称为“x+y”,但在data.frame格式中被修改成了“x...y”。大部分情况下,这种默认的修改是数据框格式的一种自我保护机制,目的是为了后续计算时引用变量名不会产生歧义。但是这种保护机制同时会与编程数据分析的另一项基本原则发生冲突,即常量输入等于常量输出(这里的常量可以理解为变量名),除非用户主动修改,否则其名称应保持一致。至于如何选择,就需要读者自行决断了。
表2-6 传统数据框格式对变量名的处理
表2-7 tibble格式对变量名的处理
第1章中提到查看data.frame中的变量类型时,通常需要调用str函数。但是在tbl格式中,无须调用任何函数,直接输入数据集名称即可查看相关信息。默认情况下,tbl格式会根据console窗口的大小,自动调整显示的内容。内容会包含数据格式、列总数、行总数、变量名称和类型,以及无法完全展示部分的变量信息。有一定data.frame使用经验的读者肯定知道,对于不调用str函数直接在console中运行data.frame格式的数据集,R会将小于1000列×1000行的所有内容都显示出来,而且其中还不包括变量属性等信息。tbl格式查看数据集相关信息的示例代码如下:
> iris # A tibble: 50 x 12 Sepal.L..Setosa Sepal.W..Setosa <dbl> <dbl> 1 5.10 3.50 2 4.90 3.00 3 4.70 3.20 4 4.60 3.10 5 5.00 3.60 6 5.40 3.90 7 4.60 3.40 8 5.00 3.40 9 4.40 2.90 10 4.90 3.10 # ... with 40 more rows, and 10 more variables: # Petal.L..Setosa <dbl>, # Petal.W..Setosa <dbl>, # Sepal.L..Versicolor <dbl>, # Sepal.W..Versicolor <dbl>, # Petal.L..Versicolor <dbl>, # Petal.W..Versicolor <dbl>, # Sepal.L..Virginica <dbl>, # Sepal.W..Virginica <dbl>, # Petal.L..Virginica <dbl>, # Petal.W..Virginica <dbl>
在练习使用tibble时,可以通过函数tibble或tribble[A24]来创建新的数据框。tibble函数创建新数据框的方法与baseR中data.frame函数方法一致。等号左边为变量名称,右边为相应的数据值,不同变量之间以逗号相隔。下面的代码创建了一个包含变量a和b的数据框,变量a包含6个值,分别为数字1到6,变量b为a列中的值乘以2,因为同为6个数值。<int>代表integer(整数),<dbl>代表double(浮点型)数据类型。创建数据框的代码如下:
> library(tibble) > tibble(a = 1:6,b = a*2) # A tibble: 6 x 2 a b <int> <dbl> 1 1 2. 2 2 4. 3 3 6. 4 4 8. 5 5 10. 6 6 12.
tribble函数比较适合用来创建小型数据集,可以采用常规excel表中数据分布的格式,直接手动输入数据,变量名称以“~”起始,逗号结束,数据值以逗号分隔。下面的代码即用来生成表2-2的。该函数在特定情况下会显得非常实用,比如,用来解释变量(或指标)和因子水平的元数据一般都会杂乱无章,使用软件清理既费时又费力,但当通过肉眼能够很容易提取到关键信息时,直接使用tribble函数手动生成变量和因子水平对照表会更高效。
> tribble( ~id, ~gender, ~weight, ~age, 1, "男", 70, 23, 2, "女", 60, 25, 3, "女", 55, 26, 4, "女", 58, 22, 5, "男", 80, 23, 6, "男", 85, 30 )
在转换数据的格式之前,可以使用is_tibble来测试目标对象是否已是tbl格式,该函数只需要对象名称这一个参数即可。
可以通过as_tibble函数将对象已有的格式(vector,matrix,list和data.frame等)转换成tbl。表2-8中列出了常见对象格式的转换注解。
表2-8 常见R对象与tibble格式的转换注解
下面通过具体的代码来说明使用as_tibble函数将常见的R对象转换成tibble格式的具体方法。
(1)as_tibble函数直接将vector格式转换成数据框格式
1)随机设置一组向量,保存为y。
2)检视向量y。
3)调用as_tibble函数直接转换,并将结果显示到console中。
实现代码具体如下:
> y <- 1:3 > y ## [1] 1 2 3 > as_tibble(x = y)
向量转换成tibble格式的结果如表2-9所示。
表2-9 向量被转换成tibble格式后结果
(2)矩阵格式转换
首先创建一个名为b的示例矩阵,矩阵按照行排列数字1到9,行数和列数同为3,行名为“Row1”“Row2”“Row3”,列名为“col1”“col2”“col3”。表2-10中显示了移除行名——设置rownames=NULL的结果,表2-11为保留行名设置rownames=NA的结果。
实现代码具体如下:
> b <- matrix(data = 1:9,nrow = 3,ncol = 3,byrow = T,dimnames = list(paste0("Row",1:3),paste0("col",1:3))) > b ## col1 col2 col3 ## Row1 1 2 3 ## Row2 4 5 6 ## Row3 7 8 9 > as_tibble(x = b,rownames = NULL) > as_tibble(x = b,rownames = NA)
表2-10 函数as_tibble中参数rownames设置为NULL的结果
表2-11 函数as_tibble中参数rownames设置为NA的结果
(3)传统数据框格式转换
传统数据框格式转换与转换矩阵格式基本相同,读者可以自行试验。
(4)列表格式转换
列表格式转换一定要注意列表中要素的长度,即每个要素中所拥有的数值个数。
下面的代码创建了一个名为“l”的列表,列表中包含了三个要素,分别是a、b和c。其中,要素a包含三个数值,要素b包含三个字母数值,要素c只有一个数字,创建列表的代码如下:
> l <- list(a = 1:3,b = letters[2:4],c = 1) > l ## $a ## [1] 1 2 3 ## ## $b ## [1] "b" "c" "d" ## ## $c ## [1] 1
执行函数as_tibble可以毫无压力地将该列表转换成“tbl”格式,每个要素单独成为一个变量,原列表中的要素c中的数值将被重复使用三次以对应其他变量的长度。如果要素c的长度为2,即包含两个数值,那么转换会失败。as_tibble函数代码如下:
> as.tibble(x = l)
表2-12显示的是列表转换成功后的结果。
表2-12 列表格式转换为tibble格式结果
tibble包中另一个可以转换数据格式的函数是enframe函数。该函数的优势在于格式转换时可对向量数据进行编号。以一个长度为3的数值向量为例,运行该函数之后,会得到一个拥有两个变量,每个变量包含三个数值的数据框,第一个变量名为“name”,是对数值向量的对应编号。
在微软的Excel中,用户可以随意插入或者删除一行/列数据,add_row/column函数也为R用户提供了类似的功能。使用baseR来完成新增列的需求相对来说很简单。下面的代码首先创建了一个“tbl”,然后使用“$”来为数据新增一列名为“k”的变量,变量的数值为3、2、1:
> f <- tibble(i = 1:3,j = c("John","Sam","Joy")) > f ## # A tibble: 3 x 2 ## i j ## <int> <chr> ## 1 1 John ## 2 2 Sam ## 3 3 Joy > f$k <- 3:1 > f ## # A tibble: 3 x 3 ## i j k ## <int> <chr> <int> ## 1 1 John 3 ## 2 2 Sam 2 ## 3 3 Joy 1
在数据框的末尾加入一行新数据也可以实现新增列的功能,不过该功能需要读者对R的基本理论有一定的理解。如下代码所示,延续数据框“f”,配合使用函数“[”和“nrow”可在数据框的末尾新增一行数据。“nrow”的功能是计算对象的行数。
> f[nrow(f)+1, ] <- c(4,"Jon",0) > f ## # A tibble: 4 x 3 ## i j k ## * <chr> <chr> <chr> ## 1 1 John 3 ## 2 2 Sam 2 ## 3 3 Joy 1 ## 4 4 Jon 0
中括号紧跟在数据框后面,可以作为索引来选择数据框中的特定数值。逗号前面为行索引,后面为列索引:[行,列]。读者可以试着配合“ncol”函数,以类似新增行的方式来为“f”新增一列。
tibble包中这两个实用的小函数,可以随时随地任意新增行列数据到指定位置,而不是像baseR中的命令那样只能在数据尾部或已有变量后面新增行或列,在还不是很熟悉R的各种符号代码之前,这两个函数是可以帮助用户快速有效地解决实际问题的。
下面的代码为“f”又新增了一行数据,不过因为这里仅指定了两个变量的值,所以对于未指定的部分,系统将自动填入默认值,具体代码如下:
> add_row(f,i = 4,j = "Jon") ## # A tibble: 5 x 3 ## i j k ## <chr> <chr> <chr> ## 1 1 John 3 ## 2 2 Sam 2 ## 3 3 Joy 1 ## 4 4 Jon 0 ## 5 4 Jon <NA>
在第三行之前插入一行新数据,代码如下:
> add_row(f,i = 4,j = "Jon",.before = 3) ## # A tibble: 5 x 3 ## i j k ## <chr> <chr> <chr> ## 1 1 John 3 ## 2 2 Sam 2 ## 3 4 Jon <NA> ## 4 3 Joy 1 ## 5 4 Jon 0
第一行之后插入新数据,代码如下:
> add_row(f,i = 4,j = "Jon",.after = 1) ## # A tibble: 5 x 3 ## i j k ## <chr> <chr> <chr> ## 1 1 John 3 ## 2 4 Jon <NA> ## 3 2 Sam 2 ## 4 3 Joy 1 ## 5 4 Jon 0
在第一列之后插入新变量,代码如下:
> add_column(f,l = nrow(f):1,.after = 1) ## # A tibble: 4 x 4 ## i l j k ## <chr> <int> <chr> <chr> ## 1 1 4 John 3 ## 2 2 3 Sam 2 ## 3 3 2 Joy 1 ## 4 4 1 Jon 0