关于数据操作的另一个流行的包是dplyr,它发明了一种数据操作语法。dplyr 扩展包并
没有使用构建子集函数([ ]),而是定义了一系列基础的变形函数作为数据操作模块,并且引
入了一个管道操作符,利用管道操作符将这些变形函数串联起来,进而完成复杂的多步任务。
如果还没有安装 dplyr,请运行以下代码以从 CRAN 中安装 :
install.packages("dplyr")
首先,我们重新加载产品表格,将它们重置为原始形式:
library(readr)
product_info <- read_ _csv("data/product-info.csv")
product_stats <- read_ _csv("data/product-stats.csv")
product_tests <- read_ _csv("data/product-tests.csv")
toy_tests <- read_ _csv("data/product-toy-tests.csv")
然后,载入 dplyr 包:
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:data.table':
##
## between, last
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
以上输出信息说明 dplyr 泛化了很多内置函数。加载这个包之后,这些内置函数便被屏蔽了。
现在,我们可以使用 dplyr 包提供的变形函数了。先使用 select( ) 函数从数据框
中提取列,并将这些列存储在新创建的表中:
select(product_info, id, name, type, class)
## Source:local data frame[6x4]
##
## id name type class
## <chr> <chr> <chr> <chr>
## 1 T01 SupCar toy vehicle
## 2 T02 SupPlane toy vehicle
## 3 M01 JeepX model vehicle
## 4 M02 AircraftX model vehicle
## 5 M03 Runner model people
## 6 M04 Dancer model people
打印出来的表格与 data.frame 和 data.table 都不太一样。它不仅显示了表格本
身,也包括一个表头,用于说明数据框的大小和每一列的数据类型。
显然,select( ) 使用了非标准计算,所以我们可以直接将数据框的列名作为参数。
它和 subset( )、transform( ) 以及 with( ) 的工作方式类似。
其次,我们可以使用 filter( ) 函数,通过逻辑条件筛选数据框。同样地,这个函
数也是在数据框的语义中被计算:
filter(product_info, released == "yes")
## Source:local data frame[4x5]
##
## id name type class released
## <chr> <chr> <chr> <chr> <chr>
## 1 T01 SupCar toy vehicle yes
## 2 M01 JeepX model vehicle yes
## 3 M02 AircraftX model vehicle yes
## 4 M03 Runner model people yes
如果想要根据多个条件筛选记录,只需要把每个条件都作为 filter() 的参数:
filter(product_info,
released == "yes", type == "model")
## Source:local data frame[3x5]
##
## id name type class released
## <chr> <chr> <chr> <chr> <chr>
## 1 M01 JeepX model vehicle yes
## 2 M02 AircraftX model vehicle yes
## 3 M03 Runner model people yes
mutate( )函数可以创建一个新的数据框,这个数据框包含新列,或者替换原数据框的列。
它与 transform( )类似,不同的是,如果数据是 data.table,它也能支持原地赋值 := :
mutate(product_stats, density = size / weight)
## Source: local data frame [6 x 5]
##
## id material size weight density
## <chr> <chr> <int> <dbl> <dbl>
## 1 T01 Metal 120 10.0 12.000000
## 2 T02 Metal 350 45.0 7.777778
## 3 M01 Plastics 50 NA NA
## 4 M02 Plastics 85 3.0 28.333333
## 5 M03 Wood 15 NA NA
## 6 M04 Wood 16 0.6 26.666667
arrange( ) 函数也是用于创建一个新的数据框,这个数据框是按一个或多个列排序
后的。desc( ) 函数表示降序排列:
arrange(product_stats, material, desc(size), desc(weight))
## Source: local data frame [6 x 4]
##
## id material size weight
## <chr> <chr> <int> <dbl>
## 1 T02 Metal 350 45.0
## 2 T01 Metal 120 10.0
## 3 M02 Plastics 85 3.0
## 4 M01 Plastics 50 NA
## 5 M04 Wood 16 0.6
## 6 M03 Wood 15 NA
dplyr 包提供了丰富的连接函数,包括 inner_join( )、left_join( )、right_
join( )、full_join( )、semi_join( ) 和 anti_join( )。如果要连接的两个表
存在无法匹配的记录,这些连接操作的行为会有很大差别。对于 product_info
和 product_tests,它们的记录可以完全匹配,所以 left_join( ) 的返回结果
和 merge( ) 相同:
product_info_tests <- left_ _join(product_info, product_tests, by = "id")
product_info_tests
## Source: local data frame [6 x 8]
##
## id name type class released quality durability
## <chr> <chr> <chr> <chr> <chr> <int> <int>
## 1 T01 SupCar toy vehicle yes NA 10
## 2 T02 SupPlane toy vehicle no 10 9
## 3 M01 JeepX model vehicle yes 6 4
## 4 M02 AircraftX model vehicle yes 6 5
## 5 M03 Runner model people yes 5 NA
## 6 M04 Dancer model people no 6 6
## Variables not shown: waterproof (chr)
运行 ?dplyr::join 了解这些连接操作的更多差异。
为了对数据进行分组汇总,我们需要先利用 group_by( ) 创建一个分组后的表格。
然后使用 summarize( ) 汇总数据。例如,我们想把 product_info_tests 按
照 type 和 class 分割开,然后对每一组计算 quality 和 durability 的平均值:
summarize(group_ _by(product_info_tests, type, class),
mean_quality = mean(quality, na.rm = TRUE),
mean_durability = mean(durability, na.rm = TRUE))
## Source: local data frame [3 x 4]
## Groups: type [?]
##
## type class mean_quality mean_durability
## <chr> <chr> <dbl> <dbl>
## 1 model people 5.5 6.0
## 2 model vehicle 6.0 4.5
## 3 toy vehicle 10.0 9.5
通过前面的代码示例,我们掌握了这些变形函数: select( )、filter( )、mutate( )、
arrange( )、group_by( ) 和 summarize( )。这些函数的设计初衷都是对数据进行一个
小操作,但是将它们合理地组合到一起,就可以完成复杂的数据处理操作。除了这些函数,
dplyr 包还从 magrittr 包中引入了管道操作符 %>% ,利用 %>% 将函数连接起来,组合使用。
假设现在有 product_info 和 product_tests。我们需要对已发布的产品进行分析,
对于每种类型和类对应的组,计算该组产品的质量和耐久性的平均值,并将结果数据按照质量
均值降序排列。通过使用管道操作符将 dplyr 变形函数连接起来,可以漂亮地完成这个任务:
product_info %>%
filter(released == "yes") %>%
inner_ _join(product_tests, by = "id") %>%
group_ _by(type, class) %>%
summarize(
mean_quality = mean(quality, na.rm = TRUE),
mean_durability = mean(durability, na.rm = TRUE)) %>%
arrange(desc(mean_quality))
## Source: local data frame [3 x 4]
## Groups: type [2]
##
## type class mean_quality mean_durability
## <chr> <chr> <dbl> <dbl>
## 1 model vehicle 6 4.5
## 2 model people 5 NaN
## 3 toy vehicle NaN 10.0
但是 %>% 是如何工作的呢?其实,管道操作符基本上只负责一件事情:把符号左侧返回的
结果,作为符号右侧调用函数的第 1 个参数。也就是说,x %>% f(...) 等价于 f(x, ...)。
因为 %>% 是一个由包定义的二元操作符,所以允许我们将函数调用连接起来,一方面避免存储
多余的中间值,另一方面将嵌套调用分解,使每一步操作流程清晰地展现出来。
假设将 d0 转化为 d3 需要3 个步骤。在每一步的函数调用中,需要将前面一步的结果作为
参数。如果像这样操作数据,可能会有很多中间结果,当数据量很大的时候,会消耗很多内存:
d1 <- f1(d0, arg1)
d2 <- f2(d1, arg2)
d3 <- f3(d2, arg3)
想要避免中间结果,就不得不写嵌套调用。这个任务看起来一点都不友好,特别是在
每个函数调用都有多个参数的时候:
f3(f2(f1(d0, arg1), arg2), arg3)
使用管道操作符,工作流便可以像下面这样重新组织:
d0 %>%
f1(arg1) %>%
f2(arg2) %>%
f3(arg3)
这样的代码看起来更加简洁和直观。整个表达式不止看起来像一个管道,其工作方式
也像一个管道。d0 %>% f1(arg1) 等价于 f1(d0, arg1),并会被送往 f2(., arg2),
紧接着又会被送往 f3(., arg3)。每一步的输出结果都会成为下一步的输入。
而且,管道操作符不止在 dplyr 的函数中生效,对其他所有的函数也都是适用的。假
设我们想要对钻石价格画一个密度图,如图 12-5 所示。
data(diamonds, package = "ggplot2")
plot(density(diamonds$price, from = 0),
main = "Density plot of diamond prices")

