前言
在前面的介绍中,我们使用到了 tibbles
这种数据结构。
这是 tidyverse
包定义的类似于 R
传统的 data.frame
。
R
语言产生到现在也挺久了,一些 10
年或 20
年前很有用的东西现在不一定同样有用。在不破坏现有代码的情况下想要改变 R
的基础数据结构是很困难的,所以大多数的创新都是在开发新包中。
在这里,我们将介绍 tibble
。
首先,导入 tidyverse
library(tidyverse)
# or
library(tibble)
构造 tibbles
tibbles
是 tidyverse
包的特性之一,几乎所有函数都会生成 tibbles
。
绝大多数的 R
包都是使用常规的 data.frame
,如果你想将其转换为 tibble
,你可以使用 as_tibble()
> as_tibble(iris)
# A tibble: 150 x 5
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <fct>
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
# … with 140 more rows
你可以用 tibble()
从单个向量创建一个新的 tibble
。tibble()
会自动循环长度为 1
的输入,并允许您引用刚刚创建的变量,如下所示。
> tibble(
+ x = 1:5,
+ y = 1,
+ z = x ^ 2 + y
+ )
# A tibble: 5 x 3
x y z
<int> <dbl> <dbl>
1 1 1 2
2 2 1 5
3 3 1 10
4 4 1 17
5 5 1 26
如果你对 data.frame()
很熟悉,可以看出 tibble
做的很少:它不会更改输入的类型(例如,不会将字符串转换为因子),同时也不会更改变量的名称,也不会创建行名。
tibble
允许无效的 R
变量名称作为列名,也就是非语法名称。
例如,它们可能不以字母开头,或者可能包含特殊的字符,如空格等。为了引用这些变量,需要用 ` 将它们引起来。
> tb <- tibble(
+ `:)` = "smile",
+ ` ` = "space",
+ `2000` = "number"
+ )
> tb
# A tibble: 1 x 3
`:)` ` ` `2000`
<chr> <chr> <chr>
1 smile space number
在其他包(如 ggplot2
、dplyr
和 tidyr
)中使用这些变量时,也需要用 ` 包裹。
创建 tibble
的另一个方法是 tribble()
,是 transposed tibble
的缩写。
tribble()
是为了在代码中输入数据而定制的,列名由公式定义(即,它们以 ~
开头),条目之间用逗号分隔,如
> tribble(
+ ~x, ~y, ~z,
+ #--|--|----
+ "a", 2, 3.6,
+ "b", 1, 8.5
+ )
# A tibble: 2 x 3
x y z
<chr> <dbl> <dbl>
1 a 2 3.6
2 b 1 8.5
通常可以添加一行 #
开头的注释,来标明表头的位置
tibbles vs. data.frame
tibbles
和 data.frame
之间主要有两个区别:
- 打印
- 子集
1. 打印
tibbles
有一个精巧的打印方法,只显示前 10
行和适应屏幕上的所有列,这使得处理大数据更加容易。
除了名称之外,每列都会显示其类型,这是从 str()
中借鉴来的一个很好的特性
> (df <- tibble(
+ a = lubridate::now() + runif(1e3) * 86400,
+ b = lubridate::today() + runif(1e3) * 30,
+ c = 1:1e3,
+ d = runif(1e3),
+ e = sample(letters, 1e3, replace = TRUE)
+ ))
# A tibble: 1,000 x 5
a b c d e
<dttm> <date> <int> <dbl> <chr>
1 2021-01-17 14:24:00 2021-02-01 1 0.928 a
2 2021-01-17 06:19:44 2021-02-14 2 0.162 y
3 2021-01-16 17:42:21 2021-01-16 3 0.133 k
4 2021-01-17 09:27:45 2021-01-30 4 0.669 c
5 2021-01-17 10:49:48 2021-02-03 5 0.596 b
6 2021-01-16 19:11:51 2021-02-14 6 0.283 i
7 2021-01-17 06:49:47 2021-02-02 7 0.348 m
8 2021-01-16 17:12:39 2021-02-07 8 0.321 g
9 2021-01-16 18:12:05 2021-01-23 9 0.726 c
10 2021-01-17 03:24:03 2021-02-11 10 0.950 d
# … with 990 more rows
tibbles
的设计使您在打印大数据帧时不会意外地淹没控制台,R
的这个毛病真是实力劝退。
但有时需要比默认显示更多的输出。有几个选项可以帮助你
首先,可以显式地 print()
数据框并控制行数和列的宽度。width = Inf
将会显示所有列
> df %>% print(n=5, width=Inf)
# A tibble: 1,000 x 5
a b c d e
<dttm> <date> <int> <dbl> <chr>
1 2021-01-16 23:20:47 2021-02-07 1 0.460 i
2 2021-01-17 10:18:31 2021-02-08 2 0.384 w
3 2021-01-17 00:06:15 2021-02-11 3 0.188 s
4 2021-01-17 04:53:02 2021-02-10 4 0.328 j
5 2021-01-16 23:13:51 2021-01-31 5 0.445 m
# … with 995 more rows
您还可以通过设置以下选项来控制默认打印行为
# 如果多于 n 行,只打印 m 行。
options(tibble.print_max = n, tibble.print_min = m)
# 总是显示所有行
options(tibble.print_min = Inf)
# 无论屏幕的宽度如何,始终打印所有列
options(tibble.width = Inf)
2. 子集
到目前为止,你所学的所有工具都可以处理完整的数据框。
如果你想提取一个变量,你需要一些新的工具:$
和 [[
。[[
可以按名称或者位置提取,$
只能按名称提取,但输入的符号更少。
> df <- tibble(
+ x = runif(5),
+ y = rnorm(5)
+ )
# Extract by name
> df$x
[1] 0.18390961 0.49066767 0.08295448 0.07084228 0.25732377
> df[["x"]]
[1] 0.18390961 0.49066767 0.08295448 0.07084228 0.25732377
# Extract by position
> df[[1]]
[1] 0.18390961 0.49066767 0.08295448 0.07084228 0.25732377
如果想要使用管道操作,需要使用特殊的占位符 .
> df %>% .$x
[1] 0.18390961 0.49066767 0.08295448 0.07084228 0.25732377
> df %>% .[["x"]]
[1] 0.18390961 0.49066767 0.08295448 0.07084228 0.25732377
相较于 data.frame
,tibble
更严格。它们从不进行部分匹配,如果你访问的列不存在,它们将生成警告
4. 与旧代码交互
一些比较旧的代码不再适用于 tibble
,如果遇到这种函数,那就用 as.data.frame()
转换为 data.frame
格式
> as.data.frame(df)
x y
1 0.18390961 -0.08348267
2 0.49066767 -0.15224540
3 0.08295448 0.89195578
4 0.07084228 0.62134132
5 0.25732377 -1.13856797
> class(as.data.frame(df))
[1] "data.frame"
一些旧函数不能与 tibble
一起工作的主要原因是 [
函数,因为 dplyr::filter()
和 dplyr::select()
函数允许你使用更加清晰的代码解决相同的问题,所以并不是很需要 [
函数
对于 R
数据框,[
有时候返回数据框,有时候返回一个向量,而对于 tibble
,[
返回的总是另一个 tibble
。
5. 练习题
-
怎么分辨一个对象是否是
tibble
-
对比
data.frame
和等效的tibble
操作,有什么区别?为什么默认的数据框行为会让你感到沮丧?
df <- data.frame(abc = 1, xyz = "a")
df$x
df[, "xyz"]
df[, c("abc", "xyz")]
-
如果您有一个存储在对象中的变量名,例如
var <- "mpg"
,如何从tibble
中提取对应的变量呢? -
练习从下面的数据框中引用非语法名称
- 提取名为
1
的变量 - 绘制
1
和2
的散点图 - 创建一个名为
3
的新列,它是2
的值除以1
的值 - 将列重命名为
one
,two
和three
annoying <- tibble(
`1` = 1:10,
`2` = `1` * 2 + rnorm(length(`1`))
)
tibble::enframe()
函数的作用是什么?什么时候使用它?- 哪个选项控制在
tibble
的页脚打印多少额外的列名?