dplyr简介

关于R中的数据处理,上期我们介绍了plyr,这期我们接着介绍数据处理相关包dplyrdplyr其实可以看做是plyr的升级版,dplyr中的ddataframe,它专注于做基于数据框的处理。如果你熟悉了dplyr的这一套处理函数,它将会大大提升你处理数据的速度和效率。

示例数据

dplyr中使用的示例数据继续沿用pgdat数据集,如果你想要获取该数据集的源代码,可以通过点击微信公众号右下角的案例数据得到。

单表操作函数

select

## 在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/slice

# 选择得分大于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

# 把数据按得分和助攻数降序排列
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

# 把数据按球员进行去重处理
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/transmute

## 计算球员的助攻失误比以及效率值
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

summarise(pgdat,
  mast = mean(ast),          
  mpts = mean(pts))
##       mast    mpts
## 1 8.305489 21.7852

sample_n/sample_frac

# 随机抽取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

# 按球员进行分组
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="助攻")

管道操作 %>% or %.%

管道操作符可以把前面计算的结果输出作为后续计算的输入,它有两个好处,第一,它的使用可以大大简化程序的中间赋值操作,从而提高代码写作的效率;第二,在查阅代码时,代码的逻辑层次结构也非常清晰易读。我们来看看如下示例:

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
df1 %>% inner_join(df2) 
## Source: local data frame [1 x 4]
## 
##       x     y     a     b
##   (dbl) (int) (dbl) (chr)
## 1     1     2    10     a
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
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
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

集合操作

(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之旅吧!

长按识别图中二维码

示意图