图 12-5
使用管道操作符,我们可以像这样重写代码:
diamonds$price %>%
density(from = 0) %>%
plot(main = "Density plot of diamonds prices")
与 data.table 类似,dplyr 也提供了 do( ) 函数来对每组数据进行任意操作。例
如,将 diamonds 按 cut 分组,每组都按 log(price) ~ carat 拟合一个线性模型。
和 data.table 不同的是,我们需要为操作指定一个名称,以便将结果储存到列中。而且,
do( ) 中的表达式不能直接在分组数据的语义下计算,我们需要使用 . 来表示数据:
models <- diamonds %>%
group_ _by(cut) %>%
do(lmod = lm(log(price) ~ carat, data = .))
models
## Source: local data frame [5 x 2]
## Groups: <by row>
##
## cut lmod
## <fctr> <chr>
## 1 Fair <S3: lm>
## 2 Good <S3: lm>
## 3 Very Good <S3: lm>
## 4 Premium <S3: lm>
## 5 Ideal <S3: lm>
注意到一个新列 lmod 被创建了。这不是一个典型的原子向量列,而是一个包含了线
性回归对象的列表,也就是说,每一个 cut 的值对应的模型会以列表的形式储存在 lmod
列的对应位置中。我们可以使用索引来获得每个模型:
models$lmod[[1]]
##
## Call:
## lm(formula = log(price) ~ carat, data = .)
##
## Coefficients:
## (Intercept) carat
## 6.785 1.251
在需要完成高度定制的操作时,do( ) 函数的优势就更加明显了。举个例子,假如我们需
要分析 toy_tests 数据,要对每种产品的质量和耐久性进行汇总。如果只需要样本数最多的
3 个测试记录,并且每个产品的质量和耐久性是经样本数加权的平均数,考虑下我们应该做什么。
使用 dplyr 包的函数和管道操作符,上述任务可以通过以下代码轻松完成:
toy_tests %>%
group_ _by(id) %>%
arrange(desc(sample)) %>%
do(head(., 3)) %>%
summarize(
quality = sum(quality * sample) / sum(sample),
durability = sum(durability * sample) / sum(sample))
## Source:local data frame[2x3]
##
## id quality durability
## <chr> <dbl> <dbl>
## 1 T01 9.319149 9.382979
## 2 T02 9.040000 8.340000
注意到,当数据分组后,所有的后续操作都是按组进行的。为了查看中间结果,我们
可以运行 do(head(., 3)) 之前的代码,如下所示:
toy_tests %>%
group_ _by(id) %>%
arrange(desc(sample))
## Source: local data frame [8 x 5]
## Groups: id [2]
##
## id date sample quality durability
## <chr> <int> <int> <int> <int>
## 1 T01 20160405 180 9 10
## 2 T01 20160302 150 10 9
## 3 T01 20160502 140 9 9
## 4 T01 20160201 100 9 9
## 5 T02 20160403 90 9 8
## 6 T02 20160502 85 10 9
## 7 T02 20160303 75 8 8
## 8 T02 20160201 70 7 9
这样我们就得到了按样本数降序排列的所有记录。然后,do(head(., 3)) 将会对
每一个组计算 head(. 3),其中, . 表示每组数据:
toy_tests %>%
group_ _by(id) %>%
arrange(desc(sample)) %>%
do(head(., 3))
## Source: local data frame [6 x 5]
## Groups: id [2]
##
## id date sample quality durability
## <chr> <int> <int> <int> <int>
## 1 T01 20160405 180 9 10
## 2 T01 20160302 150 10 9
## 3 T01 20160502 140 9 9
## 4 T02 20160403 90 9 8
## 5 T02 20160502 85 10 9
## 6 T02 20160303 75 8 8
现在,我们得到了每一组的样本数最多的 3 条记录,如此汇总数据是很方便的。
dplyr 函数定义了一种非常直观的数据操作语法,并且提供了便于使用管道操作符的
高性能变形函数。更多内容,请阅读包的指南(https://cran.rstudio.com/web/packages/dplyr/
vignettes/introduction.html),并且访问 DataCamp 上的交互式教程(https://www.datacamp.
com/courses/dplyr-data-manipulation-r-tutorial)。

使用 dplyr 管道操作处理数据框的更多相关文章

  1. R语言Data Frame数据框常用操作

    Data Frame一般被翻译为数据框,感觉就像是R中的表,由行和列组成,与Matrix不同的是,每个列可以是不同的数据类型,而Matrix是必须相同的. Data Frame每一列有列名,每一行也可 ...

  2. 转载:R语言Data Frame数据框常用操作

    Data Frame一般被翻译为数据框,感觉就像是R中的表,由行和列组成,与Matrix不同的是,每个列可以是不同的数据类型,而Matrix是必须相同的. Data Frame每一列有列名,每一行也可 ...

  3. (数据科学学习手札07)R在数据框操作上方法的总结(初级篇)

    上篇我们了解了Python中pandas内封装的关于数据框的常用操作方法,而作为专为数据科学而生的一门语言,R在数据框的操作上则更为丰富精彩,本篇就R处理数据框的常用方法进行总结: 1.数据框的生成 ...

  4. (数据科学学习手札06)Python在数据框操作上的总结(初级篇)

    数据框(Dataframe)作为一种十分标准的数据结构,是数据分析中最常用的数据结构,在Python和R中各有对数据框的不同定义和操作. Python 本文涉及Python数据框,为了更好的视觉效果, ...

  5. R 数据框的操作

    1.插入一列 根据自带数据集beaver 进行操作,比如插入一列id. > colnames(beaver1) [1] "day" "time" &quo ...

  6. R语言数据分析利器data.table包 —— 数据框结构处理精讲

        R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代码简洁,只要一行命令就可以完成诸多任务,另一方面是处理 ...

  7. R语言数据分析利器data.table包—数据框结构处理精讲

    R语言数据分析利器data.table包-数据框结构处理精讲 R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代 ...

  8. R学习笔记 第三篇:数据框

    数据框(data.frame)用于存储二维表(即关系表)的数据,每一列存储的数据类型必须相同,不同的数据列的数据类型可以相同,也可以不同,但是,每列的长度必须相同.数据框的每列可以有唯一的命名,在已创 ...

  9. R语言学习 第三篇:数据框

    数据框(data.frame)是最常用的数据结构,用于存储二维表(即关系表)的数据,每一列存储的数据类型必须相同,不同数据列的数据类型可以相同,也可以不同,但是每列的行数(长度)必须相同.数据框的每列 ...

