【R语言】R包dplyr(一):数据转换

日常在工作中会碰到很多数据批量处理的问题,有的时候单独造轮子很费时间,这个时候我发现了dplyr这个R包,能帮助你解决数据处理中的绝大多数难题。dplyr是tidyverse中的一个核心包,用来进行数据操作。主要包括以下5个核心函数。

filter() 按值筛选观测

arrange() 对行进行重新排序

select() 按名称选取变量

mutate() 使用现有变量的函数创建新变量

summarize()将多个值总结为一个摘要统计量

这些函数都可以和group_by()函数联合起来使用,group_by()可以改变以上每个函数的作用范围,让其在整个数据集上的操作变为在每个分组上分别操作,这五个函数的工作方式都是相同的:

1.第一个参数是一个数据框。

2.随后的采纳数使用变量名称(不带引号)描述了在数据框上进行的操作。

3.输出结果是一个新的数据框。

Installation

# The easiest way to get dplyr is to install the whole tidyverse:
install.packages("tidyverse")

# Alternatively, install just dplyr:
install.packages("dplyr")

Demo

下面将以一个航班信息的数据集来演示一下dplyr这个包的用法。

#安装并加载这个数据集
install.packages('nycflights13')
library(nycflights13)

1.1 使用filter()筛选行

filter()函数可以基于观测的值筛选出一个观测子集。第一个参数是数据框名称,第二个参数及随后的参数是用来筛选数据框的表达式。

#筛选出1月1日出发的航班
> a <- filter(flights,month==1,day==1)
> head(a)
# A tibble: 6 x 19
   year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier
                                             
1  2013     1     1      517            515         2      830            819        11 UA     
2  2013     1     1      533            529         4      850            830        20 UA     
3  2013     1     1      542            540         2      923            850        33 AA     
4  2013     1     1      544            545        -1     1004           1022       -18 B6     
5  2013     1     1      554            600        -6      812            837       -25 DL     
6  2013     1     1      554            558        -4      740            728        12 UA     
# ... with 9 more variables: flight , tailnum , origin , dest ,
#   air_time , distance , hour , minute , time_hour 

1.1.1 比较运算符

R提供了一套标准的比较运算符:>,>=,<,<=,!=(不等于),==(等于)。

1.1.2 逻辑运算符

filter()中的多个参数是由“与”组合起来的:每个表达式都必须为真才能让一行观测包含在输出中。如果要实现其他类型的组合,你需要使用布尔运算符: &表示"与"(也就是交集), | 表示“或”, ! 表示"非"。

#找出11月或12月出发的航班
> b <- filter(flights,month==11 | month ==12)
> head(b)
# A tibble: 6 x 19
   year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier
                                             
1  2013    11     1        5           2359         6      352            345         7 B6     
2  2013    11     1       35           2250       105      123           2356        87 B6     
3  2013    11     1      455            500        -5      641            651       -10 US     
4  2013    11     1      539            545        -6      856            827        29 UA     
5  2013    11     1      542            545        -3      831            855       -24 AA     
6  2013    11     1      549            600       -11      912            923       -11 UA     
# ... with 9 more variables: flight , tailnum , origin , dest ,
#   air_time , distance , hour , minute , time_hour 

上述代码的简写形式为:

c <- filter(flights,month %in% c(11,12))

其中,x %in% y表示 选出x是y中的一个值时的所有行。

filter()只能筛选出条件为TRUE的行;它会排除那些条件为FALSE和NA的行,如果想保留缺失值,可以明确指出:

> df <- tibble(x=c(1,NA,3)) #tibble()用于构建一个数据框
> head(df)
# A tibble: 3 x 1
      x
  
1     1
2    NA
3     3
> filter(df,x>1)
# A tibble: 1 x 1
      x
  
1     3
> filter(df,is.na(x) | x > 1)
# A tibble: 2 x 1
      x
  
1    NA
2     3

1.2 使用arrange()排列行

arrange()函数的工作方式与filter()函数非常相似,但前者不选择行,而是改变行的顺序。它接受一个数据框和一组作为排序依据的列名作为参数。如果列名不止一个,那么就使用后面的列在前面排序的基础上继续排序。

