数据表相对于数据框,运行速度更快,使用时也更加方便,实际使用时完全可以取代数据框,数据表有两个优势,第一是运行速度更快,第二是运算十分便利。
我们先安装数据表
install.packages("data.table")
library(data.table)
一、创建数据表
跟数据框的创建手法一致,我们可以使用data.table()来创建数据框,此外,我们可以使用as.data.frame()/as.data.table()函数可以在数据框和数据表之间转换
例如,我们将鸢尾花数据集转换为数据表
> (iris.table<-as.data.table(iris))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<num> <num> <num> <num> <fctr>
1: 5.1 3.5 1.4 0.2 setosa
2: 4.9 3.0 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.0 3.6 1.4 0.2 setosa
---
146: 6.7 3.0 5.2 2.3 virginica
147: 6.3 2.5 5.0 1.9 virginica
148: 6.5 3.0 5.2 2.0 virginica
149: 6.2 3.4 5.4 2.3 virginica
150: 5.9 3.0 5.1 1.8 virginica
我们也可以发现一个特征,就是数据表输出的时候是不会一口气将所有数据输出的,只会输出前面和尾部几行,如果要多输出几行,可以指定行号,例如(iris_table[1:n,])
跟数据框一样,数据表也可以原地创建
> (x<-data.table(x=c(1,2,3),y=c("a","b","c")))
x y
<num> <char>
1: 1 a
2: 2 b
3: 3 c
根据下列代码我们可以得知,数据表类包含数据框data.frame,所以处理数据框所用的函数同样适用于数据表,如果出现了问题,直接用转换函数转换为数据框即可。
> class(data.table())
[1] "data.table" "data.frame"
我们可以使用tables()函数来预览目前创建的所有数据表的简略信息,同时也会告诉你内存消耗量
> tables()
NAME NROW NCOL MB COLS KEY
1: iris.table 150 5 0 Sepal.Length,Sepal.Width,Petal.Length,Petal.Width,Species
2: x 3 2 0 x,y
Total: 0MB using type_size
二、数据访问与分组运算
数据表中的数据采用[行,表达式,选项]的形式进行访问,其中行可以是行号,也可以是用来选择某些行的逻辑值
> DT<-as.data.table(iris)
> DT[1,]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<num> <num> <num> <num> <fctr>
1: 5.1 3.5 1.4 0.2 setosa
> DT[DT$Species=="setosa"]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<num> <num> <num> <num> <fctr>
1: 5.1 3.5 1.4 0.2 setosa
2: 4.9 3.0 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
在访问数据表时,[]的第二参数是列名或与列名有关的表达式,若希望得到多个列,可以使用列表罗列,例如,我们向访问iris数据集的第一行的Sepal.length列
> DT[1,Sepal.Length]
[1] 5.1
我们想要得到数据表的多个列,可以使用列表来呈现
> DT[1,list(Sepal.Length,Species)]
Sepal.Length Species
<num> <fctr>
1: 5.1 setosa
同时,第二个参数也可以是包含列名的表达式,表达式中含有多种运算,例如,我们计算Sepal.Length平均值
> DT[,mean(Sepal.Length)]
[1] 5.843333
如果我们想将数据表中将[]的第二个参数设置为包含列名的字符串或者列号,则要使用with=False函数,否则第二个参数中的字符会被视为运算表达式
DT<-as.data.table(iris)
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 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.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
> iris[1,1]
[1] 5.1
> DT[1,1]
Sepal.Length
<num>
1: 5.1
注:R已经默认将列名视为对象的一部分,因此不需要显式地设置with参数来进行选择。这种改变使得代码更加简洁和易读。因此这边只算是简单示例一下,提一嘴
数据表的第三个参数用于设置数据分组变量,比如,我们可以设置根据Specise求各品种的Sepal.Length
> DT[,mean(Sepal.Length),by="Species"]
Species V1
<fctr> <num>
1: setosa 5.006
2: versicolor 5.936
3: virginica 6.588
当然,在遇到多个分组变量时,只要将其在by中列出即可。例如,我们根据y,z来求x的平均值
> DT<-data.table(x=c(1,2,3,4,5),y=c("a","a","a","b","b"),z=c("c","c","d","d","d"))
> DT
x y z
<num> <char> <char>
1: 1 a c
2: 2 a c
3: 3 a d
4: 4 b d
5: 5 b d
> DT(,mean(x),by="y,z")
Error in DT(, mean(x), by = "y,z") : 没有"DT"这个函数
> DT[,mean(x),by="y,z"]
y z V1
<char> <char> <num>
1: a c 1.5
2: a d 3.0
3: b d 4.5
三、使用key快速访问数据
数据表可以实现二叉搜索树的索引,让查找数据变得更加便利,比数据框快得多
创建好键后,使用x[j(….),表达式],语法接口通过键来查找含有….中指定值的行,若表达式为空,则原样返回数据表的列,若表达式中含有运算,则返回运算结果
例如:数据框DF中含有X,Y两列,x中保存这连续均匀分布的随机数,y中保存着26个字母,,每个字母重复1000000次,要求从DF中找出y值为C的所有行,我们用system.time()函数来评估查找时间
> DF<-data.frame(X=runif(260000),y=rep(LETTERS,each=1000000))
> str(DF)
'data.frame': 260000000 obs. of 2 variables:
$ X: num 0.2761 0.6006 0.4095 0.0815 0.1753 ...
$ y: chr "A" "A" "A" "A" ...
> head(DF)
X y
1 0.27607233 A
2 0.60057455 A
3 0.40952382 A
4 0.08149889 A
5 0.17530720 A
6 0.15000765 A
> system.time(DF[DF$y == "C", ])
用户 系统 流逝
0.21 0.00 0.20
我们可以发现花费0.2秒,但当我们对y创建索引,然后使用y值检索时使用索引后,速度会变得非常快尤其是在检索次数很多,数据量很大的时候
> DT<-as.data.table(DF)
> setkey(DT,y)
> system.time(DT[J("C"),])
用户 系统 流逝
0 0 0
如果我们想将多个列创建为键,只要将多个列在setkey()函数中累出即可,即setkey(DT,列名1,列名2),然后使用J(列名1,列名2….)形式,列出键并检索数据,数据表的第二个参数可以是含有列名的表达式,所以可以在检索同时执行指定运算
例如:
> DT[J("A"),mean(X)]
[1] 0.4998012
如果希望得到多个计算结果,只需在list()中给出表达式,此时,给出表达式形式若为tag=value,则tag作为列名出现在结果中
例如:我们求上述数据表中x的平均值和标准差
> DT[J("A"),list(x_mean=mean(X),x_std=sd(X))]
x_mean x_std
<num> <num>
1: 0.4998012 0.2885001