随机推荐

  1. [LeetCode] 821. Shortest Distance to a Character_Easy tag: BFS

    Given a string S and a character C, return an array of integers representing the shortest distance f ...

  2. oracle怎么恢复被覆盖的存储过程

    在oracle数据库中,如果覆盖了之前的存储过程,那得赶紧闪回,时长越长闪回的可能性越小. 原理很简单,存储过程的定义就是数据字典,修改数据字典跟修改普通表的数据没有区别,此时会把修改前的内容放到un ...

  3. docker 批量删除容器和镜像

    docker 批量删除容器和镜像 1,删除单个镜像或者容器 docker  rmi  镜像ID/镜像名字:TAG docker  rm  容器ID/容器名字 1.停止所有的container,这样才能 ...

  4. IBatis 配置各种数据库

    IBatis 与各种数据库之间的配置在providers.config这个文件下. <?xml version="1.0" encoding="utf-8" ...

  5. Vim/Vi的使用

     Vim 是vi的加强 Gvim图形化的vim Vim/Vi简介 Vim/Vi是一个功能强大的全屏幕文本编辑器,是Linux/Unix上最常用的文本编辑器,他们 的作用是建立,编辑,显示文本文件 Vi ...

  6. html5新元素练习(1)

    1, <nav>  是专门由于菜单导航,链接导航的元素 2,  <header> 主体页面的头部,与head不同,它的内容可以显示出来 3,  <footer>页面 ...

  7. bug管理工具为开发者工作带来哪些改变?

    BUG管理工具的主要功能是对软件开发测试过程中出现的BUG进行跟踪管理,提高开发者的工作效率与工作质量. 在实际工作中,如果没有bug管理工具的帮助,就可能会出现如下一系列的影响: 1.软件测试人员将 ...

  8. 神经网络 java包

    java神经网络组件Joone.Encog和Neuroph https://github.com/deeplearning4j/deeplearning4j http://muchong.com/ht ...

  9. linux常用命令:vmstat 命令

    vmstat 是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动进行监控.他是对系统的整体 情况进行统计,不足之处是无法对某个进程进 ...

  10. jsonp 方式处理跨域前后端代码如何配合?

    JSONP(JSON with Padding)(json   数据填充)只支持GET请求 是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题. 跨域产生原因是浏览器的同源策略.( ...