#将flights按年月日排序
> d <- arrange(flights,year,month,day)
> head(d)
# A tibble: 6 x 19
   year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier
                                             
1  2013     1     1      517            515         2      830            819        11 UA     
2  2013     1     1      533            529         4      850            830        20 UA     
3  2013     1     1      542            540         2      923            850        33 AA     
4  2013     1     1      544            545        -1     1004           1022       -18 B6     
5  2013     1     1      554            600        -6      812            837       -25 DL     
6  2013     1     1      554            558        -4      740            728        12 UA     
# ... with 9 more variables: flight , tailnum , origin , dest ,
#   air_time , distance , hour , minute , time_hour 

使用desc()可以按列进行降序排序。

> e <- arrange(flights,desc(arr_delay))
> head(e)
# A tibble: 6 x 19
   year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier
                                             
1  2013     1     9      641            900      1301     1242           1530      1272 HA     
2  2013     6    15     1432           1935      1137     1607           2120      1127 MQ     
3  2013     1    10     1121           1635      1126     1239           1810      1109 MQ     
4  2013     9    20     1139           1845      1014     1457           2210      1007 AA     
5  2013     7    22      845           1600      1005     1044           1815       989 MQ     
6  2013     4    10     1100           1900       960     1342           2211       931 DL     
# ... with 9 more variables: flight , tailnum , origin , dest ,
#   air_time , distance , hour , minute , time_hour 

缺失值总是排在最后面。

> df <- tibble(x=c(5,NA,2))
> arrange(df,x)
# A tibble: 3 x 1
      x
  
1     2
2     5
3    NA
#将缺失值排在前面
> arrange(df,desc(is.na(x)))
# A tibble: 3 x 1
      x
  
1    NA
2     5
3     2

1.3 使用select()选择列

通过基于变量名的操作,select()可以让你快速生成一个有用的变量子集。

#按名称选择列
> f <- select(flights,year,month,day)
> head(f)
# A tibble: 6 x 3
   year month   day
    
1  2013     1     1
2  2013     1     1
3  2013     1     1
4  2013     1     1
5  2013     1     1
6  2013     1     1
# 选择‘year’和‘day’之间的所有列(包括year和day)
> g <- select(flights,year:day)
> head(g)
# A tibble: 6 x 3
   year month   day
    
1  2013     1     1
2  2013     1     1
3  2013     1     1
4  2013     1     1
5  2013     1     1
6  2013     1     1
#选择不再‘year’和‘day’之间的所有列(不包括‘year’和‘day’)
> h <- select(flights,-(year:day))
> head(h)
# A tibble: 6 x 16
  dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight tailnum
                                               
1      517            515         2      830            819        11 UA        1545 N14228 
2      533            529         4      850            830        20 UA        1714 N24211 
3      542            540         2      923            850        33 AA        1141 N619AA 
4      544            545        -1     1004           1022       -18 B6         725 N804JB 
5      554            600        -6      812            837       -25 DL         461 N668DN 
6      554            558        -4      740            728        12 UA        1696 N39463 
# ... with 7 more variables: origin , dest , air_time , distance ,
#   hour , minute , time_hour 

还可以在select()函数中使用一些辅助函数。

start_with("abc") 匹配以abc开头的名称

ends_with("xyz") 匹配以xyz结尾的名称

contains("ijk") 匹配包含ijk的名称

matches("(.) \ \ 1") 选择匹配正则表达式的那些变量,这个正则表达式会匹配名称中含有重复字符的变量。

num_range('x',1:3) 匹配x1,x2和x3

select()函数和everything()辅助函数结合起来使用,从而将几个变量移到数据框的开头。

> i <- select(flights,time_hour,air_time,everything())
> head(i)
# A tibble: 6 x 19
  time_hour           air_time  year month   day dep_time sched_dep_time dep_delay arr_time
                                             
1 2013-01-01 05:00:00      227  2013     1     1      517            515         2      830
2 2013-01-01 05:00:00      227  2013     1     1      533            529         4      850
3 2013-01-01 05:00:00      160  2013     1     1      542            540         2      923
4 2013-01-01 05:00:00      183  2013     1     1      544            545        -1     1004
5 2013-01-01 06:00:00      116  2013     1     1      554            600        -6      812
6 2013-01-01 05:00:00      150  2013     1     1      554            558        -4      740
# ... with 10 more variables: sched_arr_time , arr_delay , carrier ,
#   flight , tailnum , origin , dest , distance , hour ,
#   minute 

1.4 使用mutate()添加新变量

mutate()总是能够添加新列到数据集的最后,查看所有列的最简单的方式就是使用view()函数。

#先产生一个狭窄的数据集
> flights_sml <- select(flights,year:day,ends_with("delay"),distance,air_time)
> head(flights_sml)
# A tibble: 6 x 7
   year month   day dep_delay arr_delay distance air_time
                      
1  2013     1     1         2        11     1400      227
2  2013     1     1         4        20     1416      227
3  2013     1     1         2        33     1089      160
4  2013     1     1        -1       -18     1576      183
5  2013     1     1        -6       -25      762      116
6  2013     1     1        -4        12      719      150
#添加新列
> j <- mutate(flights_sml,gain=arr_delay - dep_delay,speed= distance/air_time * 60)
> head(j)
# A tibble: 6 x 9
   year month   day dep_delay arr_delay distance air_time  gain speed
                        
1  2013     1     1         2        11     1400      227     9  370.
2  2013     1     1         4        20     1416      227    16  374.
3  2013     1     1         2        33     1089      160    31  408.
4  2013     1     1        -1       -18     1576      183   -17  517.
5  2013     1     1        -6       -25      762      116   -19  394.
6  2013     1     1        -4        12      719      150    16  288.

创建新列后可以立马使用,如果只想保留新变量,可以使用transmute()函数。

> k <- transmute(flights,gain=arr_delay-dep_delay,hours=air_time/60,gain_per_hour=gain/hours)
> head(k)
# A tibble: 6 x 3
   gain hours gain_per_hour
            
1     9  3.78          2.38
2    16  3.78          4.23
3    31  2.67         11.6 
4   -17  3.05         -5.57
5   -19  1.93         -9.83
6    16  2.5           6.4 

1.5 使用summarize()进行分组摘要

summarize()可以将数据框折叠成一行。

> l <- summarise(flights,delay=mean(dep_delay,na.rm = T))
> head(l)
# A tibble: 1 x 1
  delay
  
1  12.6

如果summarize()不与group_by()一起使用,那么它本身也没有大用。group_by()可以将分析单位从整个数据集更改为单个分组。

#分组后就变成year,month,day相同的为一组,来计算它们的平均延误时间
> by_day <- group_by(flights,year,month,day) 
> m <- summarise(by_day,delay=mean(dep_delay,na.rm = T)) #通过设置na.rm=T,可以在计算前去除缺失值,否则会得到大量的缺失值。
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
> head(m)
# A tibble: 6 x 4
# Groups:   year, month [1]
   year month   day delay
     
1  2013     1     1 11.5 
2  2013     1     2 13.9 
3  2013     1     3 11.0 
4  2013     1     4  8.95
5  2013     1     5  5.73
6  2013     1     6  7.15

使用管道%>%提高代码的可读性,重点在于转换的过程而不是转换的对象。在阅读代码的时候,%>%读作 然后。以下这个例子研究每个目的地的距离和平均延误时间之间的关系。

> delay <- flights %>%
  group_by(dest) %>%
  summarise(
    count=n(), #对分组后的dest各元素个数进行计数
    dist=mean(distance,na.rm = T),
    delay=mean(arr_delay,na.rm = T)
  ) %>%
  filter(count > 20,dest != "HNL")
> head(delay)
# A tibble: 6 x 4
  dest  count  dist delay
     
1 ABQ     254 1826   4.38
2 ACK     265  199   4.85
3 ALB     439  143  14.4 
4 ATL   17215  757. 11.3 
5 AUS    2439 1514.  6.02
6 AVL     275  584.  8.00

寻找航班数量和平均延误时间之间的关系。

> library(ggplot2)
> not_cancelled <- flights %>%
  filter(!is.na(dep_delay),!is.na(arr_delay))
