R语言具有较强的数据分析能力,但是对于数据处理,尤其是面对较大数据量时,就有很多的不足之处,为了解决处理较大数据集的问题,R中涌现了一系列数据处理的包,data.table包就是其中之一。data.table是对data.frame的扩展类型,因此适用于data.frame的函数也同样适用于data.table,不同的是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*系列函数以及操作符“:=”都是直接对原数据进行修改。
对于数据的处理,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]
#转换为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]
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)]
以上介绍了记录数的筛选,本节介绍变量的筛选,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)]
变形实际是为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]
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)]
setorder和setorderv函数直接对原数据集进行排序,返回排序后的数据集。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)]
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值,会执行两个操作:
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)。
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,因此比较高效。