您的位置:首页 > 其它

R语言扩展包dplyr笔记

2017-05-11 15:17 260 查看


引言

2014年刚到, 就在 Feedly 订阅里看到 RStudio
Blog 介绍 
dplyr
 包已发布
(Introducing dplyr), 此包将原本 
plyr
 包中的 
ddply()
 等函数进一步分离强化, 专注接受dataframe对象, 大幅提高了速度,
并且提供了更稳健的与其它数据库对象间的接口. 既然是 Hadley Wickham 的新作, 并自称 a
grammar of data manipulation, 当然要先学为快了, 正好新申了域名, 就把原本记在 Rmd 里的笔记组织一下,
放在这里, 算是个简短的教程吧, 仅供入门.


正文: 学习笔记

以下内容主要参照 Introducing dplyr 和 
dplyr
 包自带的简介
(Introduction to dplyr),
复制了原文对应代码, 并夹杂了个人理解和观点 (多附于括号内).


0 初始化


0.1 安装

install.packages("dplyr")


0.2 示范数据

library(Lahman)
Lahman 包里的棒球比赛数据集 
Batting


library(hflights)
hflights 包里的飞机航班数据


0.3 数据集类型

将过长过大的数据集转换为显示更友好的 
tbl_df
 类型:

hflights_df
<- tbl_df(hflights)


可以 
hflights_df
 感受一下不再被刷屏的感觉.


1 基本操作

把常用的数据操作行为归纳为以下五种:


1.1 筛选: 
filter()

按给定的逻辑判断筛选出符合要求的子数据集, 类似于 
base::subset()
 函数

例如:

filter(hflights_df,
Month == 1, DayofMonth == 1)


用R自带函数实现:

hflights[hflights$Month
== 1 & hflights$DayofMonth == 1, ]


除了代码简洁外, 还支持对同一对象的任意个条件组合, 如:

filter(hflights_df,
Month == 1 | Month == 2)


注意: 表示 
AND
 时要使用 
&
 而避免 
&&


1.2 排列: 
arrange()

按给定的列名依次对行进行排序.

例如:

arrange(hflights_df,
DayofMonth, Month, Year)


对列名加 
desc()
 进行倒序:

arrange(hflights_df,
desc(ArrDelay))


这个函数和 
plyr::arrange()
 是一样的,
类似于 
order()


用R自带函数实现:

hflights[order(hflights$DayofMonth,
hflights$Month, hflights$Year), ]

hflights[order(desc(hflights$ArrDelay)),
]


1.3 选择: 
select()

用列名作参数来选择子数据集:

select(hflights_df,
Year, Month, DayOfWeek)


还可以用 
:
 来连接列名,
没错, 就是把列名当作数字一样使用:

select(hflights_df,
Year:DayOfWeek)


用 
-
 来排除列名:

select(hflights_df,
-(Year:DayOfWeek))


同样类似于R自带的 
subset()
 函数
(但不用再写一长串的 
c("colname1",
"colname2")
 或者 
which(colname(data)
== "colname3")
, 甚至还要去查找列号)


1.4 变形: 
mutate()

对已有列进行数据运算并添加为新列:
mutate(hflights_df,
gain = ArrDelay - DepDelay,
speed = Distance / AirTime * 60)


作用与 
plyr::mutate()
 相同,
与 
base::transform()
 相似,
优势在于可以在同一语句中对刚增加的列进行操作:
mutate(hflights_df,
gain = ArrDelay - DepDelay,
gain_per_hour = gain / (AirTime / 60)
)


而同样操作用R自带函数 
transform()
 的话就会报错:
transform(hflights,
gain = ArrDelay - DepDelay,
gain_per_hour = gain / (AirTime / 60)
)


1.5 汇总: 
summarise()

对数据框调用其它函数进行汇总操作, 返回一维的结果:
summarise(hflights_df,
delay = mean(DepDelay, na.rm = TRUE))


等同于 
plyr::summarise()
,
原文说该函数功能尚不是非常有用, 大概以后的更新会加强吧.


2 分组动作 
group_by()

以上5个动词函数已经很方便了, 但是当它们跟分组操作这个概念结合起来时, 那才叫真正的强大! 当对数据集通过 
group_by()
 添加了分组信息后,
mutate()
arrange()
 和 
summarise()
 函数会自动对这些 
tbl
 类数据执行分组操作
(R语言泛型函数的优势).

例如: 对飞机航班数据按飞机编号 (TailNum) 进行分组, 计算该飞机航班的次数 (
count
= n()
), 平均飞行距离 (
dist
= mean(Distance, na.rm = TRUE)
) 和 延时 (
delay
= mean(ArrDelay, na.rm = TRUE)
)
planes <- group_by(hflights_df, TailNum)
delay <- summarise(planes,
count = n(),
dist = mean(Distance, na.rm = TRUE),
delay = mean(ArrDelay, na.rm = TRUE))
delay <- filter(delay, count > 20, dist < 2000)


