使用 dplyr 管道操作处理数据框
关于数据操作的另一个流行的包是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 管道操作处理数据框的更多相关文章
- R语言Data Frame数据框常用操作
Data Frame一般被翻译为数据框,感觉就像是R中的表,由行和列组成,与Matrix不同的是,每个列可以是不同的数据类型,而Matrix是必须相同的. Data Frame每一列有列名,每一行也可 ...
- 转载:R语言Data Frame数据框常用操作
Data Frame一般被翻译为数据框,感觉就像是R中的表,由行和列组成,与Matrix不同的是,每个列可以是不同的数据类型,而Matrix是必须相同的. Data Frame每一列有列名,每一行也可 ...
- (数据科学学习手札07)R在数据框操作上方法的总结(初级篇)
上篇我们了解了Python中pandas内封装的关于数据框的常用操作方法,而作为专为数据科学而生的一门语言,R在数据框的操作上则更为丰富精彩,本篇就R处理数据框的常用方法进行总结: 1.数据框的生成 ...
- (数据科学学习手札06)Python在数据框操作上的总结(初级篇)
数据框(Dataframe)作为一种十分标准的数据结构,是数据分析中最常用的数据结构,在Python和R中各有对数据框的不同定义和操作. Python 本文涉及Python数据框,为了更好的视觉效果, ...
- R 数据框的操作
1.插入一列 根据自带数据集beaver 进行操作,比如插入一列id. > colnames(beaver1) [1] "day" "time" &quo ...
- R语言数据分析利器data.table包 —— 数据框结构处理精讲
R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代码简洁,只要一行命令就可以完成诸多任务,另一方面是处理 ...
- R语言数据分析利器data.table包—数据框结构处理精讲
R语言数据分析利器data.table包-数据框结构处理精讲 R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代 ...
- R学习笔记 第三篇:数据框
数据框(data.frame)用于存储二维表(即关系表)的数据,每一列存储的数据类型必须相同,不同的数据列的数据类型可以相同,也可以不同,但是,每列的长度必须相同.数据框的每列可以有唯一的命名,在已创 ...
- R语言学习 第三篇:数据框
数据框(data.frame)是最常用的数据结构,用于存储二维表(即关系表)的数据,每一列存储的数据类型必须相同,不同数据列的数据类型可以相同,也可以不同,但是每列的行数(长度)必须相同.数据框的每列 ...
随机推荐
- Floyd 判圈算法
Floyd 判圈算法 摘自维基百科, LeetCode 上 141题 Linked List Cycle 用到这个, 觉得很有意思. 记录一下. 链接: https://zh.wikipedia.or ...
- .net MVC 下拉多级联动及编辑
多级联动实现,附源码.当前,部分代码是参与博客园其它网友. 新增,前台代码: <script src="~/Scripts/jquery-1.10.2.js">< ...
- 3:4 OGNL 表达式二
总结: 一:ActionContext的结构: 1:set标签创建的对象也是作为非值栈对象. 2:(非值栈的存储都是以键值对的方式存的.) [问非值栈的 User对象] [用例] 关于request: ...
- 教你玩转产品管理系统iClap(PC端功能篇)
之前和大家介绍了iClap的基础功能, 这一次针对PC端右侧的工具栏再做一个详细的介绍 随着版本的更新迭代,陆续会有更多工具和功能推出! 导航 为项目成员提供网址浏览访问导航服务,帮助项目成员快速查找 ...
- MFC六大核心机制之四:永久保存(串行化)
永久保存(串行化)是MFC的重要内容,可以用一句简明直白的话来形容其重要性:弄懂它以后,你就越来越像个程序员了! 如果我们的程序不需要永久保存,那几乎可以肯定是一个小玩儿.那怕我们的记事本.画图等小程 ...
- Object-C-NSArray
NSArray *fruitArray=[[NSArray alloc] initWithObjects:@"apple",@"banana",@"p ...
- selenium3启动浏览器
selenium3比2更加标准化,支持的浏览器更多,启动浏览器需要做如下操作: 首先,下载对应浏览器的驱动,并放在项目下(也可以放在其他地方,写代码的时候路径填写正确即可),如下图所示: 下面就是启动 ...
- linux常用命令:ss 命令
ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信 ...
- web前端----css选择器样式
一.css概述 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,对html标签的渲染和布局 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 例如 二.c ...
- 20145106 《Java程序设计》第10周学习总结
教材学习内容总结 什么是计算机网络? 计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享 ...