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 文件数据的导入。
cars <- read.table(file = "data/data-import/mtcars.csv", header = TRUE, sep = ",")
操作完成了,我们检查下头几行:
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 文件进行简单的测试:
huge_car <- cars[rep(1:32, 10000), ]
把这个数据先保存下来,然后再利用不同的工具进行导入。
temp_csv <- tempfile(fileext = ".csv")
readr::write_csv(huge_car, path = temp_csv)
现在我们分别使用 system.time() 测试下 R 内置的 read.csv() 函数与 readr 提供的 read_csv() 以及 data.table 提供的 fread() 的读取效率。
time1 <- system.time(
z1 <- read.csv(temp_csv)
)
time1
#> 用户 系统 流逝
#> 0.896 0.021 0.917
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
#>
#> 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
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 章进行介绍。