暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

data.table的使用—初级篇

机器会学习 2019-02-07
184


大家好,好久没有更新过了,先在这里和大家说一声抱歉,再说一声新春快乐,新年伊始,我打算更频繁的做一些更新,写一些干货,希望对数据分析和机器学习爱好者从业者有些许的帮助。


今天的话题是data.table;很多朋友应该听过data.table,或者用过。我自己也和周围的朋友以及一些网友交流过,也去参与过一些meetup,但是感觉喜欢用并且在工作学习中经常用data.table的并不是太多,至少没办法和dplyr[1]相比。

 

python不同,R语言自带data.frame类型,python没有内置的数据框类型,一般使用pandas模块。基本上所有的数据操作都可以用data.frameR里面的其他内置数据类型和函数实现。如果在十年前,或者二十年前,新的数据操作软件包看起来不是那么的必要,可是随着大数据以及大计算的概念的火热,大家学习工作中会处理越来越大的数据,对计算时间也越来越敏感。所以R语言自带的data.frame已经难以满足广大R语言用户的需求了。一些第三方的数据处理软件包吸引了越来越多的注意,比如dplyrdata.tableSparkR[3], feather[4], fst[5]等等。这些工具包可能侧重数据处理中的不同阶段或者任务。比如fst侧重于数据框类型的读写效率,SparkRR中提供了Spark的接口。

 

大家常常会把dplyrdata.table做比较。基于我有限的dplyr的使用经验,dplyr就像是一套招数繁杂的武功,你需要记住select(), mutate(), join(), distinct(), nrow(), sample_n() 等等一系列的函数,而且可能会有越来越多的特定功能的函数会被加入。也许在你每次学习到一个函数的时候,会获得一些成就感,但是我始终觉得这不是一种非常好的设计。相比之下,data.table的语法就简洁态度了,当然也有人觉得data.table的语法很奇怪。刚开始用data.table大概都会觉得它的语法奇怪,尤其是 `:=` 操作。对我来说data.table的另一个优势是性能/速度,这一点尤其重要。dplyr好像就是一套招式繁杂的武功,比如五岳剑派的剑法,data.table呢,好像是辟邪剑谱吧?(这个比喻貌似不是很恰当,不知道大家有什么看法)。

 

下面我们就来讲一讲data.table的使用,分为入门和高级两部分。这个文章里面我们先来讲讲入门,高级篇明天发。

  

首先要安装data.table,然后就可以使用了

 

创建data.table对象

 

使用data.table我们需要创造一个data.table类型的对象。有不同的办法去创造一个data.table的对象;这里我们来看三种

1) 把data.frame对象转变成data.table对象,比如

iris_dt=data.table(iris)

2) 支持创造一个data.table对象,比如

data.table(x=c(1:10),y=c(1:10)^2)

3)使用data.table里面的fread函数读csv文件,可以直接返回data.table对象,当然也可以返回data.frame对象。

 

选择子集

 

我们可以从一个data.table里面选择一些行或者一些列来创造一个新的data.table对象,这种操作也是数据处理中经常遇到的,比如在建模的时候,需要把数据分成训练集和测试集(行),或者在变量选取的时候我们要选一些特定的特征(列)。

 

iris_dt=data.table(iris)

iris_dt_subset=iris_dt[1:50, .(Sepal.Length,Petal.Length,Species)]

head(iris_dt_subset)

   Sepal.Length Petal.Length Species

1:          5.1          1.4  setosa

2:          4.9          1.4  setosa

3:          4.7          1.3  setosa

4:          4.6          1.5  setosa

5:          5.0          1.4  setosa

6:          5.4          1.7  setosa


注意我们选取列的时候的语法,

  1. 括号前面有一个点,这个点其实是list的别称,所以我们可以得到一样的结果使用iris_dt[1:50, list(Sepal.Length,Petal.Length,Species)]

  2. 列的名字没有引号,如果加了引号,那么结果变成如下

> iris_dt[1:5,.('Sepal.Length',Petal.Length,Species)]

             V1 Petal.Length Species

1: Sepal.Length          1.4  setosa

2: Sepal.Length          1.4  setosa

3: Sepal.Length          1.3  setosa

4: Sepal.Length          1.5  setosa

5: Sepal.Length          1.4  setosa

 

添加新的列

 

比如我们想在iris_dt数据框上加上一列 max.Length=max(Sepal.Length, Petal.Length),我们需要使用:=操作符。

 

> iris_dt[,max.Length:=pmax(Sepal.Length,Petal.Length)]

> head(iris_dt)

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species max.Length

1:          4.3         3.0          1.1         0.1  setosa        4.3

2:          4.4         2.9          1.4         0.2  setosa        4.4

3:          4.4         3.0          1.3         0.2  setosa        4.4

4:          4.4         3.2          1.3         0.2  setosa        4.4

5:          4.5         2.3          1.3         0.3  setosa        4.5

6:          4.6         3.1          1.5         0.2  setosa        4.6

 

排序

 

data.table的排序功能非常方便而且强大。比如我们想把iris_dt按Sepal.Length排序:

> setkey(iris_dt,Sepal.Length)

> head(iris_dt)

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species

1:          4.3         3.0          1.1         0.1  setosa

2:          4.4         2.9          1.4         0.2  setosa

