dplyr包是Hadley Wickham的新作,主要⽤于数据清洗和整理,该包专注dataframe数据格式,从⽽⼤幅提⾼了数据处理速度,并且提供了与其它数据库的接⼝;tidyr包的作者是Hadley Wickham, 该包⽤于“tidy”你的数据,这个包常跟dplyr结合使⽤。dplyr、tidyr包安装及载⼊
install.packages(\"dplyr\")install.packages(\"tidyr\")library(dplyr)library(tidyr)
使⽤datasets包中的mtcars数据集做演⽰,⾸先将过长的数据整理成友好的tbl_df数据:
mtcars_df = tbl_df(mtcars)
⼀、dplyr包基本操作 1.1 筛选: filter()
按给定的逻辑判断筛选出符合要求的⼦数据集
filter(mtcars_df,mpg==21,hp==110)
# A tibble: 2 x 11
mpg cyl disp hp drat wt qsec vs am gear carb
1.2 排列: arrange() 按给定的列名依次对⾏进⾏排序: arrange(mtcars_df, disp) #可对列名加 desc(disp) 进⾏倒序 # A tibble: 32 x 11 mpg cyl disp hp drat wt qsec vs am gear carb 1.3 选择: select() ⽤列名作参数来选择⼦数据集: select(mtcars_df, disp:wt)# A tibble: 32 x 4 disp hp drat wt * 1.4 变形: mutate() 对已有列进⾏数据运算并添加为新列: mutate(mtcars_df, NO = 1:dim(mtcars_df)[1]) # A tibble: 32 x 12 mpg cyl disp hp drat wt qsec vs am gear carb NO 10 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 10# ... with 22 more rows 1.5 汇总: summarise() 对数据框调⽤其它函数进⾏汇总操作, 返回⼀维的结果: summarise(mtcars_df, mdisp = mean(disp, na.rm = TRUE))# A tibble: 1 x 1 mdisp 1.6 分组: group_by() 当对数据集通过group_by()添加了分组信息后,mutate(),arrange() 和 summarise() 函数会⾃动对这些 tbl 类数据执⾏分组操作。 cars <- group_by(mtcars_df, cyl) countcars <- summarise(cars, count = n()) # count = n()⽤来计算次数# A tibble: 3 x 2 cyl count 1.7 连接符%>% 包⾥还新引进了⼀个操作符, 使⽤时把数据名作为开头, 然后依次对此数据进⾏多步操作. Batting %>%group_by(playerID) %>%summarise(total = sum(G)) %>%arrange(desc(total)) %>%head(5) ⼆、tidyr包基本操作 2.1 宽转长:gather() 使⽤gather()函数实现宽表转长表,语法如下: gather(data, key, value, …, na.rm = FALSE, convert = FALSE)data:需要被转换的宽形表 key:将原数据框中的所有列赋给⼀个新变量keyvalue:将原数据框中的所有值赋给⼀个新变量value…:可以指定哪些列聚到同⼀列中na.rm:是否删除缺失值 widedata <- data.frame(person=c('Alex','Bob','Cathy'),grade=c(2,3,4),score=c(78,89,88))widedata person grade score1 Alex 2 782 Bob 3 893 Cathy 4 88 longdata <- gather(widedata, variable, value,-person)longdata person variable value1 Alex grade 22 Bob grade 33 Cathy grade 44 Alex score 785 Bob score 896 Cathy score 88 2.2 长转宽:spread() 有时,为了满⾜建模或绘图的要求,往往需要将长形表转换为宽形表,或将宽形表变为长形表。如何实现这两种数据表类型的转换。使⽤spread()函数实现长表转宽表,语法如下: spread(data, key, value, fill = NA, convert = FALSE, drop = TRUE)data:为需要转换的长形表 key:需要将变量值拓展为字段的变量value:需要分散的值 fill:对于缺失值,可将fill的值赋值给被转型后的缺失值 mtcarsSpread <- mtcarsNew %>% spread(attribute, value)head(mtcarsSpread) car am carb cyl disp drat gear hp mpg qsec vs wt1 AMC Javelin 0 2 8 304 3.15 3 150 15.2 17.30 0 3.4352 Cadillac Fleetwood 0 4 8 472 2.93 3 205 10.4 17.98 0 5.2503 Camaro Z28 0 4 8 350 3.73 3 245 13.3 15.41 0 3.8404 Chrysler Imperial 0 4 8 440 3.23 3 230 14.7 17.42 0 5.3455 Datsun 710 1 1 4 108 3.85 4 93 22.8 18.61 1 2.3206 Dodge Challenger 0 2 8 318 2.76 3 150 15.5 16.87 0 3.520 2.3 合并:unit()unite的调⽤格式如下: unite(data, col, …, sep = “_”, remove = TRUE)data:为数据框 col:被组合的新列名称…:指定哪些列需要被组合 sep:组合列之间的连接符,默认为下划线 remove:是否删除被组合的列 wideunite<-unite(widedata, information, person, grade, score, sep= \"-\")wideunite information1 Alex-2-782 Bob-3-893 Cathy-4-88 2.4 拆分:separate() separate()函数可将⼀列拆分为多列,⼀般可⽤于⽇志数据或⽇期时间型数据的拆分,语法如下: separate(data, col, into, sep = “[^[:alnum:]]+”, remove = TRUE,convert = FALSE, extra = “warn”, fill = “warn”, …)data:为数据框 col:需要被拆分的列 into:新建的列名,为字符串向量sep:被拆分列的分隔符 remove:是否删除被分割的列 widesep <- separate(wideunite, information,c(\"person\",\"grade\",\"score\"), sep = \"-\")widesep person grade score1 Alex 2 782 Bob 3 893 Cathy 4 88 三、data.table R语⾔data.table包是⾃带包data.frame的升级版,⽤于数据框格式数据的处理,最⼤的特点快。包括两个⽅⾯,⼀⽅⾯是写的快,代码简洁,只要⼀⾏命令就可以完成诸多任务,另⼀⽅⾯是处理快,内部处理的步骤进⾏了程序上的优化,使⽤多线程,甚⾄很多函数是使⽤C写的,⼤⼤加快数据运⾏速度。因此,在对⼤数据处理上,使⽤data.table⽆疑具有极⾼的效率。这⾥我们主要讲的是它对数据框结构的快捷处理。可见separate()函数和unite()函数的功能相反。和data.frame的⾼度兼容 DT = data.table(x=rep(c(\"b\",\"a\",\"c\"),each=3), y=c(1,3,6), v=1:9) 下⾯DT都是⽤这个data.table 可见它是属于data.table和data.frame类,并且取列,维数,都可以采⽤data.frame的⽅法。 DF = data.frame(x=rep(c(\"b\",\"a\",\"c\"),each=3), y=c(1,3,6), v=1:9)DT = data.table(x=rep(c(\"b\",\"a\",\"c\"),each=3), y=c(1,3,6), v=1:9)DFDT identical(dim(DT), dim(DF)) # TRUEidentical(DF$a, DT$a) # TRUEis.list(DF) # TRUEis.list(DT) # TRUE is.data.frame(DT) # TRUE 不过data.frame默认将⾮数字转化为因⼦;⽽data.table 会将⾮数字转化为字符data.table数据框也可使⽤dplyr包的管道,这⾥不作阐述。 data.table常⽤的函数 as.data.table(x, keep.rownames=FALSE, ...) 将⼀个R对象转化为data.table,R可以时⽮量,列表,data.frame等,keep.rownames决定是否保留⾏名或者列表名,默认FALSE,如果TRUE,将⾏名存在\"rn\"⾏中,keep.rownames=\"id\⾏名保存在\"id\"⾏中。 DF = data.frame(x=rep(c(\"b\",\"a\",\"c\"),each=3), y=c(1,3,6), v=1:9) #新建data.frame DFDT=as.data.table(DF,keep.rownames=TRUE) setDT(x, keep.rownames=FALSE, key=NULL, check.names=FALSE) 把⼀个R对象转化为data.table,⽐as.data.table快,因为以传地址的⽅式直接修改原对象,没有拷贝 copy(x) 深度拷贝⼀个data.table,x即data.table对象。data.table为了加快速度,会直接在对象地址修改,因此如果需要就要在修改前copy,直接修改的命令有:=添加⼀列,set系列命令⽐如下⾯提到的setattr,setnames,setorder等;当使⽤dt_names = names(DT)的时候,修改dt_names会修改原data.table的列名,如果不想被修改,这个时候应copy原data.table,也可以使⽤dt_names <- copy(names(DT))直接copy列名,这样不必copy整个data.table。 kDT=copy(DT) #kDT时DT的⼀个copy rowid(..., prefix=NULL) 产⽣unique的id,prefix参数在id前⾯加前缀 setattr 设置DT的属性,setattr(x,name,value) x时data.table,list或者data.frame,⽽name时属性名,value时属性值,setnames(x,old,new),设置x的列名,old是旧列名或者数字位置,new是新列名 setcolorder(x,neworder) 重新安排列的顺序,neworder字符⽮量或者⾏数 set(DT,rownum,colnum,value)直接修改某个位置的值,rownum⾏号,colnum,列号,⾏号列号推荐使⽤整型,保证最快速度,⽅法是在数字后⾯加L,⽐如1L,value是需要赋予的值。⽐:=还快,通常和循环配合使⽤ ⾄于这个操作究竟有多快,可以看⼀下(参照官⽅manual的命令),另外个⼈觉得最⽜的三个函数是set(),fread,和fwrite fread fread(input, sep=\"auto\", sep2=\"auto\", nrows=-1L, header=\"auto\", na.strings=\"NA\", file,stringsAsFactors=FALSE, verbose=getOption(\"datatable.verbose\"), autostart=1L,skip=0L, select=NULL, drop=NULL, colClasses=NULL,integer64=getOption(\"datatable.integer64\"),# default: \"integer64\" dec=if (sep!=\".\") \".\" else \, col.names, check.names=FALSE, encoding=\"unknown\", quote=\"\\\"\", strip.white=TRUE, fill=FALSE, blank.lines.skip=FALSE, key=NULL,showProgress=getOption(\"datatable.showProgress\"), # default: TRUEdata.table=getOption(\"datatable.fread.datatable\") # default: TRUE) input输⼊的⽂件,或者字符串(⾄少有⼀个\"\\n\");sep列之间的分隔符; sep2,分隔符内再分隔的分隔符,功能还没有应⽤;nrow,读取的⾏数,默认-l全部,nrow=0仅仅返回列名;header第⼀⾏是否是列名;na.strings,对NA的解释; file⽂件路径,再确保没有执⾏shell命令时很有⽤,也可以在input参数输⼊;stringsASFactors是否转化字符串为因⼦,verbose,是否交互和报告运⾏时间; autostart,机器可读这个区域任何⾏号,默认1L,如果这⾏是空,就读下⼀⾏; skip跳过读取的⾏数,为1则从第⼆⾏开始读,设置了这个选项,就会⾃动忽略autostart选项,也可以是⼀个字符,skip=\"string\那么会从包含该字符的⾏开始读;select,需要保留的列名或者列号,不要其它的; drop,需要取掉的列名或者列号,要其它的; colClasses,类字符⽮量,⽤于罕见的覆盖⽽不是常规使⽤,只会使⼀列变为更⾼的类型,不能降低类型;integer64,读如64位的整型数; dec,⼩数分隔符,默认\".\"不然就是\ col.names,给列名,默认试⽤header或者探测到的,不然就是V+列号; encoding,默认\"unknown\",其它可能\"UTF-8\"或者\"Latin-1\",不是⽤来重新编码的,⽽是允许处理的字符串在本机编码; quote,默认\"\"\",如果以双引开头,fread强有⼒的处理⾥⾯的引号,如果失败了就会⽤其它尝试,如果设置quote=\"\默认引号不可⽤strip.white,默认TRUE,删除结尾空⽩符,如果FALSE,只取掉header的结尾空⽩符;fill,默认FALSE,如果TRUE,不等长的区域可以⾃动填上,利于⽂件顺利读⼊;blank.lines.skip,默认FALSE,如果TRUE,跳过空⽩⾏key,设置key,⽤⼀个或多个列名,会传递给setkeyshowProgress,TRUE会显⽰脚本进程,R层次的C代码data.table,TRUE返回data.table,FALSE返回data.frame 实例如下,1.8GB的数据读⼊94秒,可见读⼊⽂件速度⾮常快, fwrite fwrite(x, file = \"\", append = FALSE, quote = \"auto\",sep = \, sep2 = c(\"\",\"|\",\"\"), eol = if (.Platform$OS.type==\"windows\") \"\\r\\n\" else \"\\n\", na = \"\", dec = \".\", row.names = FALSE, col.names = TRUE,qmethod = c(\"double\",\"escape\"), logicalAsInt = FALSE, dateTimeAs = c(\"ISO\",\"squash\",\"epoch\",\"write.csv\"),buffMB = 8L, nThread = getDTthreads(), showProgress = getOption(\"datatable.showProgress\"),verbose = getOption(\"datatable.verbose\")) x,具有相同长度的列表,⽐如data.frame和data.table等;file,输出⽂件名,\"\"意味着直接输出到操作台;append,如果TRUE,在原⽂件的后⾯添加; quote,如果\"auto\因⼦和列名只有在他们需要的时候才会被加上双引号,例如该部分包括分隔符,或者以\"\\n\"结尾的⼀⾏,或者双引号它⾃⼰,如果FALSE,那么区域不会加上双引号,如果TRUE,就像写⼊CSV⽂件⼀样,除了数字,其它都加上双引号;sep,列之间的分隔符; sep2,对于是list的⼀列,写出去时list成员间以sep2分隔,它们是处于⼀列之内,然后内部再⽤字符分开;eol,⾏分隔符,默认Windows是\"\\r\\n\其它的是\"\\n\";na,na值的表⽰,默认\"\"; dec,⼩数点的表⽰,默认\".\"; row.names,是否写出⾏名,因为data.table没有⾏名,所以默认FALSE; col.names ,是否写出列名,默认TRUE,如果没有定义,并且append=TRUE和⽂件存在,那么就会默认使⽤FALSE;qmethod,怎样处理双引号,\"escape\类似于C风格,⽤反斜杠逃避双引,“double\默认,双引号成对;logicalAsInt,逻辑值作为数字写出还是作为FALSE和TRUE写出; dateTimeAS, 决定 Date/IDate,ITime和POSIXct的写出,\"ISO\"默认,-2016-09-12, 18:12:16和2016-09-12T18:12:16.999999Z;\"squash\和 20160912181216999;\"epoch\,65536和1473703936;\"write.csv\",就像write.csv⼀样写⼊时间,仅仅对POSIXct有影响,as.character将digits.secs转化字符并通过R内部UTC转回本地时间。前⾯三个选项都是⽤新的特定C代码写的,较快buffMB,每个核⼼给的缓冲⼤⼩,在1到1024之间,默认80MBnThread,⽤的核⼼数。 showProgress,在⼯作台显⽰进程,当⽤file==\"\"时,⾃动忽略此参数verbose,是否交互和报告时间 data.table数据框结构处理语法 data.table[ i , j , by] i 决定显⽰的⾏,可以是整型,可以是字符,可以是表达式,j 是对数据框进⾏求值,决定显⽰的列,by对数据进⾏指定分组,除了by ,也可以添加其它的⼀系列参数: keyby,with,nomatch,mult,rollollends,which,.SDcols,on。 i 决定显⽰的⾏ DT = data.table(x=rep(c(\"b\",\"a\",\"c\"),each=3), y=c(1,3,6), v=1:9) #新建data.table对象DTDT[2] #取第⼆⾏ DT[2:3] #取第⼆到第三⾏ DT[order(x)] #将DT按照X列排序,简化操作,另外排序也可以setkey(DT,x),出来的DT就已经是按照x列排序的了。⽤haskey(DT)判断DT是否已经设置了key,可以设置多个列作为keyDT[y>2] # DT$y>2的⾏ DT[!2:4] #除了2到4⾏剩余的⾏ DT[\"a\",on=\"x\"] #on 参数,DT[D,on=c(\"x\取DT上\"x\列上与D上“x\"、\"y\"的列相关联的⾏,与D进⾏merge。⽐如此例取出DT 中 X 列为\"a\"的⾏,和\"a\"进⾏merge。on参数的第⼀列必须是DT的第⼀列DT[.(\"a\"), on=\"x\"] #和上⾯⼀样.()有类似与c()的作⽤DT[\"a\", on=.(x)] #和上⾯⼀样 DT[x==\"a\"] # 和上⾯⼀样,和使⽤on⼀样,都是使⽤⼆分查找法,所以它们速度⽐⽤data.frame的快。也可以⽤setkey之后的DT,输⼊DT[\"a\"]或者DT[\"a\如果有⼏个key的话推荐⽤onDT[x!=\"b\" | y!=3] #x列不等于\"b\"或者y列不等于3的⾏DT[.(\"b\", 3), on=.(x, v)] #取DT的x,v列上x=\"b\的⾏ j 对数据框进⾏求值输出 j 参数对数据进⾏运算,⽐如sum,max,min,tail等基本函数,输出基本函数的计算结果,还可以⽤n输出第n列,.N(总列数,直接在j输⼊.N取最后⼀列),:=(直接在data.table上添加列,没有copy过程,所以快,有需要的话注意备份),.SD输出⼦集,.SD[n]输出⼦集的第n列,DT[,.(a = .(), b = .())] 输出⼀个a、b列的数据框,.()就是 要输⼊的a、b列的内容,还可以将⼀系列处理放⼊⼤括号,如{tmp <- mean(y);.(a = a-tmp, b = b-tmp)} DT[,y] #返回y列,⽮量 DT[,.(y)] #返回y列,返回data.tableDT[, sum(y)] #对y列求和 DT[, .(sv=sum(v))] #对y列求和,输出sv列,列中的内容就是sum(v)DT[, .(sum(y)), by=x] # 对x列进⾏分组后对各分组y列求总和 DT[, sum(y), keyby=x] #对x列进⾏分组后对各分组y列求和,并且结果按照x排序DT[, sum(y), by=x][order(x)] #和上⾯⼀样,采取data.table的链接符合表达式 DT[v>1, sum(y), by=v] #对v列进⾏分组后,取各组中v>1的⾏出来,各组分别对定义的⾏中的y求和DT[, .N, by=x] #⽤by对DT ⽤x分组后,取每个分组的总列数 DT[, .SD, .SDcols=x:y] #⽤.SDcols 定义SubDadaColums(⼦列数据),这⾥取出x到之间的列作为⼦集,然后.SD 输出所有⼦集DT[2:5, cat(y, \"\\n\")] #直接在j ⽤cat函数,输出2到5列的y值 DT[, plot(a,b), by=x] #直接在j⽤plot函数画图,对于每个x的分组画⼀张图 DT[, m:=mean(v), by=x] #对DT按x列分组,直接在DT上再添加⼀列m,m的内容是mean(v),直接修改并且不输出到屏幕上DT[, m:=mean(v), by=x] [] #加[]将结果输出到屏幕上 DT[,c(\"m\",\"n\"):=list(mean(v),min(v)), by=x][] # 按x分组后同时添加m,n 两列,内容是分别是mean(v)和min(v),并且输出到屏幕DT[, `:=`(m=mean(v),n=min(v)),by=x][] #内容和上⾯⼀样,另外的写法DT[,(seq = min(y):max(v)), by=x] #输出seq列,内容是min(a)到max(b) DT[, c(.(y=max(y)), lapply(.SD, min)), by=x, .SDcols=y:v] #对DT取y:v之间的列,按x分组,输出max(y),对y到v之间的列每列求最⼩值输出。 by,on,with等参数 by 对数据进⾏分组 on DT[D,on=c(\"x\取DT上\"x\列上与D上\"x\列相关联的⾏,并与D进⾏merge DT[X, on=\"x\"] #左联接X[DT, on=\"x\"] #右联接 DT[X, on=\"x\", nomatch=0] #内联接,nomatch=0表⽰不返回不匹配的⾏,nomatch=NA表⽰以NA返回不匹配的值 with 默认是TRUE,列名能够当作变量使⽤,即x相当于DT$\"x\当是FALSE时,列名仅仅作为字符串,可以⽤传统data.frame⽅法并且返回data.table,x[, cols,with=FALSE] 和x[, .SD, .SDcols=cols]⼀样 mult 当有i 中匹配到的有多⾏时,mult控制返回的⾏,\"all\"返回全部(默认),\"first\返回第⼀⾏,\"last\"返回最后⼀⾏ roll 当i中全部⾏匹配只有某⼀⾏不匹配时,填充该⾏空⽩,+Inf(或者TRUE)⽤上⼀⾏的值填充,-Inf⽤下⼀⾏的值填充,输⼊某数字时,表⽰能够填充的距离,near⽤最近的⾏填充 rollends 填充⾸尾不匹配的⾏,TRUE填充,FALSE不填充,与roll⼀同使⽤ which TRUE返回匹配的⾏号,NA返回不匹配的⾏号,默认FALSE返回匹配的⾏ .SDcols 取特定的列,然后.SD就包括了页写选定的特定列,可以对这些⼦集应⽤函数处理 allow.cartesian FALSE防⽌结果超出nrow(x)+nrow(i)⾏,常常因为i中有重复的列⽽超出。这⾥的cartesian和传统上的cartesian不⼀样。 因篇幅问题不能全部显示,请点此查看更多更全内容