第 3 章 数据导入
在掌握一定的 R 编程技能后,我们开始迈入数据分析的殿堂。大部分数据分析事务的数据都不是通过 R 创建,而是来自于各种数据收集软硬件、渠道,包括 Excel、网络等。本章聚焦于如何将数据导入 R 中以便于开始数据分析。对于本章的内容,读者既可以系统地从头到尾深入阅读学习,也可以根据自己实际工作需要或时间限制选择一些重点或感兴趣内容进行掌握。
本章的重点内容包括符号分隔文件、Excel 文件、JSON 文件以及 R 支持的数据格式 RData 和 RDS,其他格式的数据将放到本章【常见问题与方案】一节作为补充介绍。
3.1 符号分隔文件
符号分隔文件是最最常用的数据文件格式,知道如何导入它是读者的必备技能。这里的符号泛指一切可以用作数据内容分隔的符号,常见的包括逗号(,
),制表符(\t
),我们常称它们为 CSV 文件和 TSV 文件。
3.1.1 CSV
CSV 文件常以 .csv
作为文件拓展名,比如接下来我们会导入的 mtcars.csv
。注意,文件拓展名并不会影响文件本身的内容,它只是用来方便帮助人们快速的了解内容格式,另外支持其他一些程序的自动解读(在你的计算机上,不同的文件拓展名系统软件可以会对它们使用不同的图标,如 Word 文档和 PPT)。
一般规整的数据以行作为观测,列作为记录(变量、域),如一个班级同学的成绩。
student,chinese,math,english
stu1,99,100,98
stu2,60,50,88
R 内置了 read.table()
函数用于便捷导入各类分隔符的文件。下面我们直接将这个成绩记录信息以文本的形式传入,结果会返回一个数据框:
stu <- read.table(text = "
student,chinese,math,english
stu1,99,100,98
stu2,60,50,88
", header = TRUE, sep = ",")
stu
#> student chinese math english
#> 1 stu1 99 100 98
#> 2 stu2 60 50 88
class(stu)
#> [1] "data.frame"
实际上要处理的数据并不会这么的少,一般会以文件的形式存储在计算机磁盘中。下面我们依旧使用 read.table()
函数完成 CSV 文件数据的导入。
操作完成了,我们检查下头几行:
head(cars)
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> 2 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> 3 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#> 4 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#> 5 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#> 6 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
除了使用 read.table()
,我们还可以使用内置的 read.csv()
函数完成 CSV 文件的读入。与 read.table()
不同的时,我们无需再指定分隔符,因为该函数本身就是为了 CSV 文件设计的。另外函数默认将 header
选项设定为 TRUE
,即有列名,所以我们也无需指定它,因而读取操作就被简化了:
cars2 <- read.csv(file = "data/data-import/mtcars.csv")
head(cars2)
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> 2 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> 3 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#> 4 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#> 5 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#> 6 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
上述的读取操作基于 R 内置的函数实现,无需载入任何其他三方包就可以完成数据的读入,这在针对小型数据(集)或者计算机条件受限时(无法安装和使用三方包)非常有用。在这种常规以符号分隔的文件数据读取方面,我必须提及 2 个三方包:readr 和 data.table。它们都能以极快的速度读取大内存数据,推荐读者作为常规导入操作的解决方案。
tidyverse 是 R 语言大神 Hadley(以 ggplot2 作者闻名于世) 组织构建的一整套数据分析生态系统,包括读入、处理、建模与可视化等, readr 包是 tidyverse 的一部分,用于完成数据的导入工作。
data.table 包以 R 社区最快的数据读取和处理操作而著名,它主要是提供了一个增强版的数据框 data.table
。
根据 readr 包官方文档介绍,readr 包通常比 data.table 包慢大概 1.2~2 倍左右。不过它们各有特点,readr 包被设计用于更为常规的数据读取操作,而 data.table 的目标则是尽量的快。
为了体现上述两个包和内置函数的差别,这里我们构造一个较大的 CSV 文件进行简单的测试:
把这个数据先保存下来,然后再利用不同的工具进行导入。
现在我们分别使用 system.time()
测试下 R 内置的 read.csv()
函数与 readr 提供的 read_csv()
以及 data.table 提供的 fread()
的读取效率。
library(readr)
time2 <- system.time(
z2 <- read_csv(temp_csv)
)
time2
#> 用户 系统 流逝
#> 0.287 0.008 0.294
library(data.table)
time3 <- system.time(
z3 <- fread(temp_csv)
)
time3
#> 用户 系统 流逝
#> 0.041 0.005 0.047
上面我们使用了 320000 行数据进行测试,在我的计算机上,内置的函数 read.csv()
总共花费了 0.917s,readr 的 read_csv()
花费了 0.294s,而 data.table 的 fread()
仅用了 0.047s 左右。好的、适合的工具可以帮助我们极大地提升工作效率。
如果我们进一步观察几种不同方式导入的数据格式,就会发现它们有些不太相同。
z1
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
#> 2 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
#> 3 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
#> 4 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#> 5 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
#> 6 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#> 7 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
#> 8 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
#> 9 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
#> 10 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
#> 11 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
#> 12 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
#> 13 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
#> 14 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
#> 15 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
#> 16 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
#> 17 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
#> 18 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
#> 19 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
#> 20 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
#> 21 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
#> 22 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
#> 23 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
#> 24 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
#> 25 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
#> 26 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
#> 27 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
#> 28 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
#> 29 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
#> 30 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
#> 31 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
#> 32 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
#> 33 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
#> 34 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
#> 35 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
#> 36 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#> 37 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
#> 38 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#> 39 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
#> 40 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
#> 41 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
#> 42 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
#> 43 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
#> 44 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
#> 45 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
#> 46 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
#> 47 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
#> 48 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
#> 49 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
#> 50 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
#> 51 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
#> 52 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
#> 53 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
#> 54 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
#> 55 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
#> 56 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
#> 57 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
#> 58 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
#> 59 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
#> 60 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
#> 61 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
#> 62 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
#> 63 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
#> 64 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
#> 65 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
#> 66 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
#> 67 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
#> 68 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#> 69 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
#> 70 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#> 71 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
#> 72 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
#> 73 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
#> 74 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
#> 75 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
#> 76 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
#> 77 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
#> 78 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
#> 79 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
#> 80 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
#> 81 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
#> 82 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
#> 83 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
#> 84 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
#> 85 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
#> 86 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
#> 87 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
#> 88 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
#> 89 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
#> 90 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
#> [ reached 'max' / getOption("max.print") -- omitted 319910 rows ]
z2
#> # A tibble: 320,000 x 11
#> mpg cyl disp hp drat wt qsec vs
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 21 6 160 110 3.9 2.62 16.5 0
#> 2 21 6 160 110 3.9 2.88 17.0 0
#> 3 22.8 4 108 93 3.85 2.32 18.6 1
#> 4 21.4 6 258 110 3.08 3.22 19.4 1
#> 5 18.7 8 360 175 3.15 3.44 17.0 0
#> 6 18.1 6 225 105 2.76 3.46 20.2 1
#> 7 14.3 8 360 245 3.21 3.57 15.8 0
#> 8 24.4 4 147. 62 3.69 3.19 20 1
#> 9 22.8 4 141. 95 3.92 3.15 22.9 1
#> 10 19.2 6 168. 123 3.92 3.44 18.3 1
#> # … with 319,990 more rows, and 3 more variables:
#> # am <dbl>, gear <dbl>, carb <dbl>
z3
#> mpg cyl disp hp drat wt qsec vs am gear
#> 1: 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4
#> 2: 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4
#> 3: 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4
#> 4: 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3
#> 5: 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3
#> ---
#> 319996: 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5
#> 319997: 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5
#> 319998: 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5
#> 319999: 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5
#> 320000: 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4
#> carb
#> 1: 4
#> 2: 4
#> 3: 1
#> 4: 1
#> 5: 2
#> ---
#> 319996: 2
#> 319997: 4
#> 319998: 6
#> 319999: 8
#> 320000: 2
这个所谓的不同与结构中存储的数据信息无关,而是在不同的设计上。我们检查一下它们的类属性:
class(z1)
#> [1] "data.frame"
class(z2)
#> [1] "spec_tbl_df" "tbl_df" "tbl"
#> [4] "data.frame"
class(z3)
#> [1] "data.table" "data.frame"
不难看到这 3 个对象存在共有的类名 data.frame
。我们使用内置函数读入数据仅包含该类名,而后两者还存在其他的类名,这是因为后两者继承了 data.frame
。简单地说,后两者是增强版的 data.frame
,它们赋予了不同于 R 内置数据框的特性,读者可以观察到的最明显的区别就是它们打印信息的方式不同。
通常地说,我们将对象 z2
称为 tibble
,因为它是由 tibble 包创建的类,是 tidyverse 系列包的数据结构基础,设计者称它为现代的 data.frame
,在基础的使用方式上与 data.frame
并无不同,读者可以通过官方文档阅读更为详细的介绍。
对象 z3
则常被称为 data.table
,因为它的类名和包名都是 data.table
.
tibble
和 data.table
都有一系列强大高效的数据处理方法和操作,它们将在第 4 章进行介绍。
3.1.2 TSV 与其他 CSV 变体
另一种流行的数据存储格式是 TSV,与 CSV 唯一不同的是 TSV 使用制表符 \t
作为内容的分隔符。TSV 文件除了以 .tsv
作为文件拓展名,也常用 .txt
作为文件拓展名(并不是所有的 .txt
文件都是以制表符分隔)。
通过将 read.table()
函数中的 sep
参数设定为制表符,我们可以轻松完成该格式文件内容的读取:
mt <- read.table("data/data-import/mtcars.tsv", sep = "\t", header = TRUE)
mt
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
#> 2 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
#> 3 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
#> 4 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#> 5 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
#> 6 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#> 7 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
#> 8 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
#> 9 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
#> 10 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
#> 11 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
#> 12 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
#> 13 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
#> 14 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
#> 15 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
#> 16 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
#> 17 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
#> 18 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
#> 19 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
#> 20 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
#> 21 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
#> 22 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
#> 23 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
#> 24 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
#> 25 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
#> 26 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
#> 27 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
#> 28 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
#> 29 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
#> 30 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
#> 31 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
#> 32 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
而 readr 包提供了一系列的 read_*()
函数,方便用户将常见数据文件导入为 tibble
:
mt2 <- read_tsv("data/data-import/mtcars.tsv")
mt2
#> # A tibble: 32 x 11
#> mpg cyl disp hp drat wt qsec vs
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 21 6 160 110 3.9 2.62 16.5 0
#> 2 21 6 160 110 3.9 2.88 17.0 0
#> 3 22.8 4 108 93 3.85 2.32 18.6 1
#> 4 21.4 6 258 110 3.08 3.22 19.4 1
#> 5 18.7 8 360 175 3.15 3.44 17.0 0
#> 6 18.1 6 225 105 2.76 3.46 20.2 1
#> 7 14.3 8 360 245 3.21 3.57 15.8 0
#> 8 24.4 4 147. 62 3.69 3.19 20 1
#> 9 22.8 4 141. 95 3.92 3.15 22.9 1
#> 10 19.2 6 168. 123 3.92 3.44 18.3 1
#> # … with 22 more rows, and 3 more variables:
#> # am <dbl>, gear <dbl>, carb <dbl>
使用 data.table 则更为轻松,因为所有分隔格式的数据都可以通过 fread()
读取完成:
mt3 <- fread("data/data-import/mtcars.tsv")
mt3
#> mpg cyl disp hp drat wt qsec vs am gear
#> 1: 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4
#> 2: 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4
#> 3: 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4
#> 4: 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3
#> 5: 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3
#> 6: 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3
#> 7: 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3
#> 8: 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4
#> 9: 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4
#> 10: 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4
#> 11: 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4
#> 12: 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3
#> 13: 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3
#> 14: 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3
#> 15: 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3
#> 16: 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3
#> 17: 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3
#> 18: 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4
#> 19: 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4
#> 20: 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4
#> 21: 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3
#> 22: 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3
#> 23: 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3
#> 24: 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3
#> 25: 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3
#> 26: 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4
#> 27: 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5
#> 28: 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5
#> 29: 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5
#> 30: 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5
#> 31: 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5
#> 32: 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4
#> mpg cyl disp hp drat wt qsec vs am gear
#> carb
#> 1: 4
#> 2: 4
#> 3: 1
#> 4: 1
#> 5: 2
#> 6: 1
#> 7: 4
#> 8: 2
#> 9: 2
#> 10: 4
#> 11: 4
#> 12: 3
#> 13: 3
#> 14: 3
#> 15: 4
#> 16: 4
#> 17: 4
#> 18: 1
#> 19: 2
#> 20: 1
#> 21: 1
#> 22: 2
#> 23: 2
#> 24: 4
#> 25: 2
#> 26: 1
#> 27: 2
#> 28: 2
#> 29: 4
#> 30: 6
#> 31: 8
#> 32: 2
#> carb
为什么 fread()
没有设置分隔符却可以导入 CSV 和 TSV 文件?其中的巧妙在于该函数的内部会自动检测数据文件内的分隔符号。
通过查看该函数的参数,我们可以看到 sep = "auto"
,这个参数我们可以人为指定,以适应不同的需求场景。
args(fread)
#> function (input = "", file = NULL, text = NULL, cmd = NULL, sep = "auto",
#> sep2 = "auto", dec = ".", quote = "\"", nrows = Inf, header = "auto",
#> na.strings = getOption("datatable.na.strings", "NA"), stringsAsFactors = FALSE,
#> verbose = getOption("datatable.verbose", FALSE), skip = "__auto__",
#> select = NULL, drop = NULL, colClasses = NULL, integer64 = getOption("datatable.integer64",
#> "integer64"), col.names, check.names = FALSE, encoding = "unknown",
#> strip.white = TRUE, fill = FALSE, blank.lines.skip = FALSE,
#> key = NULL, index = NULL, showProgress = getOption("datatable.showProgress",
#> interactive()), data.table = getOption("datatable.fread.datatable",
#> TRUE), nThread = getDTthreads(verbose), logical01 = getOption("datatable.logical01",
#> FALSE), keepLeadingZeros = getOption("datatable.keepLeadingZeros",
#> FALSE), yaml = FALSE, autostart = NA, tmpdir = tempdir())
#> NULL
上面展示出 fread()
存在众多的参数设置。R 的易用与友好在于此,众多 R 包提供的函数大多已经为用户恰当地设置好了默认选项,用户无需经过繁琐的操作即可快速获取所需的结果。
在学习了如何导入 TSV 文件后,我们应该能够归纳出 CSV、TSV 本质上是一类数据格式文件。例如,我们也可以使用分号 ;
(西方不少国家的逗号分隔文件就是以分号分隔的文件)。我们统一以 CSV 作为代表,将其他类似格式文件称为 CSV 变体。
3.2 Excel
Excel 是知名的微软 Office 套件之一,提供了日常的表格数据处理方案。尽管大多数数据分析人员不会产生和不想处理 Excel 文件,但由于它随着微软系统的流行而被广泛使用,因此读入 Excel 中的数据成为数据处理无法避免的日常工作任务之一。
R 中有诸多拓展包可以导入 Excel 中的数据,其中最为推荐的就是本部分介绍的 readxl 包。
使用该包,导入 Excel 中的数据可以像读入 CSV 文件一样简单。
library(readxl)
mt_excel <- read_excel("data/data-import/mtcars.xlsx")
head(mt_excel)
#> # A tibble: 6 x 11
#> mpg cyl disp hp drat wt qsec vs am
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 21 6 160 110 3.9 2.62 16.5 0 1
#> 2 21 6 160 110 3.9 2.88 17.0 0 1
#> 3 22.8 4 108 93 3.85 2.32 18.6 1 1
#> 4 21.4 6 258 110 3.08 3.22 19.4 1 0
#> 5 18.7 8 360 175 3.15 3.44 17.0 0 0
#> 6 18.1 6 225 105 2.76 3.46 20.2 1 0
#> # … with 2 more variables: gear <dbl>, carb <dbl>
read_excel()
同时支持 .xls
和 xlsx
两者文件格式。
Excel 文件支持多个表格(Sheet),这种情况下默认第 1 个 Sheet 的内容会被读入。通过简单的设置,读者也可以轻松导入任意 Sheet 的内容。
下面通过 readxl 包的官方示例展示这一过程。
我们先查看下该包提供的示例数据:
readxl_example()
#> [1] "clippy.xls" "clippy.xlsx" "datasets.xls"
#> [4] "datasets.xlsx" "deaths.xls" "deaths.xlsx"
#> [7] "geometry.xls" "geometry.xlsx" "type-me.xls"
#> [10] "type-me.xlsx"
选一个文件并查看其所在的路径:
readxl_example("datasets.xlsx")
#> [1] "/Users/wsx/R_Library/readxl/extdata/datasets.xlsx"
# 将路径保存备用
excel_path <- readxl_example("datasets.xlsx")
如果将路径传入 excel_sheets()
函数,读者可以获取文件中存在的 Sheets。
然后依旧是使用 read_excel()
函数,设定想要读入的 Sheet 名字即可。
iris <- read_excel(excel_path, sheet = "iris")
head(iris)
#> # A tibble: 6 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 5.1 3.5 1.4 0.2
#> 2 4.9 3 1.4 0.2
#> 3 4.7 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 5 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
#> # … with 1 more variable: Species <chr>
关于函数的更多用法读者可以通过 ?read_excel()
查看。
有时候人们会在同一个 Excel Sheet 中放置多个表格信息,这种情况无法通过简单地使用 readxl 包读入数据。只要数据是有规律的,读者可以尝试下通过 tidycells 包导入数据。
具体的使用请读者参照 README 进行学习。
3.3 JSON
JSON 是目前使用最广泛的网络数据交换格式,它非常轻量,易于阅读和编写。与 R 不同,它有自己的数据结构存储数值、字符串、布尔值、数组等信息。关于它的介绍请读者阅读官网文档,这里我们仅介绍如何将其导入 R 中以便于分析。
R 中最流行的 JSON 数据解析包是 jsonlite,它非常轻量,但功能强大:支持 JSON 格式数据的解析以及将 R 中的数据结构编码为 JSON 格式。
下面的例子展示了 R 中常见数据结构解析为 JSON 格式后的结果:
jsonlite::toJSON(letters)
#> ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
jsonlite::toJSON(c(a = 1L, b = 2.0))
#> [1,2]
jsonlite::toJSON(data.frame(a = 1:3, b = 2:4))
#> [{"a":1,"b":2},{"a":2,"b":3},{"a":3,"b":4}]
jsonlite::toJSON(list(a = 1L, b = 2:5, c = c(TRUE, FALSE), d = NULL))
#> {"a":[1],"b":[2,3,4,5],"c":[true,false],"d":{}}
使用 write_json()
函数即可将 JSON 数据保存到本地:
jsonlite::write_json(list(a = 1L, b = 2:5, c = c(TRUE, FALSE), d = NULL),
path = "data/data-import/example.json")
有了示例的 JSON 文件,接下来我们看如何将其导入 R 中:
jsonlite::read_json("data/data-import/example.json")
#> $a
#> $a[[1]]
#> [1] 1
#>
#>
#> $b
#> $b[[1]]
#> [1] 2
#>
#> $b[[2]]
#> [1] 3
#>
#> $b[[3]]
#> [1] 4
#>
#> $b[[4]]
#> [1] 5
#>
#>
#> $c
#> $c[[1]]
#> [1] TRUE
#>
#> $c[[2]]
#> [1] FALSE
#>
#>
#> $d
#> named list()
不难发现,JSON 文件中的信息被保存到了列表之中,但结构过于复杂,我们可以用 simplifyVector
选项进行简化:
jsonlite::read_json("data/data-import/example.json", simplifyVector = TRUE)
#> $a
#> [1] 1
#>
#> $b
#> [1] 2 3 4 5
#>
#> $c
#> [1] TRUE FALSE
#>
#> $d
#> named list()
如果 JSON 文件结构非常复杂,那么导入 R 以后形成的列表也会极其复杂,读者需要良好的列表处理功底才能进行处理。
3.4 R 数据文件
使用 R 自身提供的数据存储格式是一种有效且常见的方式,非常适合数据分析项目。相比于通用格式如 CSV,它可以避免外部程序的删改,且有效利用磁盘存储空间。 R 内置的格式有 RData 和 RDS 两种。
3.4.1 RData
RData 可能是读者最常见的一种 R 存储格式,它用于保存用户空间一个或多个对象。存储时使用 save()
函数,导入时使用 load()
函数。
上面我们在空间创建了 2 个示例对象,我们将其保存如下:
这里注意一定要写明 file =
,这是一个新手常犯的错误,如果不指明参数名称,路径将被看作一个对象,这会导致报错:
save(d1, d2, "data/data-import/mtcars.RData")
#> Error in save(d1, d2, "data/data-import/mtcars.RData"): 目标对象'data/data-import/mtcars.RData'不存在
导入时这一操作则是非必需的:
这里我们之前保存的两个对象都会被直接导入环境空间,对象名称也不会改变。如果读者想要迅速保存当前环境所有的对象,有效的方式是使用 save.image()
函数。
3.4.2 RDS
有时候我们想要保存单个的对象,且在导入时可以直接重命名该对象,这时我们可以用 RDS 格式。存储时使用 saveRDS()
函数,导入时使用 readRDS()
函数。
saveRDS(mtcars, file = "data/data-import/mtcars.rds")
mtcars_rename <- readRDS("data/data-import/mtcars.rds")
head(mtcars_rename)
#> mpg cyl disp hp drat wt qsec
#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46
#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02
#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61
#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44
#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02
#> Valiant 18.1 6 225 105 2.76 3.460 20.22
#> vs am gear carb
#> Mazda RX4 0 1 4 4
#> Mazda RX4 Wag 0 1 4 4
#> Datsun 710 1 1 4 1
#> Hornet 4 Drive 1 0 3 1
#> Hornet Sportabout 0 0 3 2
#> Valiant 1 0 3 1
3.5 常见问题与方案
除了本节目前罗列的问题,读者在学习本章内容时遇到的其他问题都可以通过 GitHub Issue 提出和进行讨论。如果读者提出的是通性问题,将增补到该节。
3.5.1 通过键盘和剪贴板载入数据
如果读者想要从键盘读取数据,我们需要使用到标准输入 stdin()
函数进行辅助。
这种操作并不好用,稍微好一点的是先将数据拷贝到剪切板,然后使用下面的命令读入:
3.5.2 逐行读取数据
如果读者想要简单地从用户输入交互地读取一些信息,readline()
是一个好办法,例如:
如果是想要将文件的内容按行的方式读取进来,需要使用 readLines()
函数。
下面是一个来自 readLines()
的官方示例:
3.5.3 读取等宽格式数据
有时候我们会碰到数据的列宽度是固定的,我们称它为定宽文本文件,像下面这样:
First Last Sex Number
Currer Bell F 2
Dr. Seuss M 49
"" Student NA 21
导入的方法这里推荐使用 2 种:
- R 内置的
read.fwf()
函数。 - readr 包提供的
read_fwf()
。
详细的使用方法请读者参考文档学习。