R高效数据处理包
标签(空格分隔): R dplyr data.table
之前在某公司实习的时候,需要结合shiny从数据库选取数据进行网页实时交互的可视化。然而我发现每次类似于 updateSelectizeInput
的更新数据,我都会直接操作数据库select * from * where *
,结果运行的时候发现实时更新贼慢,根本不是实时交互啊,至少延迟一分钟(几十万级数据集)。于是,去请教总工,总工就指导我学习dplyr、data.table,一试简直让我喜极而泣啊,延迟减少到了十秒钟。由于效果显著,就把它献给我的第一篇博客,上交学习心得体会。
dplyr
高效处理大量数据,可以高效快速的对数据进行筛选、变形、汇总、分组、管道等各式各样的数据处理操作
1. 数据集
本文用datasets
中的airquality
来做实验。
library(dplyr)
library(datasets)
head(airquality) ##dplyr有类似的函数tbl_df()
Ozone Solar.R Wind Temp Month Day
1 41 190 7.4 67 5 1
2 36 118 8.0 72 5 2
3 12 149 12.6 74 5 3
4 18 313 11.5 62 5 4
5 NA NA 14.3 56 5 5
6 28 NA 14.9 66 5 6
2.管道操作符 %>%
R的管道操作符
%>%
,可以省略第一个参数,可以链接一长串的操作函数。当对数据进行一系列的操作而又不想一个一个保存中间值,这无疑是一个最佳选择。
airquality%>%filter(Month==5)%>%group_by(Month)%>%summarize(mean(Temp,na.rm=TRUE))
Month `mean(Temp, na.rm = TRUE)`
<int> <dbl>
1 5 65.54839
3.筛选:select/filter
3.1 筛选列select()
根据列名选取某几列:
airquality%>%select(Ozone,Month,Day)
Ozone Month Day
1 41 5 1
2 36 5 2
3 12 5 3
4 18 5 4
5 NA 5 5
6 28 5 6
用:
,根据范围选择列:
airquality%>%select(Ozone:Month)
Ozone Solar.R Wind Temp Month
1 41 190 7.4 67 5
2 36 118 8.0 72 5
3 12 149 12.6 74 5
4 18 313 11.5 62 5
5 NA NA 14.3 56 5
6 28 NA 14.9 66 5
-
,选取除该列之外的列:
airquality%>%select(-Ozone,-Month)
Solar.R Wind Temp Day
1 190 7.4 67 1
2 118 8.0 72 2
3 149 12.6 74 3
4 313 11.5 62 4
5 NA 14.3 56 5
6 NA 14.9 66 6
starts_with()
,选取以某字符串开头列名的列:
airquality%>%select(starts_with('S'))
Solar.R
1 190
2 118
3 149
4 313
5 NA
6 NA
ends_with()
,选取以某字符串结尾的列名的列:
airquality%>%select(ends_with('d'))
Wind
1 7.4
2 8.0
3 12.6
4 11.5
5 14.3
6 14.9
contains()
,选取包含某字符串的列名的列:
airquality%>%select(contains('o'))
Ozone Solar.R Month
1 41 190 5
2 36 118 5
3 12 149 5
4 18 313 5
5 NA NA 5
6 28 NA 5
matcthes()
,选取符合正则匹配的列名的列:
airquality%>%select(matches('So?'))
Solar.R
1 190
2 118
3 149
4 313
5 NA
6 NA
one_of()
,从一组列名里选择列:
airquality%>%select(one_of('Day','year','Month'))
Day Month
1 1 5
2 2 5
3 3 5
4 4 5
5 5 5
6 6 5
3.2 筛选行filter()
使用逻辑操作符(>、<、>=、<=、%in%,&,|等)来构成逻辑表达式
airquality%>%filter(Month==5 & Day>=10)
Ozone Solar.R Wind Temp Month Day
1 NA 194 8.6 69 5 10
2 7 NA 6.9 74 5 11
3 16 256 9.7 69 5 12
4 11 290 9.2 66 5 13
5 14 274 10.9 68 5 14
6 18 65 13.2 58 5 15
airquality%>%filter(Month %in% c(6,7,10))
Ozone Solar.R Wind Temp Month Day
1 NA 286 8.6 78 6 1
2 NA 287 9.7 74 6 2
3 NA 242 16.1 67 6 3
4 NA 186 9.2 84 6 4
5 NA 220 8.6 85 6 5
6 NA 264 14.3 79 6 6
4. 对行排序 arrange()
airquality%>%arrange(desc(Month),Day)
Ozone Solar.R Wind Temp Month Day
1 96 167 6.9 91 9 1
2 78 197 5.1 92 9 2
3 73 183 2.8 93 9 3
4 91 189 4.6 93 9 4
5 47 95 7.4 87 9 5
6 32 92 15.5 84 9 6
7 20 252 10.9 80 9 7
8 23 220 10.3 78 9 8
9 21 230 10.9 75 9 9
10 24 259 9.7 73 9 10
airquality%>%arrange(Month,Day)%>%filter(Month<=8)%>%head
Ozone Solar.R Wind Temp Month Day
1 41 190 7.4 67 5 1
2 36 118 8.0 72 5 2
3 12 149 12.6 74 5 3
4 18 313 11.5 62 5 4
5 NA NA 14.3 56 5 5
6 28 NA 14.9 66 5 6
5. 添加新列,mutate()
airquality%>%mutate(TempInC=(Temp-32)*5/9)%>%head
Ozone Solar.R Wind Temp Month Day TempInC
1 41 190 7.4 67 5 1 19.44444
2 36 118 8.0 72 5 2 22.22222
3 12 149 12.6 74 5 3 23.33333
4 18 313 11.5 62 5 4 16.66667
5 NA NA 14.3 56 5 5 13.33333
6 28 NA 14.9 66 5 6 18.88889
6. 计算某个变量的统计特性,summarise()
summarise()
可以计算某列的统计特征,结合如sd()
,mean()
,min()
,max()
,median()
,sum()
,n()
(返回变量向量的长度),first()
(返回向量的第一个值),last()
,n_distinct()
(变量向量中不同值的数量)等函数
airquality%>%summarise(min(Wind))
min(Wind)
1 1.7
summarise_each()
允许对多列同时进行统计(PS:我在R上试验,提醒已经逐渐弃用summarise_each(),推荐使用summarise_at())
airquality%>%summarise_at(c('Month','Day'),mean)
airquality%>%summarise_each(funs(mean),Month,Day)
Month Day
1 6.993464 15.80392
7. 根据某列值去掉重复行,distinct()
airquality%>%distinct(Month,.keep_all=TRUE) ## keep_all参数设置选择所有列,默认为FALSE
Ozone Solar.R Wind Temp Month Day
1 41 190 7.4 67 5 1
2 NA 286 8.6 78 6 1
3 135 269 4.1 84 7 1
4 39 83 6.9 81 8 1
5 96 167 6.9 91 9 1
8. 分组,group_by
group_by
group_by()是dplyr中的一个重要函数。正如我们之前提到的,它与“分拆适用组合”的概念有关。我们实际上想要通过一些变量拆分数据观测值,将功能应用于各组数据,然后组合输出。
airquality%>%group_by(Month)%>%summarise(min(Wind))
Month `min(Wind)`
<int> <dbl>
1 5 5.7
2 6 1.7
3 7 4.1
4 8 2.3
5 9 2.8
ungroup()
从数据中移除分组信息
9. dplyr中关于结合两个数据表(x,y)的函数
inner_join :只包含同时出现在x,y表中的行
left_join: 包含所有x中以及y中匹配的行
right_join: 包含所有y中以及x中匹配的行
full_join: 包含x,y中所有的行和列
semi_join:包含x中,在y中有匹配的行
anti_join: 包含x中不匹配y的行
data.table
fread()
Similar to read.table but faster and more convenient.fread()
accepts http and https URLs directly as well as operating system commands such as sed and awk output
data=fread('E:/ds/R dplyr/flights2014.csv')
data.table与数据库相对应的语法规则,对于数据集DT,i选取行,根据分组by对列j进行计算
DT[i, j, by]
## R: i j by
## SQL: where select | update group by
1. Basics-数据筛选
1.1 按条件筛选行
对应于R的基本函数subset()、dplyr的filter(),data.table()筛选行的方式比较简单。
单变量筛选
data[month %in% c(5,6,7,8)]
year month day dep_time dep_delay arr_time arr_delay
1: 2014 5 1 1743 43 1955 5
2: 2014 5 1 759 -1 1057 -38
3: 2014 5 1 1540 0 1854 14
4: 2014 5 1 1823 78 2104 54
5: 2014 5 1 756 -4 912 2
---
106459: 2014 8 31 1123 -7 1251 -29
106460: 2014 8 31 1542 -8 1714 -11
106461: 2014 8 31 1502 -8 1638 -12
106462: 2014 8 31 1847 18 2039 25
106463: 2014 8 31 1441 126 1557 107
多变量筛选
data[month %in% c(5,6,7,8) & day>=20]
year month day dep_time dep_delay arr_time arr_delay
1: 2014 5 20 1656 -4 1933 -7
2: 2014 5 20 758 -7 1143 8
3: 2014 5 20 1620 0 1841 -39
4: 2014 5 20 1710 -5 1959 -16
5: 2014 5 20 815 -5 924 -6
---
41115: 2014 8 31 1123 -7 1251 -29
41116: 2014 8 31 1542 -8 1714 -11
41117: 2014 8 31 1502 -8 1638 -12
41118: 2014 8 31 1847 18 2039 25
41119: 2014 8 31 1441 126 1557 107
切片操作
data[1:5]
year month day dep_time dep_delay arr_time arr_delay
1: 2014 1 1 914 14 1238 13
2: 2014 1 1 1157 -3 1523 13
3: 2014 1 1 1902 2 2224 9
4: 2014 1 1 722 -8 1014 -26
根据变量对行进行排序
data[order(-month,arr_time)] ##默认升序,加‘-‘表示降序排
year month day dep_time dep_delay arr_time arr_delay
1: 2014 10 1 2113 43 1 70
2: 2014 10 1 2255 146 1 130
3: 2014 10 2 2014 49 1 132
4: 2014 10 5 2109 84 1 59
5: 2014 10 12 2100 0 1 -24
1.2 筛选列
筛选列,返回的形式为vector
head(data[,arr_delay])
[1] 13 13 9 -26 1 0
筛选列,返回的形式为data.table,使用list()装饰列或者DT[,.(colnames)]
head(data[,list(arr_delay)])
head(data[,.(arr_delay)])
arr_delay
1: 13
2: 13
3: 9
4: -26
5: 1
6: 0
对筛选出的重命名
head(data[,.(delay_arr=arr_delay)])
delay_arr
1: 13
2: 13
3: 9
4: -26
5: 1
6: 0
Special Notes:使用参数with=False,可以像普通的数据框一样对行/列进行操作,使用列的名称或者索引筛选列
data[,c('day','month'),with=FALSE]
data[,c(3,2),with=FALSE]
day month
1: 1 1
2: 1 1
3: 1 1
4: 1 1
5: 1 1
1.3 对筛选的列进行操作和计算
data.table的j可以处理不仅仅是选择列 ——它可以处理表达式,即对列进行计算。这不应该是令人惊讶的,因为列可以被称为是变量。那么我们应该能够通过调用这些变量的函数来计算。
data[,sum((arr_delay+dep_delay)<0)]
141814
1.4 筛选行并对列进行计算
data[origin=='JFK'&month==6,.(m_arr=mean(arr_delay),m_dep=mean(dep_delay))]
m_arr m_dep
1: 5.839349 9.807884
Special symbol .N:
.N是一个特殊的内置变量,用于保存当前组中的观察次数。当我们与gruop_by结合在一起特别有用。没有分组操作的情况下,它只返回子集中的行数。相当于对筛选出的行任何一列,执行length()函数
data[origin=='JFK'&month==6,.N]
nrow(data[origin=='JFK'&month==6])
8422
2. 整合函数
2.1 使用by进行分组
data[,.(.N),by=.(origin)]
data[,.(.N),by='origin']
origin N
1: JFK 81483
2: LGA 84433
3: EWR 87400
j中只有一个列或表达式被引用时,我们可以删除符号。这纯粹是为了方便。我们可以改为:
data[,.N,by=origin]
by
可以接受多列作为输入,我们只需要提供分组所需的列:
data[carrier=='AA',.(.N),by=.(origin,dest)]
origin dest N
1: JFK LAX 3387
2: LGA PBI 245
3: EWR LAX 62
4: JFK MIA 1876
5: JFK SEA 298
6: EWR MIA 848
7: JFK SFO 1312
data[carrier=='AA',.(arr_mean=mean(arr_delay),dep_mean=mean(dep_delay)),by=.(origin,dest,month)]
origin dest month arr_mean dep_mean
1: JFK LAX 1 6.590361 14.2289157
2: LGA PBI 1 -7.758621 0.3103448
3: EWR LAX 1 1.366667 7.5000000
4: JFK MIA 1 15.720670 18.7430168
5: JFK SEA 1 14.357143 30.7500000
2.2 排序-keyby
data[carrier=='AA',.(arr_mean=mean(arr_delay),dep_mean=mean(dep_delay)),keyby=.(origin,dest,month)]
origin dest month arr_mean dep_mean
1: EWR DFW 1 6.427673 10.0125786
2: EWR DFW 2 10.536765 11.3455882
3: EWR DFW 3 12.865031 8.0797546
4: EWR DFW 4 17.792683 12.9207317
5: EWR DFW 5 18.487805 18.6829268
Special Notes:实际上,
keyby
做的不仅仅是排序,它也可以通过设置一个叫sorted
的属性值在排序之后设置一个key值。
2.3 联合操作
可以一个接一个地表达形式,形成一连串的操作 DT[ … ][ … ][ … ].
或者:
DT[ …
][ …
][ …
]
data[carrier=='AA',.(arr_mean=mean(arr_delay),dep_mean=mean(dep_delay)),keyby=.(origin,dest,month)][order(origin,-dest)]
origin dest month arr_mean dep_mean
1: EWR PHX 7 -5.103448 0.2758621
2: EWR PHX 8 3.548387 6.2258065
3: EWR PHX 9 -4.233333 -1.6666667
4: EWR PHX 10 -3.032258 -4.2903226
5: EWR MIA 1 11.011236 12.1235955
2.4 by中的表达式
data[,.N,.(dep_delay>0,arr_delay>0)]
dep_delay arr_delay N
1: TRUE TRUE 72836
2: FALSE TRUE 34583
3: FALSE FALSE 119304
4: TRUE FALSE 26593
2.5 对多列同时进行运算,j
-`.SD
Special symbol:
.SD
它表示子集或数据, 它本身就是一个data.table,它保存了运行by后的分组数据
DT
ID a b c
1 b 1 7 13
2 b 2 8 14
3 b 3 9 15
4 a 4 10 16
5 a 5 11 17
6 c 6 12 18
DT<-as.data.table(DT)
DT[,print(.SD),by=ID]
a b c
1: 1 7 13
2: 2 8 14
3: 3 9 15
a b c
1: 4 10 16
2: 5 11 17
a b c
1: 6 12 18
.SD
包含了除分组列外的所有列,并且保存了和原始数据顺序一样的分组数据结果。对多列进行运算,可以使用R的基本函数lapply()
DT[,lapply(.SD,mean),by=ID]
ID a b c
1: b 2.0 8.0 14.0
2: a 4.5 10.5 16.5
3: c 6.0 12.0 18.0
Special symbol:
.SDcols
,表示只对指定的几列进行运 算,它的参数为列名或者列的索引。与with=FALSE
相似,也可以使用-
或者!
删除不需要的列,或者选择连续的列colA:colB
,不选择某些连续的列!(colA:colB)
或者-(colA:colB)
data[carrier=='AA',lapply(.SD,mean),by=.(origin,dest,month),.SDcols=c('arr_delay','dep_delay')]
origin dest month arr_delay dep_delay
1: JFK LAX 1 6.590361 14.2289157
2: LGA PBI 1 -7.758621 0.3103448
3: EWR LAX 1 1.366667 7.5000000
4: JFK MIA 1 15.720670 18.7430168
5: JFK SEA 1 14.357143 30.7500000
2.6 使用.SD
提取分组后的子集
data[,head(.SD,2),by=month]
month year day dep_time dep_delay arr_time arr_delay
1: 1 2014 1 914 14 1238 13
2: 1 2014 1 1157 -3 1523 13
3: 2 2014 1 859 -1 1226 1
4: 2 2014 1 1155 -5 1528 3
5: 3 2014 1 849 -11 1306 36
6: 3 2014 1 1157 -3 1529 14
7: 4 2014 1 1812 -8 1927 -23
8: 4 2014 1 1812 -8 1949 -11
2.7 位置j使用的灵活性
DT[,.(val=c(a,b)),by=ID]
##使用基本函数c()并联向量
ID val
1: b 1
2: b 2
3: b 3
4: b 7
5: b 8
6: b 9
7: a 4
8: a 5
9: a 10
10: a 11
11: c 6
12: c 12
如果想要返回的是一个list型,使用list()
来装饰
DT[,.(val=list(c(a,b))),by=ID]
ID val
1: b 1,2,3,7,8,9
2: a 4, 5,10,11
3: c 6,12
一旦开始内部使用j,你会意识到语法的强大程度。一个非常有用的方式来理解它是在print()的帮助下。
## (1) look at the difference between
DT[, print(c(a,b)), by = ID]
# [1] 1 2 3 7 8 9
# [1] 4 5 10 11
# [1] 6 12
# Empty data.table (0 rows) of 1 col: ID
## (2) and
DT[, print(list(c(a,b))), by = ID]
# [[1]]
# [1] 1 2 3 7 8 9
#
# [[1]]
# [1] 4 5 10 11
#
# [[1]]
# [1] 6 12
# Empty data.table (0 rows) of 1 col: ID
在(1)中,对于每个组,返回一个向量,其长度为6,4,2。然而,(2)返回每个组的长度为1的列表,其第一个元素保持向量为6,4,2。因此(1)返回6 + 4 + 2 = 12的长度,而(2)返回1 + 1 + 1 = 3。