> delays <- not_cancelled %>%
  group_by(tailnum) %>%
  summarise(
    delay=mean(arr_delay,na.rm = T),
    n=n
  )
> ggplot(data = delays,mapping = aes(x=n,y=delay))+
  geom_point()

查看上述图形时,通常应该筛选掉那些观测数量非常少的分组,这样就可以避免受到特别小的分组中极端变动的影响,进而更好的发现数据模式。

delays %>%
  filter(n>25) %>%
  ggplot(mapping = aes(x=n,y=delay)) +
  geom_point()

另一个案例:使用Lahman包中的数据来计算大联盟的每个棒球队员的打击率(安打数/打数)。

#转换成tibble,以便输出更美观
> install.packages("Lahman")
> library(Lahman)
> batting <- as_tibble(Lahman::Batting)
> batters <- batting %>%
  group_by(playerID) %>%
  summarise(
    ba=sum(H,na.rm = T)/sum(AB,na.rm = T),
    ab=sum(AB,na.rm = T)
  )
> batters %>%
  filter(ab>100) %>%
  ggplot(mapping = aes(x=ab,y=ba))+
  geom_point()+
  geom_smooth(se=F)

1.6 常用的摘要函数

只使用均值,计数和求和是远远不够的,R中还提供了很多其他的常用摘要函数。

1.6.1 位置度量

median(x)用来求中位数,50%的x大于它,同时50%的x小于它。

将聚合函数和逻辑筛选组合起来使用。

> not_cancelled %>%
> group_by(year,month,day) %>%
  summarise(
    #平均延误时间
    avg_delay1=mean(arr_delay),
    #平均正延误时间
    avg_delay2=mean(arr_delay[arr_delay>0])
  )
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 5
# Groups:   year, month [12]
    year month   day avg_delay1 avg_delay2
                 
 1  2013     1     1     12.7         32.5
 2  2013     1     2     12.7         32.0
 3  2013     1     3      5.73        27.7
 4  2013     1     4     -1.93        28.3
 5  2013     1     5     -1.53        22.6
 6  2013     1     6      4.24        24.4
 7  2013     1     7     -4.95        27.8
 8  2013     1     8     -3.23        20.8
 9  2013     1     9     -0.264       25.6
10  2013     1    10     -5.90        27.3
# ... with 355 more rows
1.6.2 秩的度量:min(x),quantile(x,0.25)和max(x)

quantile(x,0.25)会找出x中从小到大顺序大于前25%而小于后75%的值。

示例:找出每天最早和最晚的航班何时出发.

> n <- not_cancelled %>%
  group_by(year,month,day) %>%
  summarise(
    first=min(dep_time),
    last=max(dep_time)
  )
> head(n)
# A tibble: 6 x 5
# Groups:   year, month [1]
   year month   day first  last
      
1  2013     1     1   517  2356
2  2013     1     2    42  2354
3  2013     1     3    32  2349
4  2013     1     4    25  2358
5  2013     1     5    14  2357
6  2013     1     6    16  2355
1.6.3 计数

前面已经使用过n()来返回当前分组的大小。如果想计算出非缺失值的数量,可以使用sum(!is.na(x))。如果想要计算出唯一值的数量,可以使用n_distinct(x)

#查看哪个目的地具有最多的航空公司
> o <- not_cancelled %>%
  group_by(dest) %>%
  summarise(carriers=n_distinct(carrier)) %>% #只计算唯一值
  arrange(desc(carriers))
> head(o)
# A tibble: 6 x 2
  dest  carriers
      
1 ATL          7
2 BOS          7
3 CLT          7
4 ORD          7
5 TPA          7
6 AUS          6

###关于n_distinct()的理解,可以运行一下代码,实际上是去除唯一值的重复值,只看唯一值的数量。
> x <- sample(1:10, 1e5, rep = TRUE)
> length(unique(x))
> n_distinct(x) #与上一行代码相当

因为计数太常用了,所以dplyr提供了一个简单的辅助函数,用于只需要计数的情况。

> not_cancelled %>%
   count(dest)
#计算每架飞机飞行的总里程,实际上就是求和。
> not_cancelled %>%
    count(tailnum,wt=distance)
1.6.4 逻辑值的计数和比例

当与数值型函数一同使用时,TRUE会转换成1,FALSE会转换成0,这使得sum()mean()非常适用于逻辑值:sum(x)可以找出x中TRUE的数量,mean(x)则可以找出比例。

#早上五点前出发的有多少架航班
> not_cancelled %>%
+   group_by(year,month,day) %>%
+   summarise(n_nearly=sum(dep_time<500))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day n_nearly
         
 1  2013     1     1        0
 2  2013     1     2        3
 3  2013     1     3        4
 4  2013     1     4        3
 5  2013     1     5        3
 6  2013     1     6        2
 7  2013     1     7        2
 8  2013     1     8        1
 9  2013     1     9        3
10  2013     1    10        3
# ... with 355 more rows

#延误超过1小时的航班比例是多少
> not_cancelled %>%
+   group_by(year,month,day) %>%
+   summarise(hour_perc=mean(arr_delay>60))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day hour_perc
          
 1  2013     1     1    0.0722
 2  2013     1     2    0.0851
 3  2013     1     3    0.0567
 4  2013     1     4    0.0396
 5  2013     1     5    0.0349
 6  2013     1     6    0.0470
 7  2013     1     7    0.0333
 8  2013     1     8    0.0213
 9  2013     1     9    0.0202
10  2013     1    10    0.0183
# ... with 355 more rows
1.7 分组新变量(和筛选器)

虽然与summarize()函数结合起来使用是最有效的,但分组也可以与mutate()filter()函数结合,以完成非常便捷的操作。

示例一:找出每个分组中最差的成员。

> flights_sml %>%
+   group_by(year,month,day) %>%
+   filter(rank(desc(arr_delay))<5)
# A tibble: 1,464 x 7
# Groups:   year, month, day [365]
    year month   day dep_delay arr_delay distance air_time
                       
 1  2013     1     1       853       851      184       41
 2  2013     1     1       290       338     1134      213
 3  2013     1     1       260       263      266       46
 4  2013     1     1       379       456     1092      222
 5  2013     1     2       268       288     1092      203
 6  2013     1     2       334       323      937      150
 7  2013     1     2       337       368     2586      346
 8  2013     1     2       379       359     1620      228
 9  2013     1     3       174       176     1008      152
10  2013     1     3       268       270     1069      158
# ... with 1,454 more rows

其中对rank()函数的使用做一个补充。

rank()函数是对一维度数组、向量x 进行排序。若x 为数值,则按照小数在前大数在后的原则进行排序。

rank() 将数据分为确定值与缺失值两种。缺失值可按先后排在确定值之前(na.last = FALSE);也可排在之后(na.last = TRUE),;也可保留,不参与排序(na.last = "keep")。

"first" 是最基本的排序,小数在前大数在后,相同元素先者在前后者在后。

"max" 是相同元素都取该组中最好的水平,即通常所讲的并列排序。

"min" 是相同元素都取该组中最差的水平,可以增大序列的等级差异。

"average" 是相同元素都取该组中的平均水平,该水平可能是个小数。

"random" 是相同元素随机编排次序,避免了“先到先得”,“权重”优于“先后顺序”的机制增大了随机的程度。

> rank(t <- c(6.8, 8.1, 7.2))
[1] 1 3 2

示例二:找出大于某个阈值的所有分组:

 > popular_dests <- flights %>%
+   group_by(dest) %>%
+   filter(n()>365)
> head(popular_dests)
# A tibble: 6 x 19
# Groups:   dest [5]
   year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier
                                             
1  2013     1     1      517            515         2      830            819        11 UA     
2  2013     1     1      533            529         4      850            830        20 UA     
3  2013     1     1      542            540         2      923            850        33 AA     
4  2013     1     1      544            545        -1     1004           1022       -18 B6     
5  2013     1     1      554            600        -6      812            837       -25 DL     
6  2013     1     1      554            558        -4      740            728        12 UA     
# ... with 9 more variables: flight , tailnum , origin , dest ,
#   air_time , distance , hour , minute , time_hour 

参考链接:
1.https://dplyr.tidyverse.org/
2.《R for Data Science》

你可能感兴趣的:(【R语言】R包dplyr(一):数据转换)