使用 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)是最常用的数据结构,用于存储二维表(即关系表)的数据,每一列存储的数据类型必须相同,不同数据列的数据类型可以相同,也可以不同,但是每列的行数(长度)必须相同.数据框的每列 ...
随机推荐
- VS2013密钥(所有版本)
Visual Studio Ultimate 2013 KEY(密钥):BWG7X-J98B3-W34RT-33B3R-JVYW9 Visual Studio Premium 2013 KEY(密钥) ...
- [lr & ps] 色彩空间管理
色彩空间 • 定义 色彩空间,Color Space,又称作色域.在色彩学中,人们建立了许多色彩模型,以一维.二维.三维甚至四维空间坐标来表示某一色彩,这种坐标系统所能定义的色彩范围即色彩空间.我们经 ...
- c++移动构造函数
写在前面 C++中有“左值”.“右值”的概念,C++11以后,又有了“左值”.“纯右值”.“将亡值”的概念.关于这些概念,许多资料上都有介绍,本文在拾人牙慧的基础上又加入了一些自己的一些理解,同时提出 ...
- [转载]C#深拷贝的方法
首先了解下深拷贝和浅拷贝的定义: 浅拷贝(影子克隆):只复制对象的基本类型,对象类型,仍属于原来的引用. 深拷贝(深度克隆):不紧复制对象的基本类,同时也复制原对象中的对象.就是说完全是新对 ...
- cocoapods 配置
二.CocoaPods 安装 CocoaPods可以方便地通过Mac自带的RubyGems安装. 打开Terminal(Mac电脑自带的终端): (1).设置ruby的软件源 这是因为ruby的软件源 ...
- Mocha describe 生命周期
1 describe('test', function() { 2 // 在本测试块的所有测试用例之前执行且仅执行一次 3 before(function() { 4 5 }); 6 // 在本测试块 ...
- bootstrap实战教程
bootstrap实战教程 bootstrap介绍 简介 Bootstrap 是最受欢迎的 HTML.CSS 和 JS 框架,用于开发响应式布局.移动设备优先的 WEB 项目.全球数以百万计的网站都是 ...
- linux服务器管理员的12个有用的命令
ifconfig: 在修改内核中已有的网络接口时,你会用到ifconfig命令.这个命令通常用于系统调校和调试,但同时也可以用于在启动过程中设置接口. netstat: 对于Linux用户来说这是一个 ...
- Centos6版本使用yum报错 Loaded plugins: fastestmirror, refresh-packagekit, security Loading mirror speeds from cached hostfi Setting up Install Process No package gcc available. Error: Nothing to do
在使用Centos6版本yum时报错 Loaded plugins: fastestmirror, refresh-packagekit, securityLoading mirror speeds ...
- P3008 [USACO11JAN]道路和飞机Roads and Planes
P3008 [USACO11JAN]道路和飞机Roads and Planes Dijkstra+Tarjan 因为题目有特殊限制所以不用担心负权的问题 但是朴素的Dijkstra就算用堆优化,也显然 ...