3:          4.4         3.0          1.3         0.2  setosa

4:          4.4         3.2          1.3         0.2  setosa

5:          4.5         2.3          1.3         0.3  setosa

6:          4.6         3.1          1.5         0.2  setosa

 

data.tablesetkey()函数可以对一个data.table按照某一列进行排序,排序之后,这个data.table对象会被标记为排过序了,这个排序不会在内存中复制被排序的data.table对象,所以非常高效。

 

我们也可以对data.table按照几列一起来排序,这时需要使用setkeyv()函数:


> setkeyv(iris_dt,c('Sepal.Length','Sepal.Width'))

> head(iris_dt)

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species max.Length

1:          4.3         3.0          1.1         0.1  setosa        4.3

2:          4.4         2.9          1.4         0.2  setosa        4.4

3:          4.4         3.0          1.3         0.2  setosa        4.4

4:          4.4         3.2          1.3         0.2  setosa        4.4

5:          4.5         2.3          1.3         0.3  setosa        4.5

6:          4.6         3.1          1.5         0.2  setosa        4.6


需要注意的是我们对列名加了引号。

 

分组操作

 

如何得到每一和类别的Sepal.Length的平均值呢?


iris_dt[,.(mean_sepal_length=mean(Sepal.Length)),by=Species]

      Species mean_sepal_length

1:     setosa             5.006

2: versicolor             5.936

3:  virginica             6.588

 

上面这行代码可能看起来有点奇怪,首先我们看到在[ ] 里面有两个逗号。第一个逗号之前是用来选取行的,我们这里选取所有的行, 所以第一个逗号之前是空的,第二个逗号后面的by= x 会把数据按照x的不同值进行分组,然后我们对每一组数据进行两个逗号之间的操作,也就是计算Sepal.Length的平均值并且把这一列命名为mean_sepal_length。也许你会疑问为什么这里用=而不是:=,这是因为我们并没有在原始的iris_dt中加入新的一列,当我们用by关键字的时候,一个新的data.table对象被创造出来。另一个可能有的疑问是为什么要用 .() 来创造mean_sepal_length;如果把 .() 删除,那么这一行代码会报错。这可以理解为data.table的语法要求,使用by关键字,我们在创造新的有名字的列的时候需要用list()或者.();如果我们不需要对新的列命名,那我们可以把上面的代码简化:


> iris_dt[,mean(Sepal.Length),by=Species]

      Species    V1

1:     setosa 5.006

2: versicolor 5.936

3:  virginica 6.588

 

上面的例子只是按照Species 来分组,我们也可以用多列一起来分组,比如我们可以这样写:

 

> iris_dt[,mean(Sepal.Length),by=.(Species, Petal.Width)]

也可以这样写:

> iris_dt[,mean(Sepal.Length),by=c('Species', 'Petal.Width')]


我们一样可以同时创造几个不同的列,比如:


>iris_dt[,.(mean_sepal_length=mean(Sepal.Length),mean_petal_width=mean(Petal.Width)),by=Species]

      Species mean_sepal_length mean_petal_width

1:     setosa             5.006            0.246

2: versicolor             5.936            1.326

3:  virginica             6.588            2.026

 

看到这里有没有觉得data.table非常简洁方便?

 

join

 

data.table提供两种不同的语法进行数据框的join操作:

类型

语法1

语法2

inner

X[Y,nomatch=0]或者

Y[X,nomatch=0]

merge(X,Y,all=FALSE)

left

Y[X]

merge(X,Y,all.x=TRUE)

right

X[Y]

merge(X,Y,all.y=TRUE)

full

-

merge(X,Y,all=TRUE)

 

在使用语法1的时候,我们需要先用setkey(v) 去设置data.table的key;用语法2,我们可以在merge函数里面指定用来join的列名。


>iris_summary=iris_dt[,.(mean_sepal_length=mean(Sepal.Length),mean_petal_width=mean(Petal.Width)),by=Species]

> iris_summary

      Species mean_sepal_length mean_petal_width

1:     setosa             5.006            0.246

2: versicolor             5.936            1.326

3:  virginica             6.588            2.026

> setkey(iris_dt,Species)

> setkey(iris_summary,Species)

> head(iris_summary[iris_dt])

   Species mean_sepal_length mean_petal_width Sepal.Length Sepal.Width

1:  setosa             5.006            0.246          4.3         3.0

2:  setosa             5.006            0.246          4.4         2.9

3:  setosa             5.006            0.246          4.4         3.0

4:  setosa             5.006            0.246          4.4         3.2

5:  setosa             5.006            0.246          4.5         2.3

6:  setosa             5.006            0.246          4.6         3.1

   Petal.Length Petal.Width max.Length

1:          1.1         0.1        4.3

2:          1.4         0.2        4.4

3:          1.3         0.2        4.4

4:          1.3         0.2        4.4

5:          1.3         0.3        4.5

6:          1.5         0.2        4.6

 


References:

[1] https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html

[2] https://cran.r-project.org/web/packages/dplyr/dplyr.pdf

[3] https://spark.apache.org/docs/latest/sparkr.html

[4] https://github.com/wesm/feather

[5] http://www.fstpackage.org


欢迎关注微信公众号 —— 机器会学习

文章转载自机器会学习,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论