用 
ggplot2
 包作个图观察一下,
发现飞机延时不延时跟飞行距离没太大相关性:
ggplot(delay, aes(dist, delay)) +
geom_point(aes(size = count), alpha = 1/2) +
geom_smooth() +
scale_size_area()


(图就不上了, 右键复制来的链接太凶残了, 看着像是现算的)

更多例子见 
vignette("introduction",
package = "dplyr")


另: 一些汇总时的小函数

n()
:
计算个数
n_distinct()
:
计算 
x
 中唯一值的个数.
(原文为 
count_distinct(x)
,
测试无用)
first(x)
last(x)
 和 
nth(x,
n)
: 返回对应秩的值, 类似于自带函数 
x[1]
x[length(x)]
,
和 
x


注意: 分组计算得到的统计量要清楚样本已经发生了变化, 此时的中位数是不可靠的


3 连接符 
%.%

包里还新引进了一个操作符, 使用时把数据名作为开头, 然后依次对此数据进行多步操作.

比如:
Batting %.%
group_by(playerID) %.%
summarise(total = sum(G)) %.%
arrange(desc(total)) %.%
head(5)


这样可以按进行数据处理时的思路写代码, 一步步深入, 既易写又易读, 接近于从左到右的自然语言顺序, 对比一下用R自带函数实现的:
head(arrange(summarise(group_by(Batting, playerID), total = sum(G)) , desc(total)), 5)


或者像这篇文章所用的方法:
totals <- aggregate(. ~ playerID, data=Batting[,c("playerID","R")], sum)
ranks <- sort.list(-totals$R)
totals[ranks[1:5],]


文章里还表示: 用他的 MacBook Air 跑 
%.%
 那段代码用了
0.036 秒, 跑上面这段代码则用了 0.266 秒, 运算速度提升了近7倍. (当然这只是一例, 还有其它更大的数字.)

更多请 
?"%.%"
,
至于这个新鲜的概念会不会和 ggplot2 里的 
+
 连接号一样,
发挥出种种奇妙的功能呢? 还是在实际使用中多体验感受吧.


感想

可以看到, 用 
dplyr
 所含函数实现的代码都要简洁易读得多,
说到底, R语言只是一个工具, 作为工具, 就是要拿来用的, 越称手越便利越简洁越好, 可是, 正如 Hadley Wickham 在2013年的访谈中提到的那样:

如果你用了8小时进行数据清理和数据整理,而只用了2小时进行建模,那么很明显,你希望了解如何将数据清理和整理的时间尽可能缩短。

反思之下, 本人也是将大把的时间花在了对数据的反复调整上, 或许是手生, 当然R语言在这方面也确实有一定不足, 大神又说了:

数据分析有两个瓶颈,一是我们的目标是什么,二是我们如何用计算机去实现。我现有的很多作品,如 ggplot2,plyr 和 reshape2,更关注的是如何更简单地表达你的目标,而不是如何让计算机算得更快。

这种内在的理念正是要将工具工具化, 把无谓的时间减少, 让精力用在真正需要考虑的地方. 正如 Vim 一样, 在投入一定的学习成本后, 继续用继续学,
不知不觉地就能心手如一, 想做什么, 就已经按下去了, 从而更多地思考要编辑什么, 而不必纠结于光标移动选择等细节. 这其中的巧妙之处在于: 实现过程要以人脑的思维运作方式为标准, 让工具来适应人, 以实现目的为导向, ggplot2 的图形图层语法也是如此.
不管是软件也好, 编程语言也好, 高效的方法都是相通的, 这也正是许多人努力的方向, 另外平素语出惊人的王垠最近也表达了类似观点.

顺便肖凯老师在网易云课堂新开的R语言初级教程里提到了十大必学R包的说法,
并把 
plyr
 列为之一,
有趣的是居然还有人在问答平台上求详情, 好奇之下放狗一搜,
原来出处在此 (脱水版),
其中 
ggplot2
 和 
reshape2
 是平时都有在用的,
还有实用的 
knitr
 和 
Slidify
 ,
其它就没什么发言权了.


深入学习

暂时没有太多的相关资料, 如欲进一步学习, 可参阅:

dplyr
 包自带的60页详细文档

其余几个vignettes (网页)
或 
vignette(package
= "dplyr")
 , 包含了数据库相关, 混合编程, 运算性能比较, 以及新的 window-functions 等内容.

简单看了下
vignette("window-functions",
package = "dplyr")
, 提供了一系列函数, 扩展了原来只能返回一个数值的聚焦类函数(如
sum()
mean()
)至返回等长度的值,
变成 
cumsum()
和 
cummean()
,
以及 
n()
lead()
 和 
lag()
等便捷功能.

plyr
 包的相关文档: 主页

还有 
data.table
 包也是很强大的哦,
空下来可以学一学
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: