【R语言】必学包之data.table包

      R语言具有较强的数据分析能力,但是对于数据处理,尤其是面对较大数据量时,就有很多的不足之处,为了解决处理较大数据集的问题,R中涌现了一系列数据处理的包,data.table包就是其中之一。data.table是对data.frame的扩展类型,因此适用于data.frame的函数也同样适用于data.table,不同的是data.table增加了索引设置,数据处理效率更高,能够快速地进行数据读取,聚合排序以及分组运算等。 

1)data.table对象

     可以使用data.table直接转换数据框为data.table类型。其中变量key是列名组成的向量,用于设置索引列。另外不同于data.frame,字符类型的列默认不会转换为因子类型(stringsAsFactors=FALSE) 。as.data.table和setDT也可用于转换list和data.frame为data.table对象,不同的是前者会完全复制原对象,然后进行转换,而后者是直接操作原数据集,因此前者更加的耗时和耗内存。

data.table(..., keep.rownames=FALSE, check.names=FALSE, key=NULL, stringsAsFactors=FALSE)
setDT(x, keep.rownames=FALSE, key=NULL, check.names=FALSE)
as.data.table(x, keep.rownames=FALSE, ...) 
例 1 :
#转换为data.table对象
dt1 <- data.table(mtcars)
dt_as1 <- as.data.table(mtcars)
#转换为data.table对象,保留原数据框行名称
dt2 <- data.table(mtcars, keep.rownames = TRUE)
dt_as2 <- as.data.table(mtcars, keep.rownames = TRUE)
#转换为data.table对象,建立索引在cyl列上
dt3 <- data.table(mtcars, key = "cyl")
#返回逻辑值,查看dt对象是否存在索引
haskey(dt3)
#使用key函数可以得到dt对象上的索引列,没有索引列返回NULL
key(dt3)
#返回内存中所有的data.table对象,包括行数,列名和索引列
tables()
例 2 :
#转换list为data.table, 自动填充列名
X = list(1:4, letters[1:4])
setDT(X)
#转换list为data.table, 并在变量a上创建key
X = list(a = 4:1, b=runif(4))
setDT(X, key="a")
key(X)
NOTE:  data.table中的set*系列函数以及操作符“:=”都是直接对原数据进行修改。
2)data.table操作   

     对于数据的处理,data.table包提供了一个非常简洁的通用格式:DT[i, j , by],其表示的含义为对数据集 DT,选取子集行 i , 通过 by 分组计算 j 

DT[i, j, by, keyby, with = TRUE,
    nomatch = getOption("datatable.nomatch"),                          
    mult = "all",
    roll = FALSE,
    rollends = if (roll=="nearest") c(TRUE,TRUE)
                 else if (roll>=0) c(FALSE,TRUE)
                 else c(TRUE,FALSE),
    which = FALSE,
    .SDcols,
    verbose = getOption("datatable.verbose"),                           
    allow.cartesian = getOption("datatable.allow.cartesian"),   
    drop = NULL, on = NULL]
  • i,设定数据的选取条件;
  • j,设定结果的计算方式,j 的计算输出为list(使用list()或者.())时,整个操作输出为data.table对象,并且data.table的每一列对应着list的每一个元素, j为非list输出时,返回为向量对象;如果i为data.table对象,j中可以”i. ”为前缀调用对象 i 的列,如X[Y, .(val, i.val)],val为X中的列,i.val为Y中的列。以“x.”为前缀可以调用DT对象中的列,如X[Y, .(x.a - i.a, b), on="a"]。j 中也可以使用一些特殊的只读符号:.SD, 为一个data.table对象,包含DT对象中每个分组的子集,默认包含除分组变量外的所有变量;.N, 为整数,可用于 i 和 j,用于返回每组的记录数;.I, 为数值向量,等于seq_len(nrow(x)),分组时用于返回记录在原dt中的行号;.GRP, 为数值向量,返回每个分组的组号;.BY, 为list对象,对应by中每个元素;
  • by,设定数据的分组情况;
  • keyby, 和by类似,不同的是会对结果执行setkey() 函数,返回排序后的结果,默认升序排列;
  • with, 逻辑值,当为TRUE时,j所表示的列名被视为变量,除了可用于筛选列外还可以用于计算;为FALSE时,j为字符向量所表示的列名或者是数值向量用于选择列;
  • nomatch,当 i 在DT中没有匹配值时,返回nomatch的默认值NA。设置nomatch为0,不返回匹配的行;
  • mult, 当 i 在DT中匹配多个值时,mult用于设置返回的记录,默认all返回所有匹配记录,first返回第一条匹配的记录,last返回最后一条匹配的记录;
  • .SDcols,与.SD一起使用,用于选择输出列;
  • verbose, 为TRUE会在控制台输出相应的状态和信息;
  • which,为FALSE(默认值)时返回DT中与 i 匹配的值,TRUE返回匹配元素的行号,NA返回不匹配元素的行号;
  • on,列名用于join对象 i 和 DT;
例 1 :
#转换为data.table对象
dt <- data.table(mtcars)
#以cyl分组,返回每组最大的mpg值
dt[, max(mpg), by=cyl]
#以cyl分组,返回每组最大的mpg值,返回的结果按cyl排序
dt[, max(mpg), keyby=cyl]
dt[, max(mpg), by=cyl][order(cyl)]
例 2 :
dt <- data.table(mtcars)
#j为向量形式,返回向量
dt[, c(mpg, cyl)]
#j为list,返回data.table对象
dt[, .(mpg, cyl)]
dt[, list(mpg, cyl)]
#计算cyl = 4的所有记录的mpg的和,返回向量
dt[cyl == 4, sum(mpg)]
#计算cyl = 4的所有记录的mpg的和,返回data.table
dt[cyl == 4, .(sum(mpg))]
例 3 :
dt <- data.table(mtcars)
#选择变量cyl
dt[, cyl]
#选择变量cyl,速度更快
dt[["cyl"]]
#with为FALSE时选择变量,j为字符串或数值
dt[, "cyl", with = FALSE]
dt[, 2, with = FALSE]
例 4 :
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
dt_rn[cyl == 4, ]
#筛选记录等同于cyl == 4
dt_rn[.(4), on = "cyl"]
dt_rn[.(4), on = .(cyl)]
dt_rn[list(4), on = "cyl"]
#筛选记录等同于cyl == 4,返回匹配的第一条记录
dt_rn[.(4), on = "cyl", mult = "first"]
#筛选记录等同于cyl == 4,返回匹配的最后一条记录
dt_rn[.(4), on = "cyl", mult = "last"]
#筛选行名称为Toyota Corona的记录
dt_rn["Toyota Corona", on = .(rn)]
例 5 :
dt <- data.table(mtcars)
#返回dt的最后一条记录
dt[.N]
#返回dt的记录数
dt[, .N] 
nrow(dt)
#根据cyl分组,返回每个分组的记录数
dt[, list(total=.N), by = cyl]
dt[, .N, by = cyl]
#选取hp到vs的所有列
dt[, .SD, .SDcols = hp:vs]
#返回第一行记录
dt[, .SD[1]]
#返回以cyl分组后的每组的第一条记录
dt[, .SD[1], by = cyl]
#以cyl分组,并对返回列求和,以及返回每组记录数
dt[, c(.N, lapply(.SD, sum)), by=cyl, .SDcols = hp:vs]
#返回以cyl分组后的每组第一条记录的行号
dt[, .I[1], by = cyl]
#为对象dt添加组号
dt[, grp := .GRP, by=cyl] 
3)筛选: filter

     data.table可以和data.frame一样进行方便的数据筛选操作,可以使用行号,或者多个条件语句的组合进行数据筛选。

例 1 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#按行号筛选数据,‘,’为可选项,而在data.frame中是不可省略的
dt_rn[3]
dt_rn[3:2]
dt_rn[3:2,]
#使用’!’或者’-‘号剔除数据
dt_rn[!3:2]
dt_rn[-(3:2)]
#以mpg排序,order返回的为行号
dt_rn[order(mpg)]
dt_rn[order(mpg),]
#返回cyl为4的所有记录
dt_rn[cyl == 4]
#返回cyl为4和6的所有记录
dt_rn[cyl %in% c(4,6)]
#’&’连接多个筛选条件,mean(mpg)是整个mpg的均值而不是cyl筛选后的mpg均值
dt_r1 <- dt_rn[cyl == 6 & mpg > mean(mpg)]
dt_r2 <- dt_rn[mpg > mean(mpg) & cyl == 6]
identical(dt_r1, dt_r2)

       在data.table中也可以使用between和inrange函数进行数据筛选。两者的区别在于,between只是对x和y做一一对应的比较,返回满足条件的数据,inrange则是x只要匹配y中任何一个区间即返回对应的数据。

#取lower和upper之间的值,inchounds用于设置是否包含边界,默认包含
between(x, lower, upper, incbounds=TRUE)
x %between% y
#返回在lower和upper之间任何一个间隔内的值
inrange(x, lower, upper, incbounds=TRUE)
x %inrange% y
例 2 :
X = data.table(a=1:5, b=6:10, c=c(5:1))
#返回b在[7,9]内的X记录
X[b %between% c(7,9)]
X[between(b, 7, 9)]
#返回c在[a, b]内的X记录
X[c %between% list(a,b)]
X[between(c, a, b)]
#返回c在(a, b)内的X记录
X[between(c, a, b, incbounds=FALSE)]
例 3 :
Y = data.table(a=c(8,3,10,7,-10), val=runif(5))
range = data.table(start = 1:5, end = 6:10)
#只要a的值在任何一个range的interval内便返回相应的Y记录
Y[a %inrange% range]
Y[inrange(a, range$start, range$end)]
Y[inrange(a, range$start, range$end, incbounds=FALSE)]
#只会做a与range之间的一一比较
Y[a %between% range]
Y[between(a, range$start, range$end)]
Y[between(a, range$start, range$end, incbounds=FALSE)]

      like函数可用于模糊筛选,pattern会首先传递给grepl函数进行模糊匹配,然后返回匹配后的记录。

#模糊匹配,使用正则表达式匹配记录
like(vector, pattern)
vector %like% pattern
例 4 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#返回行名称以Merc开头的行
dt_rn[rn %like% "^Merc"]
dt_rn[like(rn, "^Merc")]

      chmatch返回x中字符串在table中第一个匹配值的位置,有多个匹配值时只取第一个匹配记录的位置,%chin%返回匹配的逻辑向量,x匹配table中的值时返回TURE,否则返回FALSE。匹配过程只支持完全匹配,不支持模糊匹配。

#快速的匹配函数,类似于base::match, 但只适用于字符向量,返回第一个匹配值的位置
chmatch(x, table, nomatch = NA_integer_)
x %chin% table
例 5 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#返回指定名称的记录
dt_rn[rn %chin% c("Merc 280", "Merc 280C")]
dt_rn[chmatch(c("Merc 280", "Merc 280C"), rn)]
4)选择: select

      以上介绍了记录数的筛选,本节介绍变量的筛选,data.table可以根据列号或列名称进行变量的筛选,也可使用.SD筛选变量。

例 1 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#按列号选择和剔除列
dt_rn[,1]
dt_rn[, 2:4]
dt_rn[, !2:4]
dt_rn[, -(2:4)]
#按列名称选择和剔除列
dt_rn[, mpg]         #返回向量
dt_rn[, .(mpg)]      #返回data.table
dt_rn[, list(mpg)]   #返回data.table 
dt_rn[, mpg:cyl]
dt_rn[, !(mpg:cyl)]
dt_rn[, -(mpg:cyl)]
#with为FALSE,使用字符串代表的列名选择和剔除列
dt_rn[, "mpg", with = FALSE]
dt_rn[, c("mpg","cyl"), with = FALSE]
dt_rn[, mpg:cyl, with = FALSE]
dt_rn[, !c("mpg","cyl"), with = FALSE]
dt_rn[, -c("mpg","cyl"), with = FALSE]
#筛选列名包含字符s的列
subset(dt_rn, select = grep("s", names(dt_rn)))
#使用只读符号.SD选择列
dt_rn[, .SD, .SDcols = 1:3]
dt_rn[, .SD, .SDcols = rn:cyl]
dt_rn[, .SD, .SDcols = c("mpg", "cyl")]
#使用变量选择列
colname <- 1:3
dt_rn[, colname, with = FALSE]
colname <- c("mpg", "cyl")
dt_rn[, colname, with = FALSE]
#选择并重命名列
dt_rn[, .(rowname = rn)]
5)变形: mutate

     变形实际是为data.table增加新列或删除列, 这里需要用到‘:=’操作符, 使用过程会对原data.table直接操作,因此可以直接用于添加或者删除列。

例 1 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#删除列rn
dt_rn[, rn := NULL]
#添加新列wt_kg和wt_t,wt_kg不能直接使用创建wt_t
dt_rn[, ':='(wt_kg = wt * 453.592, wt_t = wt * 453.592 / 1000)]
dt_rn[, c('wt_kg', 'wt_t') := NULL]
dt_rn[, c('wt_kg', 'wt_t') := list(wt * 453.592,  wt * 453.592 / 1000)]
dt_rn[, c('wt_kg', 'wt_t') := NULL]
#使用类似管道操作添加多个列
dt_rn[, wt_kg := wt * 453.592][, wt_t := wt_kg / 1000]
6)排列: arrange

      data.table中依然可以使用order按给定的列排序,但是不同的是data.table中使用的order实际是调用forder进行快速排序。

例 1 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#以变量cyl正序,mpg反序排序
dt_rn[order(cyl, -mpg)]
#对cyl等于4的记录按mpg升序排序
dt_rn[cyl == 4][order(mpg)]

      setordersetorderv函数直接对原数据集进行排序,返回排序后的数据集。order用于设置升序(1)或者降序(-1), 可以为向量,默认为升序排序。

setorder(x, ..., na.last=FALSE)
setorderv(x, cols, order=1L, na.last=FALSE)
例 2 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#以变量cyl正序,mpg反序排序
setorder(dt_rn, cyl, -mpg)
#以变量cyl反序,mpg正序排序
setorderv(dt_rn, c("cyl", "mpg"), c(-1, 1))

     以下函数可以实现按字符向量进行排序。

#对字符向量快速排序
chorder(x)
#对字符向量分组,每个分组按元素在原向量中第一次出现的顺序排序
chgroup(x)
例 3 :
#转换为data.table对象,并保留行名称
dt_rn <- data.table(mtcars, keep.rownames = TRUE)
#以行名称排序
dt_rn[chorder(rn)]
dt_rn[rev(chorder(rn))]
例 4 :
xx<-c("abc", "bmn", "bml", "zxy", "uws", "cfb", "ceh", "bml",     
"abc", "zxy", "bml")
#对字符分组,并且按每个分组元素在原向量中的位置排序
xx[chgroup(xx)]
7)去重: distinct

     data.table提供了类似于base包中相同功能去重的函数,可用于直接删除或者返回data.table中的重复数据。

#返回逻辑向量,表示每行数据是否重复,重复为TRUE,不重复为FALSE
duplicated(x, incomparables=FALSE, fromLast=FALSE, 
by=seq_along(x), ...)
#返回去重后的data.table
unique(x, incomparables=FALSE, fromLast=FALSE, by=seq_along(x), ...)
#返回第一个重复值的行号
anyDuplicated(x,incomparables=FALSE,fromLast=FALSE, by=seq_along(x), ...)
#返回不重复值的个数
uniqueN(x, by=if (is.list(x)) seq_along(x) else NULL, na.rm=FALSE)
     incomparables用于设置例外的值(目前没有使用);fromLast,用于设置从前往后还是从后往前检查元素的唯一性,去重也按照相应的顺序去重,默认为FALSE从前往后;by设置用于检查唯一性的列,默认使用全部列
例 1 :
DT <- data.table(A = rep(1:3, each=4), 
                    B = rep(1:4, each=3), 
                    C = rep(1:2, 6), key = "A,B")
#默认以全部列去重,返回每行唯一的data.table
unique(DT)
unique(DT, fromLast = TRUE)
#返回逻辑向量,TRUE表示重复值,FALSE表示不重复
duplicated(DT)
duplicated(DT, fromLast = TRUE)
#以A列和B列去重
unique(DT, by=c("A", "B"))
duplicated(DT, by=c("A", "B"))

#返回去重后的data.table,等价于unique(DT)
DT[!duplicated(DT)]
#返回重复的行
unique(DT[duplicated(DT)])

#返回第一次出现重复值的行号
anyDuplicated(DT)
anyDuplicated(DT, by = c("A", "B"))

#返回不重复行的个数
uniqueN(DT)
unique(DT, by = c("A", "B"))

8)键和索引: key & Index

     data.table能够快速的进行数据检索得益于key和index的设计,对于key或者index的列,可以实现二分查找,使得查询的速度更快。key类似于SQL Server的聚集索引,规定了data.table数据的物理存储顺序,一个data.table只能包含一个key,但key可以包含多个不同类型的列,并不强制要求唯一性,key的值是允许重复的。在data.table上建立key值,会执行两个操作:

  • 直接对原data.table按key升序排序,而不是对原数据集的copy;
  • 设置sorted属性到原data.table;
如果修改了key列元素的值(即使设置为原值),相应的key会被移除,此时使用key()函数查询的结果为NULL。
例 1 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
#查看对象dt_flights包含的属性
names(attributes(dt_flights))
dt_flights_nokey <- head(dt_flights)
#在列origin上设置key(两种方法等效)
setkey(dt_flights, origin)
setkeyv(dt_flights, "origin")
#查看对象dt_flights包含的属性,多了一个sorted属性
names(attributes(dt_flights))
dt_flights_key <- head(dt_flights)
#设置key后原对象顺序被调整,因此返回FALSE
identical(dt_flights_nokey, dt_flights_key)
例 2 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
setkey(dt_flights, origin)
#默认使用key筛选,等价于origin == "JFK"
dt_flights[.("JFK")]
dt_flights[list("JFK")]
dt_flights[J("JFK")]
dt_flights["JFK"]
dt_flights["JFK", on = "origin"]
#虽然已近在origin上建立key,但是这种调用形式并不会使用key
dt_flights[origin == "JFK"]
#选择多个值
dt_flights[c("JFK", "LGA")]
dt_flights[.(c("JFK", "LGA"))]
dt_flights[c("JFK", "LGA"), on = "origin"]
#重新设置key在多个列上(两种方法等效)
setkey(dt_flights, origin, dest)
setkeyv(dt_flights, c("origin", "dest"))
#类似于origin == "JFK" and dest == "MIA",首先使用origin筛选,然后使用dest对筛选后的数据集进行筛选
dt_flights[.("JFK", "MIA")]
dt_flights[.("JFK", "MIA"), on = c("origin", "dest")]
#只使用第一个变量筛选
dt_flights[.("JFK")]
#只使用第二个变量筛选
dt_flights[.(unique(origin), "MIA")]
例 3 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
setkey(dt_flights, origin, dest)
#筛选origin == "LGA" and dest == "TPA"的记录,返回arr_delay列向量
dt_flights[.("LGA", "TPA"), arr_delay]
#筛选origin == "LGA" and dest == "TPA"的记录,返回data.table对象
dt_flights[.("LGA", "TPA"), .(arr_delay)]
dt_flights[.("LGA", "TPA"), "arr_delay", with = FALSE]
#对筛选后返回的data.table对象按arr_delay降序排列
dt_flights[.("LGA", "TPA"), .(arr_delay)][order(-arr_delay)]
例 4 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
setkey(dt_flights, time_hour)
#data.table对象的key为dt_flights
key(dt_flights)
#修改key列的值
dt_flights[, time_hour := substr(time_hour, 12, 19)]
#key被移除,返回NULL
key(dt_flights)
setkey(dt_flights, time_hour)
#即使赋值为原值,key也会被移除
dt_flights[, time_hour := time_hour]
key(dt_flights)
例 5 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
setkey(dt_flights, origin, dest)
#返回满足条件的第一条语句
dt_flights[.("JFK", "MIA"), mult = "first"]
#origin为"LGA", "JFK", "EWR",并且dest为"XNA"的最后一条记录,不能匹配的返回默认值NA
dt_flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last"]
#设置nomatch为0L,不会返回不匹配的行
dt_flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last", nomatch = 0L]
例 6 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
setkey(dt_flights, origin)
#返回以month分组后的每组的第一条记录,结果按month升序排序
dt_flights["JFK", .SD[1], keyby = month]
#对所有origin = "JFK"的记录,以month分组,返回每组最大的dep_delay值
dt_flights["JFK", max(dep_delay, na.rm = TRUE), keyby = month]

       一个data.table只能包含一个key,当使用其它列进行查询时,若希望使用key,则需要在新的查询列上重新创建key, 由于需要对整个data.table重新排序,实际上是非常消耗资源和耗时的,尤其是大数据量的情况。此时可以使用index代替,而无需重新建key。index类似于SQL Server的非聚集索引,并不会对data.table进行物理排序,而是对index列进行排序,并且生成index属性,一个data.table只能有一个key,但是可以有多个index。
      这里需要介绍下on参数,on所使用的列,可以不需要使用setindex(), 使用时会创建动态index,但是并不会保存为index属性,如果on使用的列上已近创建了index,则on会重用这个index, 而不会重新计算新的动态index。由于使用on过程中计算index的时间消耗非常小,因此可以不需要使用setindex()函数,另外on也可用于key所对应的列。
      另外,对于单列的查询,如果使用操作符“==”和“%in%”筛选数据,则会在查询列上自动创建index,并且添加index属性。如果需要需要关闭自动创建index,可以使用option语句修改全局设置,options(datatable.auto.index = FALSE)。

例 7 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
#查看对象dt_flights包含的属性
names(attributes(dt_flights))
dt_flights_noindex <- head(dt_flights)
#在列origin上设置index(两种方法等效)
setindex(dt_flights, origin)
setindexv(dt_flights, "origin")
#查看对象dt_flights包含的属性,多了一个index属性
names(attributes(dt_flights))
dt_flights_index <- head(dt_flights)
#设置index后并不改变原对象顺序,因此返回TRUE
identical(dt_flights_noindex, dt_flights_index)
#在列origin和dest上设置索引
setindex(dt_flights, origin, dest)
#查看对象dt_flights上的所有索引,"origin"以及"origin__dest"
indices(dt_flights)
#删除对象dt_flights上的所有索引
setindex(dt_flights, NULL)
#删除所有索引后返回NULL
indices(dt_flights)
#使用origin == "JFK"查询,自动创建index
dt_flights[origin == "JFK"]
#自动在origin 上创建索引
indices(dt_flights)
#关闭自动创建index
options(datatable.auto.index = FALSE)
dt_flights[origin == "JFK"]
#返回NULL,不会自动创建index
indices(dt_flights)
例 8 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
#开启verbose,输出信息”Calculated ad hoc index in 0 secs”
dt_flights["JFK", on = "origin", verbose = TRUE]
#开启verbose,输出信息” Calculated ad hoc index in 0.01 secs”
dt_flights[.("JFK","LAX"),on = c("origin","dest"),verbose = TRUE]
setindex(dt_flights, origin)
#已设置index,输出信息”on= matches existing index, using index”
dt_flights["JFK", on = "origin", verbose = TRUE]
#设置key在index列上,原来的index会被删除
setkey(dt_flights, origin)
#已设置key,输出信息”on= matches existing key, using key”
dt_flights["JFK", on = "origin", verbose = TRUE]
例 9 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
#筛选origin == "LGA"的记录,返回arr_delay列向量
dt_flights[.("LGA"), arr_delay, on = "origin"]
#筛选origin == "LGA"的记录,返回data.table对象
dt_flights[.("LGA"),.(arr_delay)]
dt_flights[.("LGA"), "arr_delay", with = FALSE]
#对筛选后返回的data.table对象按arr_delay降序排列
dt_flights[.("LGA"),.(arr_delay),on = "origin"][order(-arr_delay)]
#修改time_hour列的值
dt_flights[, time_hour := substr(time_hour, 12, 19), on = "time_hour"]
#对所有origin = "JFK"的记录,以month分组,返回每组最大的dep_delay值
dt_flights["JFK", max(dep_delay, na.rm = TRUE), keyby = month, on = "origin"]
例 10 :
library(nycflights13)
#转换为data.table对象
dt_flights <- as.data.table(flights)
#返回满足条件的第一条语句
dt_flights[c("BOS", "DAY"), on = "dest", mult = "first"]
#origin为"LGA", "JFK", "EWR",并且dest为"XNA"的最后一条记录,不能匹配的返回默认值NA
dt_flights[.(c("LGA", "JFK", "EWR"), "XNA"), on = c("origin", "dest"), mult = "last"]
#设置nomatch为0L,不会返回不匹配的行
dt_flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last", on = c("origin", "dest"), nomatch = 0L]

10) 数据关联: join

      data.table中也可以实现两个数据集间的关联操作。DT[i, j, on], 其中 i 中用于设置关联的对象,on 用于指定关联的列,j 用于选择输出列,默认返回全部列,j中可用”i. ”为前缀调用对象 i 的列,以“x.”为前缀调用DT对象中的列,j 中也可以使用一些特殊的只读符号。返回对象的同名列,会自动分配前缀区分,对象 i 的变量会使用前缀“i.”。

例 1 :
DT = data.table(x=rep(c("b","a","c"),each=3), y=c(1,3,6), v=1:9)
X = data.table(x=c("c","b", "d"), v=8:6, foo=c(6,4,2))
#DT right join X,返回X中的全部行,以及匹配的DT中的行
DT[X, on="x"]
#DT right join X,使用多个列match
DT[X, on=.(x, v)]

#DT left join X,返回DT中的全部行,以及匹配的X中的行
X[DT, on="x"]
#DT left join X,使用多个列match
X[DT, on=.(x, v)]

#DT inner join X,返回DT和X中完全匹配的行
DT[X, on="x", nomatch=0]
X[DT, on="x", nomatch=0]
#DT inner join X,使用多个列match
DT[X, on=.(x, v), nomatch=0]
X[DT, on=.(x, v), nomatch=0]

#返回DT中不在X中的行
DT[!X, on="x"]
#使用多个列match
DT[!X, on=.(x, v)]

#DT right join X,返回匹配的每个分组的第一个行
DT[X, on="x", mult="first"]
#DT right join X,返回匹配的每个分组的最后一行
DT[X, on="x", mult="last"]

#DT right join X,返回列x,foo以及DT和X中v 的积
DT[X, .(x, foo, v2 = v * i.v), on="x"]
DT[X, .(x, foo, v2 = x.v * i.v), on="x"]

#DT right join X,返回后的数据按分组对DT中的v求和,以下两种方法等效
DT[X, sum(v), by=.EACHI, on="x"]
DT[X, sum(x.v), by=.EACHI, on="x"]
#DT right join X,返回后的数据按分组对X中的v求和,i.用于代表对象X
DT[X, sum(i.v), by=.EACHI, on="x"]
#DT right join X,返回后的数据按分组对DT中的v求和,然后与X中的v相乘
DT[X, sum(v)*i.v, by=.EACHI, on="x"]

#DT right join X,首先笛卡尔积,返回X的全部记录,返回的y列会使用foo的值,且必须满足y<=foo, 由于使用x列 join,因此只返回一个x列
DT[X, on=.(x, y<=foo)]

#DT right join X,首先笛卡尔积,返回X的全部记录,返回的y列会使用foo的值,且必须满足y>=foo
DT[X, on=.(y>=foo)]

