R数据处理|data.table篇(二)
上文我们讨论到使用data.table包完成数据分析框架,遗留如下几个问题
本文介绍data.table包更深入的使用方法,顺带解决上述问题,下面是本文目录
Key数据集创建 name1 <- c("Bob","Mary","Jane","Kim") name2 <- c("Bob","Kim","Jane") weight <- c(60,65,45,55) height <- c(170,165,140,135) birth <- c("1990-1","1980-2","1995-5","1996-4") accept <- c("no","ok","no") library(data.table) dft <- data.table(name1,weight,height,accept) DT数据框的行名及key的介绍 之前提到过提取时没有使用行名提取这个方法,这是因为data.table没有行名,如果硬要说有,那就是1234,而且不能修改,也不能根据行名来做提取等操作。 rownames(dft) # "1" "2" "3" "4" rownames(dft) <- letters[1:4] rownames(dft) # "a" "b" "c" "d" dft # 虽然上面rownames改过来了,但是这样输出发现还没有变 # dft["a","weight"] # 报错,无法根据行名提取 dft[1,"weight"] # 使用行数才可以 这不是data.table的漏洞,而是因为它有更强大的操作,根本就不需要使用行名。具体的使用方法是,把data.frame的行名当成一列读进去,通过设置key来指定该列为行名。这样做的好处是,不止可以指定这一列,任意一列都可以,还可以指定多列,我们下面来看一看。 ndt <- copy(dft) # 为了和原数据框对比,创建一个新的 setkey(ndt,name1) ndt # 发现数据框自动按照name1这一列进行排序了 # 如果想去掉key,则setkey(ndt,NULL) # 实现通过行名提取 ndt["Bob",weight] ndt["Bob","weight"] ndt["Bob",2] dft["Bob",2,on="name1"] # key 相当于使用了on ndt["Bob"] # 注意 setkey(ndt,weight) ndt[60] # 认为是提取第60行 ndt[.(60)] # 下面两个才是提取weight为60的行 ndt[J(60)] 当我们把data.frame数据框转化成data.table时,默认抛弃行名,不过我们也可以用一个参数保留行名成为新的一列 df1 <- data.frame(weight,row.names = name1) dt1 <- as.data.table(df1) dt2 <- as.data.table(df1,keep.rownames=T) # 将原来数据框中的行名当成一列,列名为rn dt1;dt2 as.data.table(df1,keep.rownames = "rownames") # 自己指定新增列的列名 设置key时还有另外一个函数setkeyv name1 <- "weight" setkey(ndt,name1) # 设置name1这一列为key ndt # 观察这里的排序和下面一个的区别 setkeyv(ndt,name1) # 设置name1这个变量指向的weight这一列为key ndt setkey(ndt,name1,weight) # 设置两个key setkeyv(ndt,c("name1","weight")) 这里注意到数据框会自动按照设置key的列进行排序 检查key的函数 haskey(ndt) # 返回TF值,检查是否有Key key(ndt) # 检查它的key是什么 使用key来辅助计算 setkey(ndt,accept) ndt["ok",sum(weight)] # 指定计算accept为ok的weight之和 ndt[c("ok","no"),sum(weight)] # 全部合在一起算 ndt[c("ok",sum(weight),by=.EACHI] # 分类算 ndt[accept,by=.EACHI] # 每一类计算完,保留所有行输出 ndt[unique(accept),by=.EACHI] # 只显示和类数相同的行 ndt[,by=accept] # 上面等价于分组计算 我们可以发现,key的作用相当于设定on参数。计算时使用key,再指定计算哪些行,by=.EACHI,可以实现和分组计算一样的功能。 设置多个key # 设置多个key,可以对多列进行筛选 setkey(ndt,weight) ndt[.("Kim",50:60)] # 不匹配的全部显示NA ndt[.("Kim",50:60),nomatch=0] # 不匹配的删除掉 ndt[.("Kim",roll=T] # 没有信息的用上面的添 ndt[.("Kim",roll=Inf] # 和上一条一样 ndt[.("Kim",roll=-Inf] # 没有信息的用下面的添 ndt[!"Kim"] ndt[!.("Kim",56)] 融合重铸data.table包改写了reshape2包中的融合重铸功能。加载了data.table包之后,不需要加载reshape2包,但是它的所有用法都可以照常使用。而data.table不仅运行速度更快,而且增加了一些reshape2包没有的功能,melt和dcast函数都有改进。 melt 先来看一下两个包中的函数的帮助文档,在控制台输入 ?melt 会发现这两个包中都有这个函数,下面是函数参数 # reshape2包中处理data.frame的melt函数 #(在页面中点击melt.data.frame,这涉及到R语言中的泛函,我们以后会专门讲解) # 现在理解就是当接的data是数据框时,使用 melt.data.frame这个函数 melt(data,id.vars,measure.vars,?variable.name = "variable",...,na.rm = FALSE,value.name = "value",?factorsAsStrings = TRUE) # data.table包中的melt函数 melt(data,? ?variable.name = "variable",? ?...,variable.factor = TRUE,? ?value.factor = FALSE,? ?verbose = getOption("datatable.verbose")) 读者如果看过我的上一篇讲融合重铸的文章(https://zhuanlan.zhihu.com/p/26123110)会发现,这里的很多参数我都没有使用到,在这里我会用它们实现更多的功能。下面我们列出三个主要功能,并总结每一个功能中两个包的差异 1.得到的数据是否转化为factor的问题
2.多种数据类型融合问题 之前我们遇到的问题都是被融合的数据是同样的数据类型,比如都是整数或都是字符串,现在我们要面对的是很多不同数据类型的数据框。这里我们使用data.table包中例子里创建的数据框 set.seed(45) DT <- data.table( ?i_1 = c(1:5,NA),?i_2 = c(NA,6,7,8,9,10),?f_1 = factor(sample(c(letters[1:3],TRUE)),?f_2 = factor(c("z","a","x","c","x"),ordered=TRUE),?c_1 = sample(c(letters[1:3],TRUE),?d_1 = as.Date(c(1:3,NA,4:5),origin="2013-09-01"),?d_2 = as.Date(6:1,origin="2012-01-01")) DT[,l_1 := DT[,list(c=list(rep(i_1,sample(5,1)))),by = i_1]$c] DT[,l_2 := DT[,list(c=list(rep(c_1,by = i_1]$c] DT # ? ?i_1 i_2 f_1 f_2 c_1 ? ? ? ?d_1 ? ? ? ?d_2 ? ? ? l_1 ? ? l_2 # 1: ? 1 ?NA ? b ? z ? b 2013-09-02 2012-01-07 ? ? ? 1,1 b,b,b # 2: ? 2 ? 6 ? b ? a ?NA 2013-09-03 2012-01-06 2,2 ? NA,NA # 3: ? 3 ? 7 ? c ? x ? a 2013-09-04 2012-01-05 ? ? ? ? 3 ? ? ? a # 4: ? 4 ? 8 ? a ? c ? c ? ? ? <NA> 2012-01-04 ? ? 4,4,4 ? ? ? c # 5: ? 5 ? 9 ?NA ? x ? b 2013-09-05 2012-01-03 5,5,5 ? b,b # 6: ?NA ?10 ?NA ? x ? c 2013-09-06 2012-01-02 ? ? ? ?NA ? ? c,c 我们可以看到,这个数据框中各列的数据类型是不相同的,总结一下是这样
我们如果直接用一条命令保留前两列 melt(DT,id=c("i_1","i_2")) # 或者 melt(DT,id=1:2) 其他列融合在一起,就会出现warning,强制转换数据类型。这时就需要我们能够选择相同类型的数据融合在一起,也就是相同的前缀融合在一起。两个包都是使用了measure.vars参数来控制要被融合的列
3.新列命名 融合之后得到一列自动命名为variable和value,如果我想自己指定名字,就使用variable.name和value.name两个参数,在这个功能上,两个包没有区别
DT <- data.table(v1 = rep(1:2,each = 6),? ? ? ? ? ? ? ? v2 = rep(rep(1:3,2),each = 2),? ? ? ? ? ? ? ? v3 = factor(rep(c(1,3),6),levels=1:3),? ? ? ? ? ? ? ? v4 = rnorm(6)) # margins参数 dcast(DT,v1~v2,mean) # 生成2*3的矩阵,默认选择v4作为value计算 dcast(DT,mean,margins=T) # 3*4的矩阵,多出了对每行每列求的均值 dcast(DT,margins="v1") # 只每列求均值 dcast(DT,margins="v2") # 只每行求均值 # drop 参数 # v3因子型,2没有出现过,在融合时还会与其他列进行匹配,就会出现一行全是NA的情况 dcast(DT,v1+v3~v2,drop=F) dcast(DT,drop=T) # fill参数 dcast(DT,drop=F,fill=0) # 是NA的地方填充0 # subset参数 dcast(DT,mean) dcast(DT,subset=.(v3==1)) # 挑选v3是1的出来计算 dcast(DT,subset=.(v1==1)) # 计算完挑选v1是1的出来 dcast(DT,subset=.(v2==1)) # 计算完挑选v2是1的出来 dcast(DT,subset=.(v1==1&v3==1)) # 挑选v3是1的出来计算,之后挑选v1是1的来展示 2.value.var参数 运行了上面代码的读者也会发现,每次输出结果时都会打印出如今选用的作为value的列是什么,我们如果想要自己指定哪一列作为value,就要用value.var参数 在这个参数的使用上,data.table包比reshape2包功能更强大 # 两个包都可以这样使用 DT <- data.table(v1 = rep(1:2,? ? ? ? ? ? ? ? v4 = rnorm(6)) dcast(DT,v1~v3,mean) # 默认使用v4来计算 dcast(DT,value.var="v2") # 指定v2作为value来计算 dcast(DT,value.var="v4") # 使用v4和默认的结果相同 下面是data.table包独有的功能 dcast(DT,value.var=c("v4","v2")) # v3中的元素分别和v2和v4组合,生成四列 dcast(DT,fun=list(sum,mean),value.var="v2") # 同时使用两种计算函数生成四列 dcast(DT,"v2")) # 二者结合,生成8列 dcast(DT,value.var=list("v4","v2")) # v4的使用sum,v2的使用mean,生成4列 特殊符号添加、更新和删除 := 符号 这个符号可以实现在本身直接更改,而无需产生一个新的数据框,再赋值给原本相同的变量名 dft <- data.table(name1,accept) dft[,u:=1] # 添加一个全是1的列 dft[,height:=1:4] # 更改height列 dft[,c("accept","height"):=.(1:4,2:5)] # 作用于多个列 dft[,`:=`(m=1:4,n=3:6)] # 使用:=函数的真正调用方式 dft[,weight:=NULL] # 删除weight列 dft[,c("m","n"):=NULL] # 删除多列 dft[2,height:=22][] # 只修改一个值,加一个[]返回得到的数据框 dft <- data.table(name1,accept) dft["Bob",accept:="yes",on="name1"] # 通过逻辑判断修改 dft[,m:=mean(height),by=accept] # 增加一个列,这个列根据分组计算得出 # 注意一点 dft[name1=="Bob"][,height:=13][] # :=作用在提取之后的数据框,所以对原数据框没有改变 dft # 使用一个指向字符串的变量作为新名称 a <- "aa" dft[,a:=1][] # 使用a作为列名 dft[,(a):=2][] # 使用aa作为列名 .N .N 代表行的数量,用by参数分组时则是每一组的行数量 dft[.N-1] # 返回倒数第二行 dft[,.N] # 返回数据框一共有几行(放在第二个参数位置表示计算并输出结果) dft[,.N,by=accept] # 分组计算行数 .SD .SD 代表整个数据框,用by参数分组时则是每一组的数据框 之前我们提到过,在DT中计算时输出的总是DT,但是如果我想返回多个矩阵怎么办,那就是使用嵌套list,像把矩阵压缩成一个元素一样,放在DT中。这里我们要用分组计算,返回矩阵。
.SDcols .SDcols 指定.SD 代表的数据框包括哪些列
这两对区别如下
Dwzb?,?R语言中文社区专栏作者,厦门大学统计专业学生。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |