关于R中的数据处理,上期我们介绍了plyr,这期我们接着介绍数据处理相关包dplyr。dplyr其实可以看做是plyr的升级版,dplyr中的d指dataframe,它专注于做基于数据框的处理。如果你熟悉了dplyr的这一套处理函数,它将会大大提升你处理数据的速度和效率。
dplyr中使用的示例数据继续沿用pgdat数据集,如果你想要获取该数据集的源代码,可以通过点击微信公众号右下角的案例数据得到。
select(.data, ...): 该函数实现了对数据集的列进行选取rename(.data, ...): 该函数可以对数据集的列名进行重命名,会保留完整数据集## 在pgdat数据集中选择场次、球员姓名、助攻数、得分、抢断、失误数据
pgdat1 <- select(pgdat, rk, player, ast, pts, stl, tov)
head(pgdat1)
## rk player ast pts stl tov
## 1 1 库里 6 46 2 2
## 2 2 库里 5 37 2 4
## 3 3 库里 8 17 1 2
## 4 4 库里 9 27 2 3
## 5 5 库里 15 21 3 3
## 6 6 库里 7 39 2 4
pgdat1 <- rename(pgdat1, name = player)
head(pgdat1)
## rk name ast pts stl tov
## 1 1 库里 6 46 2 2
## 2 2 库里 5 37 2 4
## 3 3 库里 8 17 1 2
## 4 4 库里 9 27 2 3
## 5 5 库里 15 21 3 3
## 6 6 库里 7 39 2 4
filter(.data, ...): 该函数返回匹配条件的数据行,实现了对数据集的行进行选取slice(.data, ...): 该函数可以通过行位置对数据集进行筛选,它也是对数据集的行进行选取的一种方式# 选择得分大于40分的数据
filter(pgdat1, pts > 40) %>% head
## rk name ast pts stl tov
## 1 1 库里 6 46 2 2
## 2 21 库里 3 41 4 5
## 3 24 库里 6 46 2 3
## 4 25 库里 8 51 0 5
## 5 26 库里 7 42 1 7
## 6 33 库里 2 51 3 7
# 选择助攻数大于15的数据
filter(pgdat1, ast > 15) %>% head
## rk name ast pts stl tov
## 1 6 保罗 16 20 1 2
## 2 13 保罗 16 15 2 3
## 3 17 保罗 16 12 5 3
## 4 44 保罗 19 21 1 3
## 5 58 保罗 18 18 0 2
## 6 6 沃尔 16 22 2 4
slice(pgdat1, 1:12) %>% head
## rk name ast pts stl tov
## 1 1 库里 6 46 2 2
## 2 2 库里 5 37 2 4
## 3 3 库里 8 17 1 2
## 4 4 库里 9 27 2 3
## 5 5 库里 15 21 3 3
## 6 6 库里 7 39 2 4
为了显示方便,这里的数据用head函数做了处理,仅显示前几行数据,%>%操作符会在本文下文中做介绍,此处可暂时忽略。
arrange(.data, ...): 该函数可以按照列变量对数据进行排序# 把数据按得分和助攻数降序排列
filter(pgdat1, desc(pts), desc(ast)) %>% head
## rk name ast pts stl tov
## 1 1 库里 6 46 2 2
## 2 2 库里 5 37 2 4
## 3 3 库里 8 17 1 2
## 4 4 库里 9 27 2 3
## 5 5 库里 15 21 3 3
## 6 6 库里 7 39 2 4
arrange的用法同plyr包中的arrange,想要详细了解参见前期文章数据处理之——plyr
distinct(.data, ...): 该函数可以按照列变量对数据进行去重处理# 把数据按球员进行去重处理
distinct(pgdat1, name)
## rk name ast pts stl tov
## 1 1 库里 6 46 2 2
## 2 1 康利 4 22 2 1
## 3 1 保罗 13 12 2 5
## 4 1 沃尔 13 15 1 3
## 5 1 威少 14 13 1 3
## 6 1 欧文 5 35 2 6
值得注意的是,用该函数进行数据列的去重处理时会仅保留最先出现的数据行。
mutate(.data, ...): 该函数增加新的数据列,并保留原先的数据列transmute(.data, ...): 该函数仅新增数据列,不保留原先的数据列## 计算球员的助攻失误比以及效率值
mutate(pgdat,
atr = ifelse(tov > 0, round(ast/tov,2),NA),
per = (pts+ast+reb+stl+blk)-(fga-fgm)-(fta-ftm)-tov) %>% head
## rk player wl match gs min fgp fgm fga p3p p3m p3a ftp
## 1 1 库里 胜 灰熊104-125勇士 1 30 62.5% 15 24 52.6% 10 19 100.0%
## 2 2 库里 胜 马刺86-92勇士 1 35 59.1% 13 22 44.4% 4 9 100.0%
## 3 3 库里 胜 灰熊99-100勇士 1 34 31.8% 7 22 21.4% 3 14
## 4 4 库里 胜 马刺101-112勇士 1 36 57.9% 11 19 42.9% 3 7 100.0%
## 5 5 库里 负 森林狼124-117勇士 1 43 28.0% 7 25 28.6% 4 14 100.0%
## 6 6 库里 胜 开拓者111-136勇士 1 35 61.9% 13 21 69.2% 9 13 100.0%
## ftm fta reb oreb dreb ast stl blk tov pf pts atr per
## 1 6 6 4 1 3 6 2 0 2 2 46 3.00 47
## 2 7 7 5 0 5 5 2 0 4 4 37 1.25 36
## 3 0 0 9 0 9 8 1 0 2 1 17 4.00 18
## 4 2 2 5 0 5 9 2 0 3 2 27 3.00 32
## 5 3 3 6 2 4 15 3 0 3 4 21 5.00 24
## 6 4 4 6 2 4 7 2 1 4 2 39 1.75 43
transmute(pgdat,
atr = ifelse(tov > 0, round(ast/tov,2),NA),
per = (pts+ast+reb+stl+blk)-(fga-fgm)-(fta-ftm)-tov) %>% head
## atr per
## 1 3.00 47
## 2 1.25 36
## 3 4.00 18
## 4 3.00 32
## 5 5.00 24
## 6 1.75 43
R的基本函数中transform也有类似mutate的功能,但是mutate的优势是可以在新增的数据列的基础上,继续新增数据列,transform则不行。这部分没做数据演示,可以自行尝试。
summarise(.data, ...): 该函数可以操作多列数据,生成单个数值。summarise(pgdat,
mast = mean(ast),
mpts = mean(pts))
## mast mpts
## 1 8.305489 21.7852
sample_n(tbl, size, ...): 给定抽样数据条数,按行进行数据抽样sample_frac(tbl, size, ...): 给定抽样数据比率,按行进行数据抽样# 随机抽取5条数据
sample_n(pgdat1, 5)
## rk name ast pts stl tov
## 1760 80 威少 10 33 2 5
## 2121 21 欧文 6 22 1 4
## 1713 33 威少 15 24 0 4
## 437 17 康利 9 12 2 1
## 849 9 保罗 7 25 1 4
# 按0.01比例抽取部分数据
sample_frac(pgdat1, 0.01)
## rk name ast pts stl tov
## 452 32 康利 4 9 4 2
## 878 38 保罗 9 30 3 4
## 1275 15 沃尔 11 20 2 1
## 899 59 保罗 5 14 4 5
group_by(.data, ...): 按数据列对数据进行分组# 按球员进行分组
by_player <- group_by(pgdat, player)
# 分组计算场均得分以及助攻数据
mydat <- summarise(by_player,
toln = n(),
mast = mean(ast),
mpts = mean(pts))
# ggplot2数据可视化
ggplot(mydat, aes(mpts,mast)) +
geom_point(aes(col = player), size=5,alpha = 3/5) +
labs(title="场均得分与助攻分布",x="得分",y="助攻")
lhs %.% rhs: 管道操作符管道操作符可以把前面计算的结果输出作为后续计算的输入,它有两个好处,第一,它的使用可以大大简化程序的中间赋值操作,从而提高代码写作的效率;第二,在查阅代码时,代码的逻辑层次结构也非常清晰易读。我们来看看如下示例:
dat1 <- group_by(pgdat, player)
dat2 <- select(dat1, rk, player, ast, pts, stl, tov)
dat3 <- summarise(dat2,
toln = n(),
mast = mean(ast),
mpts = mean(pts))
dat4 <- filter(dat3, mpts > 20 & mast > 10)
dat4
## Source: local data frame [1 x 4]
##
## player toln mast mpts
## (chr) (int) (dbl) (dbl)
## 1 威少 80 10.425 23.475
%>% 版pgdat %>%
group_by(player) %>%
select(rk, player, ast, pts, stl, tov) %>%
summarise(
toln = n(),
mast = mean(ast),
mpts = mean(pts)
) %>%
filter(mpts > 20 & mast > 10)
## Source: local data frame [1 x 4]
##
## player toln mast mpts
## (chr) (int) (dbl) (dbl)
## 1 威少 80 10.425 23.475
dplyr里有四种表关联函数,功能不尽相同,我们通过下面的例子来说明:
(df1 <- data_frame(x = c(1, 2), y = 2:1))
## Source: local data frame [2 x 2]
##
## x y
## (dbl) (int)
## 1 1 2
## 2 2 1
(df2 <- data_frame(x = c(1, 3), a = 10, b = "a"))
## Source: local data frame [2 x 3]
##
## x a b
## (dbl) (dbl) (chr)
## 1 1 10 a
## 2 3 10 a
inner_join(x, y) 内连接,提取x与y所有匹配上的数据df1 %>% inner_join(df2)
## Source: local data frame [1 x 4]
##
## x y a b
## (dbl) (int) (dbl) (chr)
## 1 1 2 10 a
left_join(x, y) 左连接,它包括所有的x数据,以及所有和x匹配上的y的数据。df1 %>% left_join(df2)
## Joining by: "x"
## Source: local data frame [2 x 4]
##
## x y a b
## (dbl) (int) (dbl) (chr)
## 1 1 2 10 a
## 2 2 1 NA NA
right_join(x, y) 右连接,它包括所有的y数据,以及所有和y匹配上的x的数据。df1 %>% right_join(df2)
## Joining by: "x"
## Source: local data frame [2 x 4]
##
## x y a b
## (dbl) (int) (dbl) (chr)
## 1 1 2 10 a
## 2 3 NA 10 a
df2 %>% left_join(df1)
## Joining by: "x"
## Source: local data frame [2 x 4]
##
## x a b y
## (dbl) (dbl) (chr) (int)
## 1 1 10 a 2
## 2 3 10 a NA
full_join() 全连接,它包括所有x和y数据.df1 %>% full_join(df2)
## Joining by: "x"
## Source: local data frame [3 x 4]
##
## x y a b
## (dbl) (int) (dbl) (chr)
## 1 1 2 10 a
## 2 2 1 NA NA
## 3 3 NA 10 a
intersect(x, y): 集合取交集union(x, y): 集合取并集setdiff(x, y): 取x中与y不同的部分(df1 <- data_frame(x = 1:2, y = c(1L, 1L)))
## Source: local data frame [2 x 2]
##
## x y
## (int) (int)
## 1 1 1
## 2 2 1
(df2 <- data_frame(x = 1:2, y = 1:2))
## Source: local data frame [2 x 2]
##
## x y
## (int) (int)
## 1 1 1
## 2 2 2
intersect(df1, df2)
## Source: local data frame [1 x 2]
##
## x y
## (int) (int)
## 1 1 1
union(df1, df2)
## Source: local data frame [3 x 2]
##
## x y
## (int) (int)
## 1 1 1
## 2 2 1
## 3 2 2
setdiff(df1, df2)
## Source: local data frame [1 x 2]
##
## x y
## (int) (int)
## 1 2 1
setdiff(df2, df1)
## Source: local data frame [1 x 2]
##
## x y
## (int) (int)
## 1 2 2
R中的两表操作函数与sql有如下等价关系:
| R | SQL |
|---|---|
inner_join() |
SELECT * FROM x JOIN y ON x.a = y.a |
left_join() |
SELECT * FROM x LEFT JOIN y ON x.a = y.a |
right_join() |
SELECT * FROM x RIGHT JOIN y ON x.a = y.a |
full_join() |
SELECT * FROM x FULL JOIN y ON x.a = y.a |
semi_join() |
SELECT * FROM x WHERE EXISTS (SELECT 1 FROM y WHERE x.a = y.a) |
anti_join() |
SELECT * FROM x WHERE NOT EXISTS (SELECT 1 FROM y WHERE x.a = y.a) |
intersect(x, y) |
SELECT * FROM x INTERSECT SELECT * FROM y |
union(x, y) |
SELECT * FROM x UNION SELECT * FROM y |
setdiff(x, y) |
SELECT * FROM x EXCEPT SELECT * FROM y |
本文介绍了dplyr的主要功能,但是它还有一些特殊的函数及功能,比如介绍了如何连接Mysql等结构化数据库;介绍了不用写sql语句便可对数据库中的数据进行查询的方法;还有当采用mutate函数进行数据整合时,如何利用窗口函数,这些听起来是不是很炫酷啊,那么就开始你的dplyr之旅吧!