#DT right join X,首先笛卡尔积,返回X的全部记录,返回的y列会使用foo的值,且必须满足y<=foo
DT[X, on=.(y<=foo), allow.cartesian=TRUE]
DT[X, on="y<=foo", allow.cartesian=TRUE]  
DT[X, on=c("y<=foo"), allow.cartesian=TRUE]

#DT right join X,首先笛卡尔积,返回X的全部记录,返回的v列会使用
#X中v列的值,且必须满足DT.v >= X.v,返回的结果对y求和并与foo相乘
DT[X, sum(y)*foo, by=.EACHI, on=.(x, v>=v)]

11) 性能比较:performance

      以上是data.table相关操作的介绍,本节通过一些实例,对比data.frame和data.table的查询性能,以及对data.table中分别设置key或者index的查询性能进行比较。

例 1 :
N = 1e8L
x = sample(letters, N, TRUE)
y = sample(1000L, N, TRUE)
val = runif(N)
#创建data.frame对象
#user  system elapsed 
#4.15    0.39    4.62 
system.time((df = data.frame(x, y, val)))
#762.9 Mb
print(object.size(df), units = "Mb")
#user  system elapsed 
#1.35    0.03    1.37
system.time(df[x == "g" & y == 877L,])

#创建data.table对象
#user  system elapsed 
#0.59    0.47    1.16
system.time((dt = data.table(x, y, val)))
#762.9 Mb
print(object.size(df), units = "Mb")
#user  system elapsed 
#1.14    0.25    2.56 
system.time(dt[x == "g" & y == 877L])
#在x和y上创建key
#user  system elapsed 
#10.41    0.81   13.80 
system.time(setkey(dt, x, y))
#user  system elapsed 
#0       0       0 
system.time(dt[.("g", 877L)])
#以下查询形式并不会使用key查询
#user  system elapsed 
#0.96    0.17    1.12 
system.time(dt[x == "g" & y == 877L])

#创建data.table对象
dt_index = data.table(x, y, val)
#使用on创建临时的index,然后进行查询
#user  system elapsed 
#3.14    0.18    3.35
system.time(dt_index[.("9",877L), on = c("x","y")])
#在x和y上创建index
#user  system elapsed 
#3.36    0.17    3.54 
system.time(setindex(dt_index, x, y))
#user  system elapsed 
#0.01    0.00    0.01 
system.time(dt_index[.("9",877L), on = c("x","y")])

      以上实例可以说明创建data.table比data.frame高效,但是对没有key或者index的data.table的查询性能并没有优势。使用key的查询速度会非常快,但是创建key由于需要物理排序,实际上是非常耗时的,如果不是较长使用的查询列,最好不要设置key。同样,使用index的查询也十分高效,并且创建index由于并不需要物理排序,也比创建key省时,对于查询列不固定的情况,建议可以多使用index, 可以直接使用on子句直接建立临时的index进行查询,而不一定需要显式的创建index。

例 2 :
N = 1e8L
x = sample(letters, N, TRUE)
y = sample(1000L, N, TRUE)
val = runif(N)
#创建dt并且没有设置index
dt = data.table(x, y, val)
indices(dt)
#首次使用单列x以及”==”操作符筛选数据
#user  system elapsed 
#1.09  0.47    6.56 
system.time(dt[x == "g"])
#首次使用后自动在x列上创建了index
indices(dt)
#再次查询时会使用index
#user  system elapsed 
#0.21    0.00    0.22
system.time(dt[x == "g"])
#user  system elapsed 
#0.16    0.00    0.17
system.time(dt["g", on = "x"])
#删除索引
setindex(dt, NULL)
#user  system elapsed 
#1.60    0.41    4.96 
system.time(dt[x %in% c("g", "z")])
#首次使用后自动在x列上创建index
indices(dt)
#使用index查询
#user  system elapsed 
#0.58    0.06    1.13 
system.time(dt[x %in% c("g", "z")])

     对data.table使用单变量筛选数据时(操作符“==”,“%in%”),第一次的查询时间通常较久的原因是会自动创建index,后续相同变量的查询可以使用index,因此比较高效。

你可能感兴趣的:(R语言,R必学包,dplyr,data.table)