全部版块 我的主页
论坛 数据科学与人工智能 数据分析与数据科学 R语言论坛
2504 7
2015-03-22
下面的例子是来自一本书上的,R语言实战。 我的问题是,如果不是一个班级,而是100个班级。每个班级都要这么排序,那么应该如何处理这个问题。

一组学生参加了数学、科学和英语考试。为了给所有学生确定一个单一的成绩衡量指标,需要将这些科目的成绩组合起来。另外,你还想将前20%的学生评定为A,接下来20%的学生评定为B,依次类推。最后,你希望按字母顺序对学生排序。数据如表5-1所示。

学生姓名                        数 学       科 学         英 语
John Davis                    502          95            25
Angela Williams            600          99            22
Bullwinkle Moose          412          80           18
David Jones                 358          82           15
Janice Markhammer    495          75           20
Cheryl Cushing           512          85           28
Reuven Ytzrhak          410          80           15
Greg Knox                  625          95           30
Joel England             573           89           27
Mary Rayburn          522            86           18

观察此数据集,马上可以发现一些明显的障碍。首先,三科考试的成绩是无法比较的。由于它们的均值和标准差相去甚远,所以对它们求平均值是没有意义的。你在组合这些考试成绩之前,必须将其变换为可比较的单元。其次,为了评定等级,你需要一种方法来确定某个学生在前述得分上百分比排名。再次,表示姓名的字段只有一个,这让排序任务复杂化了。为了正确地将其排序,需要将姓和名拆开。

使用R操作就方便多了。

options(digits=2)
Student <- c("John Davis", "Angela Williams",
    "Bullwinkle Moose", "David Jones",
    "Janice Markhammer", "Cheryl Cushing",
    "Reuven Ytzrhak", "Greg Knox", "Joel England",
    "Mary Rayburn")
Math <- c(502, 600, 412, 358, 495, 512, 410, 625, 573, 522)
Science <- c(95, 99, 80, 82, 75, 85, 80, 95, 89, 86)
English <- c(25, 22, 18, 15, 20, 28, 15, 30, 27, 18)
roster <- data.frame(Student, Math, Science, English,
    stringsAsFactors=FALSE)

z <- scale(roster[,2:4])

score <- apply(z, 1, mean)                                            
roster <- cbind(roster, score)

y <- quantile(score, c(.8,.6,.4,.2))

roster$grade[score >= y[1]] <- "A"                                    
roster$grade[score < y[1] & score >= y[2]] <- "B"
roster$grade[score < y[2] & score >= y[3]] <- "C"
roster$grade[score < y[3] & score >= y[4]] <- "D"
roster$grade[score < y[4]] <- "F"

name <- strsplit((roster$Student), " ")                                
lastname <- sapply(name, "[", 2)
firstname <- sapply(name, "[", 1)

roster <- cbind(firstname,lastname, roster[,-1])
roster <- roster[order(lastname,firstname),]
roster

firstname   lastname Math Science English score score.1 grade
      Cheryl    Cushing  512      85      28  0.35    0.35     C
        John      Davis  502      95      25  0.56    0.56     B
       Joel    England  573      89      27  0.70    0.70     B
       David      Jones  358      82      15 -1.16   -1.16     F
       Greg       Knox  625      95      30  1.34    1.34     A
      Janice Markhammer  495      75      20 -0.63   -0.63     D
  Bullwinkle      Moose  412      80      18 -0.86   -0.86     D
       Mary    Rayburn  522      86      18 -0.18   -0.18     C
      Angela   Williams  600      99      22  0.92    0.92     A
      Reuven    Ytzrhak  410      80      15 -1.05   -1.05     F












二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

全部回复
2015-3-22 17:32:27
R语言实战 第93页
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

2015-3-22 17:55:53
坐等高手出现,我目前只用by()解决了其中的一步。
给出正确结果的,小弟奉上50论坛币。
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

2015-3-22 22:10:34
use dplyr
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

2015-3-22 22:15:54
soccy 发表于 2015-3-22 22:10
use dplyr
谢谢。如方便的时候,能否示范一下。
感激不尽。
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

2015-3-22 22:18:11
引言
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[n]
注意: 分组计算得到的统计量要清楚样本已经发生了变化, 此时的中位数是不可靠的
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 包也是很强大的哦, 空下来可以学一学
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

点击查看更多内容…
相关推荐
栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群