美文网首页生信猿我爱编程R语言
高效数据整理工具——dplyr(一)

高效数据整理工具——dplyr(一)

作者: 井底蛙蛙呱呱呱 | 来源:发表于2018-04-07 16:17 被阅读312次

    在前面的数据处理笔记中提到了多个简单的数据处理函数(包括R内置的transform、aggregate、by、summary其他操作)以及工具包(主要为reshape、reshape2),这些工具虽然用起来比较方便,但是功能比较少,如aggregate和reshape2包groupby后处理都只能返回一个值,本篇将介绍一个更为强大而又系统的用来处理数据框结构数据的工具包——dplyr。值得一提的是,reshape、reshape2、plyr、dplyr以及ggplot2的作者都是同一人—— Hadley Wickham。下面将通过dplyr包官网中的示例了解一下大神的杰作。

    概览

    20160607113522768.jpg 20160607113514383.jpg

    dplyr包的功能应用方面主要包括3个:Single table verbs, Two-table verbs和Databases。
    本文将主要了解dplyr对单个数据表(Single table,也即数据框)的处理。使用的示例数据集来自于hflights包,值得注意的是hflights数据结构类型是tibbletibble是Rstudio开发的一种新的数据类型,被认为是未来data.frame的替代,使用as_tibble()可将data.frame转化为tibble,简单了解可看这里——R语言数据科学新类型tibble。dplyr常用的数据处理函数主要包括:

    • filter() 筛选符合条件的记录(rows)
    • arrange() 对数据进行排序
    • select()rename() 通过列名来选取变量
    • mutate()transmute() 通过已有列创建(计算并赋值)新列
    • summarise() 聚合数据,一般先分组(groupby)后再通过聚合函数返回分组的值
    • sample_n()sample_frac() 随机抽样函数(随机选取rows)
    • group_by() 分组函数
    • %>% 管道操作(pipe),连接多个操作

    1、基本操作

    1.1 筛选:filter()

    根据逻辑判断筛选出符合要求的子数据集,如:

    ### 查看数据,可看到flights是tibble类型,而且直接读取也不会全部显示,很智能人性化
    > library(nycflights13)
    > dim(flights)
    [1] 336776     19
    > flights
    # A tibble: 336,776 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
     1  2013     1     1      517            515        2.      830            819
     2  2013     1     1      533            529        4.      850            830
     3  2013     1     1      542            540        2.      923            850
     4  2013     1     1      544            545       -1.     1004           1022
     5  2013     1     1      554            600       -6.      812            837
     6  2013     1     1      554            558       -4.      740            728
     7  2013     1     1      555            600       -5.      913            854
     8  2013     1     1      557            600       -3.      709            723
     9  2013     1     1      557            600       -3.      838            846
    10  2013     1     1      558            600       -2.      753            745
    # ... with 336,766 more rows, and 11 more variables: arr_delay <dbl>,
    #   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
    #   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
    

    使用filter()筛选数据,格式为filter(data, formula),formula为逻辑判断,判断符号有==, >, >= etc,&, |, !, xor(),is.na(),between(), near()等。

    ### 筛选出month==1和day==2的行
    > filter(flights, month == 1, day == 1)
    Error in match.arg(method) : object 'day' not found
    ### 这里报错是因为有多个载入的包都含有filter函数,因此如下使用dplyr的filter函数
    > dplyr::filter(flights, month==1, day==1)
    # A tibble: 842 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     1     1      517            515        2.      830            819       11.
     2  2013     1     1      533            529        4.      850            830       20.
     3  2013     1     1      542            540        2.      923            850       33.
     4  2013     1     1      544            545       -1.     1004           1022      -18.
     5  2013     1     1      554            600       -6.      812            837      -25.
     6  2013     1     1      554            558       -4.      740            728       12.
     7  2013     1     1      555            600       -5.      913            854       19.
     8  2013     1     1      557            600       -3.      709            723      -14.
     9  2013     1     1      557            600       -3.      838            846       -8.
    10  2013     1     1      558            600       -2.      753            745        8.
    # ... with 832 more rows, and 10 more variables: carrier <chr>, flight <int>, tailnum <chr>,
    #   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
    #   time_hour <dttm>
    
    ### 使用R内置方法进行同样的处理
    > flights[flights$month==1 & flights$day==1,]
    # A tibble: 842 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     1     1      517            515        2.      830            819       11.
     2  2013     1     1      533            529        4.      850            830       20.
     3  2013     1     1      542            540        2.      923            850       33.
     4  2013     1     1      544            545       -1.     1004           1022      -18.
     5  2013     1     1      554            600       -6.      812            837      -25.
     6  2013     1     1      554            558       -4.      740            728       12.
     7  2013     1     1      555            600       -5.      913            854       19.
     8  2013     1     1      557            600       -3.      709            723      -14.
     9  2013     1     1      557            600       -3.      838            846       -8.
    10  2013     1     1      558            600       -2.      753            745        8.
    # ... with 832 more rows, and 10 more variables: carrier <chr>, flight <int>, tailnum <chr>,
    #   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
    #   time_hour <dttm>
    

    让我们再来继续比较下,不同的方法来选择子集。

    df <- expand.grid(A = 1:100, B = 1:100, C = 1:100)
    df$value <- 1:nrow(df)
    
    library(dplyr); library(microbenchmark)
    f1 <- function() subset(df, A == 1 & B == 3 | A == 3 & B == 2)
    f2 <- function() filter(df, A == 1 & B == 3 | A == 3 & B == 2)
    f3 <- function() df[with(df, A == 1 & B == 3 | A == 3 & B == 2), ]
    f4 <- function() df[(df$A == 1 & df$B == 3) | (df$A == 3 & df$B == 2),]
    
    microbenchmark(subset = f1(), filter = f2(), with = f3(), "$" = f4())
    # Unit: milliseconds
    #    expr      min       lq     mean   median       uq      max neval
    #  subset 47.42671 49.99802 75.95385 92.24430 96.05960 141.2964   100
    #  filter 36.94019 38.77325 60.22831 42.64112 84.35896 155.0145   100
    #    with 38.90918 44.36299 71.29214 86.39629 88.89008 134.7670   100
    #       $ 40.22723 44.08606 71.32186 86.71372 89.59275 133.1132   100
    

    1.2 排序:arrange()

    根据某一列或多列进行排序,格式为:arrange(data, colnames , ...),默认为升序排列,使用desc可进行降序排序。

    ### 升序排序
    > dplyr::arrange(flights, month, day)
    # A tibble: 336,776 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     1     1      517            515        2.      830            819       11.
     2  2013     1     1      533            529        4.      850            830       20.
     3  2013     1     1      542            540        2.      923            850       33.
     4  2013     1     1      544            545       -1.     1004           1022      -18.
     5  2013     1     1      554            600       -6.      812            837      -25.
     6  2013     1     1      554            558       -4.      740            728       12.
     7  2013     1     1      555            600       -5.      913            854       19.
     8  2013     1     1      557            600       -3.      709            723      -14.
     9  2013     1     1      557            600       -3.      838            846       -8.
    10  2013     1     1      558            600       -2.      753            745        8.
    # ... with 336,766 more rows, and 10 more variables: carrier <chr>, flight <int>,
    #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
    #   minute <dbl>, time_hour <dttm>
    ### 降序排序
    > dplyr::arrange(flights, desc(month, day))
    # A tibble: 336,776 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013    12     1       13           2359       14.      446            445        1.
     2  2013    12     1       17           2359       18.      443            437        6.
     3  2013    12     1      453            500       -7.      636            651      -15.
     4  2013    12     1      520            515        5.      749            808      -19.
     5  2013    12     1      536            540       -4.      845            850       -5.
     6  2013    12     1      540            550      -10.     1005           1027      -22.
     7  2013    12     1      541            545       -4.      734            755      -21.
     8  2013    12     1      546            545        1.      826            835       -9.
     9  2013    12     1      549            600      -11.      648            659      -11.
    10  2013    12     1      550            600      -10.      825            854      -29.
    # ... with 336,766 more rows, and 10 more variables: carrier <chr>, flight <int>,
    #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
    #   minute <dbl>, time_hour <dttm>
    
    ### 使用R内置的order函数进行排序
    > flights[order(flights$month,flights$day),]
    # A tibble: 336,776 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     1     1      517            515        2.      830            819       11.
     2  2013     1     1      533            529        4.      850            830       20.
     3  2013     1     1      542            540        2.      923            850       33.
     4  2013     1     1      544            545       -1.     1004           1022      -18.
     5  2013     1     1      554            600       -6.      812            837      -25.
     6  2013     1     1      554            558       -4.      740            728       12.
     7  2013     1     1      555            600       -5.      913            854       19.
     8  2013     1     1      557            600       -3.      709            723      -14.
     9  2013     1     1      557            600       -3.      838            846       -8.
    10  2013     1     1      558            600       -2.      753            745        8.
    # ... with 336,766 more rows, and 10 more variables: carrier <chr>, flight <int>,
    #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
    #   minute <dbl>, time_hour <dttm>
    

    1.3 选择与重命名:select()rename()

    通过列名来选择子数据集,格式为:select(data, colnames, ...),同样的select也可以使用-来排除列名。同时select()还具有重命名的功能,但是进行选择并重命名时他也只返回子集,而rename()则能重命名特定列并返回所有列。select()支持的选取方式还有c(colnames...)year:day等多种方式。

    ### 选择year,month,day 3列作为子集
    > df<-dplyr::select(flights,year,month,DAY=day);df
    # A tibble: 336,776 x 3
        year month   DAY
       <int> <int> <int>
     1  2013     1     1
     2  2013     1     1
     3  2013     1     1
     4  2013     1     1
     5  2013     1     1
     6  2013     1     1
     7  2013     1     1
     8  2013     1     1
     9  2013     1     1
    10  2013     1     1
    # ... with 336,766 more rows
    > dplyr::rename(flights,DAY=day)
    # A tibble: 336,776 x 19
        year month   DAY dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     1     1      517            515        2.      830            819       11.
     2  2013     1     1      533            529        4.      850            830       20.
     3  2013     1     1      542            540        2.      923            850       33.
     4  2013     1     1      544            545       -1.     1004           1022      -18.
     5  2013     1     1      554            600       -6.      812            837      -25.
     6  2013     1     1      554            558       -4.      740            728       12.
     7  2013     1     1      555            600       -5.      913            854       19.
     8  2013     1     1      557            600       -3.      709            723      -14.
     9  2013     1     1      557            600       -3.      838            846       -8.
    10  2013     1     1      558            600       -2.      753            745        8.
    # ... with 336,766 more rows, and 10 more variables: carrier <chr>, flight <int>,
    #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
    #   minute <dbl>, time_hour <dttm>
    
    ### R内置的选择子集的方法
    > flights[c('year','month','day')]
    # A tibble: 336,776 x 3
        year month   day
       <int> <int> <int>
     1  2013     1     1
     2  2013     1     1
     3  2013     1     1
     4  2013     1     1
     5  2013     1     1
     6  2013     1     1
     7  2013     1     1
     8  2013     1     1
     9  2013     1     1
    10  2013     1     1
    # ... with 336,766 more rows
    

    1.4 变形:mutate()transmutate()

    mutate()函数可用来对添加列,与cbind()以及transform()函数相似,但是更优于transform,mutate()在创建一列时还可以将其作为变量再来创建后面的列。transmutate()则是仅保留刚刚创建的变量。

    > dplyr::mutate(flights,gain = arr_delay - dep_delay,gain_per_hour = gain / (air_time / 60))
    # A tibble: 336,776 x 21
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     1     1      517            515        2.      830            819       11.
     2  2013     1     1      533            529        4.      850            830       20.
     3  2013     1     1      542            540        2.      923            850       33.
     4  2013     1     1      544            545       -1.     1004           1022      -18.
     5  2013     1     1      554            600       -6.      812            837      -25.
     6  2013     1     1      554            558       -4.      740            728       12.
     7  2013     1     1      555            600       -5.      913            854       19.
     8  2013     1     1      557            600       -3.      709            723      -14.
     9  2013     1     1      557            600       -3.      838            846       -8.
    10  2013     1     1      558            600       -2.      753            745        8.
    # ... with 336,766 more rows, and 12 more variables: carrier <chr>, flight <int>,
    #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
    #   minute <dbl>, time_hour <dttm>, gain <dbl>, speed <dbl>
    ### 这里可以看到flights是增加了两列的,而且新列gain_per_hour是通过gain这个新建的列创建的
    
    > dplyr::transmute(flights,gain = arr_delay - dep_delay,gain_per_hour = gain / (air_time / 60))
    # A tibble: 336,776 x 2
        gain gain_per_hour
       <dbl>         <dbl>
     1    9.          2.38
     2   16.          4.23
     3   31.         11.6 
     4  -17.         -5.57
     5  -19.         -9.83
     6   16.          6.40
     7   24.          9.11
     8  -11.        -12.5 
     9   -5.         -2.14
    10   10.          4.35
    # ... with 336,766 more rows
    

    1.5 聚合汇总:summarize()

    对数据框调用函数进行操作返回结果,常用于分组后的处理。

    > dplyr::summarise(flights,delay = mean(dep_delay, na.rm = TRUE))
    # A tibble: 1 x 1
      delay
      <dbl>
    1  12.6
    

    1.6 抽样:sample_n()sample_frac()

    这两个函数是从数据集中随机抽取指定行,不同之处是sample_n()表示抽取的行数而sample_frac()则表示百分比的行数。格式:sample_n(tbl, size, replace = FALSE, weight = NULL, .env = NULL)sample_frac(tbl, size = 1, replace = FALSE, weight = NULL, .env = NULL),若replace==TRUE则表示bootstrap抽样,通过weight指定权重参数。

    > sample_n(flights, 10);sample_frac(flights,0.1)
    # A tibble: 10 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013     2    12      819            825       -6.      937            945       -8.
     2  2013     5     5     2158           2159       -1.     2258           2337      -39.
     3  2013     5     3     1535           1540       -5.     1745           1650       55.
     4  2013     5     2     1824           1830       -6.     2115           2200      -45.
     5  2013     5     1     1610           1610        0.     1719           1751      -32.
     6  2013     9     8     1954           1859       55.     2134           2127        7.
     7  2013     5    27      537            540       -3.      828            840      -12.
     8  2013     1    24      806            810       -4.     1022           1044      -22.
     9  2013     5    13     1551           1555       -4.     1659           1727      -28.
    10  2013     8    12       NA            920       NA        NA           1210       NA 
    # ... with 10 more variables: carrier <chr>, flight <int>, tailnum <chr>, origin <chr>,
    #   dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
    # A tibble: 33,678 x 19
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl>
     1  2013    11    16     1950           1930       20.     2055           2047        8.
     2  2013     2     8     1724           1655       29.     2043           2009       34.
     3  2013     9    12      652            700       -8.      931            949      -18.
     4  2013     4     8     1830           1831       -1.     2157           2203       -6.
     5  2013    10    17     1022           1025       -3.     1136           1140       -4.
     6  2013     5    14     1736           1745       -9.     1942           2021      -39.
     7  2013    11    28      745            736        9.      924            920        4.
     8  2013    12    17     1034           1035       -1.     1418           1405       13.
     9  2013    12     6     1953           2000       -7.     2114           2115       -1.
    10  2013    10    22     1057           1100       -3.     1416           1415        1.
    # ... with 33,668 more rows, and 10 more variables: carrier <chr>, flight <int>, tailnum <chr>,
    #   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
    #   time_hour <dttm>
    

    2、分组操作

    上面六个函数可以解决大部分的数据清理问题了,当他们与分组操作结合时会更加强大。
    在下面的例子当中,我们将使用tailnum作为分组因子对flights进行分组。

    > dim(flights)
    [1] 336776     19
    > length(levels(factor(flights$tailnum)))
    [1] 4043
    ### 可以看到flights共有336776行,其中tailnum列包含4043个不同的航班号
    ### 分组
    > df1<-group_by(flights, tailnum);df1
    # A tibble: 336,776 x 19
    # Groups:   tailnum [4,044]
        year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight tailnum origin dest 
       <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl> <chr>    <int> <chr>   <chr>  <chr>
     1  2013     1     1      517            515        2.      830            819       11. UA        1545 N14228  EWR    IAH  
     2  2013     1     1      533            529        4.      850            830       20. UA        1714 N24211  LGA    IAH  
     3  2013     1     1      542            540        2.      923            850       33. AA        1141 N619AA  JFK    MIA  
     4  2013     1     1      544            545       -1.     1004           1022      -18. B6         725 N804JB  JFK    BQN  
     5  2013     1     1      554            600       -6.      812            837      -25. DL         461 N668DN  LGA    ATL  
     6  2013     1     1      554            558       -4.      740            728       12. UA        1696 N39463  EWR    ORD  
     7  2013     1     1      555            600       -5.      913            854       19. B6         507 N516JB  EWR    FLL  
     8  2013     1     1      557            600       -3.      709            723      -14. EV        5708 N829AS  LGA    IAD  
     9  2013     1     1      557            600       -3.      838            846       -8. B6          79 N593JB  JFK    MCO  
    10  2013     1     1      558            600       -2.      753            745        8. AA         301 N3ALAA  LGA    ORD  
    # ... with 336,766 more rows, and 5 more variables: air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
    ### 聚合
    ### 这里对分好组的数据进行了3个操作,(1)计算每个组内数据的个数(也即行数,通过n()函数获得);(2)计算每个组内距离的平均数(mean(distance));(3)计算每个组内晚点到达的平均数(mean(arr_delay))
    > df2<-summarise(df1,count=n(),dist=mean(distance, na.rm=TRUE),delay=mean(arr_delay,na.rm=TRUE));df2
    # A tibble: 4,044 x 4
       tailnum count  dist   delay
       <chr>   <int> <dbl>   <dbl>
     1 D942DN      4  854.  31.5  
     2 N0EGMQ    371  676.   9.98 
     3 N10156    153  758.  12.7  
     4 N102UW     48  536.   2.94 
     5 N103US     46  535.  -6.93 
     6 N104UW     47  535.   1.80 
     7 N10575    289  520.  20.7  
     8 N105UW     45  525.  -0.267
     9 N107US     41  529.  -5.73 
    10 N108UW     60  534.  -1.25 
    # ... with 4,034 more rows
    ### 最后对数据进行筛选
    > df3<-filter(df2, count>=20 & dist<2000);df3
    # A tibble: 2,986 x 4
       tailnum count  dist   delay
       <chr>   <int> <dbl>   <dbl>
     1 N0EGMQ    371  676.   9.98 
     2 N10156    153  758.  12.7  
     3 N102UW     48  536.   2.94 
     4 N103US     46  535.  -6.93 
     5 N104UW     47  535.   1.80 
     6 N10575    289  520.  20.7  
     7 N105UW     45  525.  -0.267
     8 N107US     41  529.  -5.73 
     9 N108UW     60  534.  -1.25 
    10 N109UW     48  536.  -2.52 
    # ... with 2,976 more rows
    

    接下来我们做个图看看飞机平均延时跟飞行距离的关系:

    > ggplot(data=df3) +
    +     geom_point(aes(x=dist, y=delay, size=count)) +
    +     geom_smooth(aes(x=dist,y=delay))
    

    从图中可以看到飞机延时跟飞行距离相关性不大。
    dplyr中一些聚合时的函数:

    • n() 计算个数
    • n_distinct() 计算 每个组中唯一值的个数
    • first(x), last(x)nth(x, n) 返回对应秩的值, 类似于自带函数 x[1], x[length(x)], 和 x[n]

    3、连接符:%>%

    连接符是dplyr包中的一个非常实用的功能,他使得我们能够将所有操作步骤写在一起而且易于理解,不用储存中间结果。下面我们使用连接符重现上一节中的数据处理操作:

    library(hflights)
    library(dplyr)
    
    df<-flights %>%
      group_by(tailnum) %>%
      summarise(count=n(),
                dist=mean(distance, na.rm=TRUE),
                delay=mean(arr_delay,na.rm=TRUE)) %>%
      filter(count>=20 & dist<2000)
    
    df
    #输出结果
    > df
    # A tibble: 2,986 x 4
       tailnum count  dist   delay
       <chr>   <int> <dbl>   <dbl>
     1 N0EGMQ    371  676.   9.98 
     2 N10156    153  758.  12.7  
     3 N102UW     48  536.   2.94 
     4 N103US     46  535.  -6.93 
     5 N104UW     47  535.   1.80 
     6 N10575    289  520.  20.7  
     7 N105UW     45  525.  -0.267
     8 N107US     41  529.  -5.73 
     9 N108UW     60  534.  -1.25 
    10 N109UW     48  536.  -2.52 
    # ... with 2,976 more rows
    

    4、总结

    可以看到dplyr的操作非常方便简洁,而且解决了reshape2中分组聚合函数不能返回一个多维数据的缺点。他的管道操作思想与shell中的管道非常相似,同时在写法上又与R内置的with(和within)函数颇为相似,都省去了每一步写数据变量名,而且易读性好。相比于reshape2更好上手。
    此外,dplyr包还有针对多个数据集之间的操作,如连接取交集等。

    参考:
    dplyr官方文档
    dplyr官方入门简介
    R语言dplyr包

    相关文章

      网友评论

        本文标题:高效数据整理工具——dplyr(一)

        本文链接:https://www.haomeiwen.com/subject/eeyyhftx.html