-
Notifications
You must be signed in to change notification settings - Fork 6
/
03-data-import.Rmd
374 lines (247 loc) · 15 KB
/
03-data-import.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# 数据导入 {#import}
在掌握一定的 R 编程技能后,我们开始迈入数据分析的殿堂。大部分数据分析事务的数据都不是通过 R 创建,而是来自于各种数据收集软硬件、渠道,包括 Excel、网络等。本章聚焦于如何将数据导入 R 中以便于开始数据分析。对于本章的内容,读者既可以系统地从头到尾深入阅读学习,也可以根据自己实际工作需要或时间限制选择一些重点或感兴趣内容进行掌握。
本章的重点内容包括符号分隔文件、Excel 文件、JSON 文件以及 R 支持的数据格式 RData 和 RDS,其他格式的数据将放到本章【**常见问题与方案**】一节作为补充介绍。
## 符号分隔文件
符号分隔文件是**最最常用**的数据文件格式,知道如何导入它是读者的必备技能。这里的**符号**泛指一切可以用作数据内容分隔的符号,常见的包括逗号(`,`),制表符(`\t`),我们常称它们为 CSV 文件和 TSV 文件。
### CSV
CSV 文件常以 `.csv` 作为文件拓展名,比如接下来我们会导入的 `mtcars.csv`。注意,文件拓展名并不会影响文件本身的内容,它只是用来方便帮助人们快速的了解内容格式,另外支持其他一些程序的自动解读(在你的计算机上,不同的文件拓展名系统软件可以会对它们使用不同的图标,如 Word 文档和 PPT)。
一般规整的数据以行作为观测,列作为记录(变量、域),如一个班级同学的成绩。
```
student,chinese,math,english
stu1,99,100,98
stu2,60,50,88
```
R 内置了 `read.table()` 函数用于便捷导入各类分隔符的文件。下面我们直接将这个成绩记录信息以文本的形式传入,结果会返回一个数据框:
```{r}
stu <- read.table(text = "
student,chinese,math,english
stu1,99,100,98
stu2,60,50,88
", header = TRUE, sep = ",")
stu
class(stu)
```
实际上要处理的数据并不会这么的少,一般会以文件的形式存储在计算机磁盘中。下面我们依旧使用 `read.table()` 函数完成 CSV 文件数据的导入。
```{r}
cars <- read.table(file = "data/data-import/mtcars.csv", header = TRUE, sep = ",")
```
操作完成了,我们检查下头几行:
```{r}
head(cars)
```
除了使用 `read.table()`,我们还可以使用内置的 `read.csv()` 函数完成 CSV 文件的读入。与 `read.table()` 不同的时,我们无需再指定分隔符,因为该函数本身就是为了 CSV 文件设计的。另外函数默认将 `header` 选项设定为 `TRUE`,即有列名,所以我们也无需指定它,因而读取操作就被简化了:
```{r}
cars2 <- read.csv(file = "data/data-import/mtcars.csv")
head(cars2)
```
上述的读取操作基于 R 内置的函数实现,无需载入任何其他三方包就可以完成数据的读入,这在针对小型数据(集)或者计算机条件受限时(无法安装和使用三方包)非常有用。在这种常规以符号分隔的文件数据读取方面,我必须提及 2 个三方包:**readr** 和 **data.table**。它们都能以极快的速度读取大内存数据,推荐读者作为常规导入操作的解决方案。
[**tidyverse**](https://github.com/tidyverse/) 是 R 语言大神 [Hadley](https://github.com/hadley)(以 **ggplot2** 作者闻名于世) 组织构建的一整套数据分析生态系统,包括读入、处理、建模与可视化等, [**readr**](https://github.com/tidyverse/readr) 包是 **tidyverse** 的一部分,用于完成数据的导入工作。
[**data.table**](https://github.com/Rdatatable/data.table) 包以 R 社区最快的数据读取和处理操作而著名,它主要是提供了一个增强版的数据框 `data.table`。
根据 **readr** 包官方文档介绍,**readr** 包通常比 **data.table** 包慢大概 1.2~2 倍左右。不过它们各有特点,**readr** 包被设计用于更为常规的数据读取操作,而 **data.table** 的目标则是尽量的快。
为了体现上述两个包和内置函数的差别,这里我们构造一个较大的 CSV 文件进行简单的测试:
```{r}
huge_car <- cars[rep(1:32, 10000), ]
```
把这个数据先保存下来,然后再利用不同的工具进行导入。
```{r}
temp_csv <- tempfile(fileext = ".csv")
readr::write_csv(huge_car, path = temp_csv)
```
现在我们分别使用 `system.time()` 测试下 R 内置的 `read.csv()` 函数与 **readr** 提供的 `read_csv()` 以及 **data.table** 提供的 `fread()` 的读取效率。
```{r}
time1 <- system.time(
z1 <- read.csv(temp_csv)
)
time1
```
```{r, message=FALSE}
library(readr)
time2 <- system.time(
z2 <- read_csv(temp_csv)
)
time2
```
```{r, message=FALSE}
library(data.table)
time3 <- system.time(
z3 <- fread(temp_csv)
)
time3
```
上面我们使用了 `r nrow(huge_car)` 行数据进行测试,在我的计算机上,内置的函数 `read.csv()` 总共花费了 `r time1[3]`s,**readr** 的 `read_csv()` 花费了 `r time2[3]`s,而 **data.table** 的 `fread()` 仅用了 `r time3[3]`s 左右。**好的、适合的工具可以帮助我们极大地提升工作效率**。
如果我们进一步观察几种不同方式导入的数据格式,就会发现它们有些不太相同。
```{r}
z1
z2
z3
```
这个所谓的不同与结构中存储的数据信息无关,而是在不同的设计上。我们检查一下它们的类属性:
```{r}
class(z1)
class(z2)
class(z3)
```
不难看到这 3 个对象存在共有的类名 `data.frame`。我们使用内置函数读入数据仅包含该类名,而后两者还存在其他的类名,这是因为后两者继承了 `data.frame`。简单地说,后两者是增强版的 `data.frame`,它们赋予了不同于 R 内置数据框的特性,读者可以观察到的最明显的区别就是它们打印信息的方式不同。
通常地说,我们将对象 `z2` 称为 `tibble`,因为它是由 [**tibble**](https://github.com/tidyverse/tibble) 包创建的类,是 **tidyverse** 系列包的数据结构基础,设计者称它为现代的 `data.frame`,在基础的使用方式上与 `data.frame` 并无不同,读者可以通过官方文档阅读更为详细的介绍。
对象 `z3` 则常被称为 `data.table`,因为它的类名和包名都是 `data.table`.
`tibble` 和 `data.table` 都有一系列强大高效的数据处理方法和操作,它们将在第 \@ref(clean) 章进行介绍。
### TSV 与其他 CSV 变体
另一种流行的数据存储格式是 TSV,与 CSV 唯一不同的是 TSV 使用制表符 `\t` 作为内容的分隔符。TSV 文件除了以 `.tsv` 作为文件拓展名,也常用 `.txt` 作为文件拓展名(并不是所有的 `.txt` 文件都是以制表符分隔)。
通过将 `read.table()` 函数中的 `sep` 参数设定为制表符,我们可以轻松完成该格式文件内容的读取:
```{r}
mt <- read.table("data/data-import/mtcars.tsv", sep = "\t", header = TRUE)
mt
```
而 **readr** 包提供了一系列的 `read_*()` 函数,方便用户将常见数据文件导入为 `tibble`:
```{r}
mt2 <- read_tsv("data/data-import/mtcars.tsv")
mt2
```
使用 **data.table** 则更为轻松,因为所有分隔格式的数据都可以通过 `fread()` 读取完成:
```{r}
mt3 <- fread("data/data-import/mtcars.tsv")
mt3
```
为什么 `fread()` 没有设置分隔符却可以导入 CSV 和 TSV 文件?其中的巧妙在于该函数的内部会自动检测数据文件内的分隔符号。
通过查看该函数的参数,我们可以看到 `sep = "auto"`,这个参数我们可以人为指定,以适应不同的需求场景。
```{r}
args(fread)
```
上面展示出 `fread()` 存在众多的参数设置。R 的易用与友好在于此,众多 R 包提供的函数大多已经为用户恰当地设置好了默认选项,用户无需经过繁琐的操作即可快速获取所需的结果。
在学习了如何导入 TSV 文件后,我们应该能够归纳出 CSV、TSV 本质上是一类数据格式文件。例如,我们也可以使用分号 `;`(西方不少国家的逗号分隔文件就是以分号分隔的文件)。我们统一以 CSV 作为代表,将其他类似格式文件称为 CSV 变体。
## Excel
**Excel** 是知名的微软 Office 套件之一,提供了日常的表格数据处理方案。尽管大多数数据分析人员不会产生和不想处理 Excel 文件,但由于它随着微软系统的流行而被广泛使用,因此读入 Excel 中的数据成为数据处理无法避免的日常工作任务之一。
R 中有诸多拓展包可以导入 Excel 中的数据,其中最为推荐的就是本部分介绍的 [**readxl**](https://github.com/tidyverse/readxl) 包。
使用该包,导入 Excel 中的数据可以像读入 CSV 文件一样简单。
```{r}
library(readxl)
mt_excel <- read_excel("data/data-import/mtcars.xlsx")
head(mt_excel)
```
`read_excel()` 同时支持 `.xls` 和 `xlsx` 两者文件格式。
Excel 文件支持多个表格(Sheet),这种情况下默认第 1 个 Sheet 的内容会被读入。通过简单的设置,读者也可以轻松导入任意 Sheet 的内容。
下面通过 **readxl** 包的官方示例展示这一过程。
我们先查看下该包提供的示例数据:
```{r}
readxl_example()
```
选一个文件并查看其所在的路径:
```{r}
readxl_example("datasets.xlsx")
# 将路径保存备用
excel_path <- readxl_example("datasets.xlsx")
```
如果将路径传入 `excel_sheets()` 函数,读者可以获取文件中存在的 Sheets。
```{r}
excel_sheets(excel_path)
```
然后依旧是使用 `read_excel()` 函数,设定想要读入的 Sheet 名字即可。
```{r}
iris <- read_excel(excel_path, sheet = "iris")
head(iris)
```
关于函数的更多用法读者可以通过 `?read_excel()` 查看。
有时候人们会在同一个 Excel Sheet 中放置多个表格信息,这种情况无法通过简单地使用 **readxl** 包读入数据。只要数据是有规律的,读者可以尝试下通过 [**tidycells**](https://github.com/r-rudra/tidycells) 包导入数据。
```{r, echo=FALSE, fig.cap="tidycells 包示例数据格式", fig.align="center"}
knitr::include_graphics("https://github.com/r-rudra/tidycells/raw/master/vignettes/ext/marks.png")
```
具体的使用请读者参照 [README](https://github.com/r-rudra/tidycells/blob/master/README.md) 进行学习。
## JSON
JSON 是目前使用最广泛的网络数据交换格式,它非常轻量,易于阅读和编写。与 R 不同,它有自己的数据结构存储数值、字符串、布尔值、数组等信息。关于它的介绍请读者阅读[官网文档](https://www.json.org/json-zh.html),这里我们仅介绍如何将其导入 R 中以便于分析。
R 中最流行的 JSON 数据解析包是 **jsonlite**,它非常轻量,但功能强大:支持 JSON 格式数据的解析以及将 R 中的数据结构编码为 JSON 格式。
下面的例子展示了 R 中常见数据结构解析为 JSON 格式后的结果:
```{r}
jsonlite::toJSON(letters)
jsonlite::toJSON(c(a = 1L, b = 2.0))
jsonlite::toJSON(data.frame(a = 1:3, b = 2:4))
jsonlite::toJSON(list(a = 1L, b = 2:5, c = c(TRUE, FALSE), d = NULL))
```
使用 `write_json()` 函数即可将 JSON 数据保存到本地:
```{r}
jsonlite::write_json(list(a = 1L, b = 2:5, c = c(TRUE, FALSE), d = NULL),
path = "data/data-import/example.json")
```
有了示例的 JSON 文件,接下来我们看如何将其导入 R 中:
```{r}
jsonlite::read_json("data/data-import/example.json")
```
不难发现,JSON 文件中的信息被保存到了列表之中,但结构过于复杂,我们可以用 `simplifyVector` 选项进行简化:
```{r}
jsonlite::read_json("data/data-import/example.json", simplifyVector = TRUE)
```
如果 JSON 文件结构非常复杂,那么导入 R 以后形成的列表也会极其复杂,读者需要良好的列表处理功底才能进行处理。
## R 数据文件
使用 R 自身提供的数据存储格式是一种有效且常见的方式,非常适合数据分析项目。相比于通用格式如 CSV,它可以避免外部程序的删改,且有效利用磁盘存储空间。
R 内置的格式有 RData 和 RDS 两种。
### RData
RData 可能是读者最常见的一种 R 存储格式,它用于保存用户空间一个或多个对象。存储时使用 `save()` 函数,导入时使用 `load()` 函数。
```{r}
## 清除空间所有对象
rm(list = ls())
## 创建两个对象
d1 <- mtcars
d2 <- mtcars
ls()
```
上面我们在空间创建了 2 个示例对象,我们将其保存如下:
```{r}
save(d1, d2, file = "data/data-import/mtcars.RData")
```
这里注意一定要写明 `file = `,这是一个新手常犯的错误,如果不指明参数名称,路径将被看作一个对象,这会导致报错:
```{r, error=TRUE}
save(d1, d2, "data/data-import/mtcars.RData")
```
导入时这一操作则是非必需的:
```{r}
rm(list = ls())
load("data/data-import/mtcars.RData")
ls()
```
这里我们之前保存的两个对象都会被直接导入环境空间,对象名称也不会改变。如果读者想要迅速保存当前环境所有的对象,有效的方式是使用 `save.image()` 函数。
### RDS
有时候我们想要保存单个的对象,且在导入时可以直接重命名该对象,这时我们可以用 RDS 格式。存储时使用 `saveRDS()` 函数,导入时使用 `readRDS()` 函数。
```{r}
saveRDS(mtcars, file = "data/data-import/mtcars.rds")
mtcars_rename <- readRDS("data/data-import/mtcars.rds")
head(mtcars_rename)
```
## 常见问题与方案
除了本节目前罗列的问题,读者在学习本章内容时遇到的其他问题都可以通过 [GitHub Issue](https://github.com/ShixiangWang/geek-r-tutorial/issues) 提出和进行讨论。如果读者提出的是通性问题,将增补到该节。
### 通过键盘和剪贴板载入数据
如果读者想要从键盘读取数据,我们需要使用到标准输入 `stdin()` 函数进行辅助。
```{r, eval=FALSE}
data <- read.table(stdin())
## 运行后输入文本
```
这种操作并不好用,稍微好一点的是先将数据拷贝到剪切板,然后使用下面的命令读入:
```{r, eval=FALSE}
data <- read.table('clipboard', header=TRUE)
```
### 逐行读取数据
如果读者想要简单地从用户输入交互地读取一些信息,`readline()` 是一个好办法,例如:
```r
> readline("输入年龄:")
输入年龄:22
[1] "22"
```
如果是想要将文件的内容按行的方式读取进来,需要使用 `readLines()` 函数。
下面是一个来自 `readLines()` 的官方示例:
```{r}
fil <- tempfile(fileext = ".data")
cat("TITLE extra line", "2 3 5 7", "", "11 13 17", file = fil,
sep = "\n")
readLines(fil, n = -1)
unlink(fil) # 清理文件
```
### 读取等宽格式数据
有时候我们会碰到数据的列宽度是固定的,我们称它为定宽文本文件,像下面这样:
```
First Last Sex Number
Currer Bell F 2
Dr. Seuss M 49
"" Student NA 21
```
导入的方法这里推荐使用 2 种:
1. R 内置的 `read.fwf()` 函数。
2. **readr** 包提供的 `read_fwf()`。
详细的使用方法请读者参考文档学习。