乐趣区

关于r:R语言之-dplyr-包

文章和代码曾经归档至【Github 仓库:https://github.com/timerring/dive-into-AI】或者公众号【AIShareLab】回复 R 语言 也可获取。

这个包以一种对立的标准更高效地解决数据框。dplyr 包里解决数据框的所有函数的第一个参数都是数据框名。

上面以 MASS 包里的 birthwt 数据集为例,介绍 dplyr 包里罕用函数的用法。该数据集来自一项对于新生儿低体重危险因素的病例对照钻研。首先加载该数据集并查看其相干信息。

library(dplyr)
data(birthwt, package = "MASS")
# ??birthwt

数据集 birthwt 里一共蕴含 189 个钻研对象、10 个变量。其中后果变量 bwt 是新生儿的体重(单位:g),变量 low 是将 bwt 的取值以 2500g 为分点转换成的一个二分类变量。其余 8 个变量均为预测变量,包含孕妇的年龄(age)、种族(race)、吸烟情况(smoke)、高血压史(ht)等。

1. 应用 filter() 和 slice() 筛选行

函数 filter() 能够基于观测值筛选数据框的一个子集。第一个参数是数据框名,第二个参数以及随后的参数是用来筛选数据框的表达式。

例如,筛选数据框里年龄大于 35 岁的对象的所有记录:

filter(birthwt, age > 35)

函数 filter () 里能够用逗号分隔多个条件。应用上面的命令将会抉择抉择年龄大于 35 岁,并且出世体重小于 2500g 或者大于 4000g 的所有记录,因为记录较多,这里只显示了前 10 行。

head(filter(birthwt, age > 35, bwt < 2500 | bwt > 4000),10)

函数 slice() 能够依照行号抉择指定的行。例如,上面的命令抉择数据集外面的第 2 行到第 5 行。

slice(birthwt, 2:5)

2. 应用 arrange() 排列行

有时候咱们想要将数据框的记录依照某个变量进行排序,函数 arrange() 能够实现这个性能。上面的命令将数据框依照变量 bwt 的值从小到大进行排序后显示:

arrange(birthwt, bwt) # 默认升序

在下面的输入中,第 6 行和第 7 行的变量 bwt 的值都是 1588,在这种状况下如果还想将数据框依照第二个变量排序,只须要在函数 arrange() 里加上第二个变量即可。例如,上面的命令将数据框依照变量 bwt 的值从小到大排序,在 bwt 取值相等的状况下再依照第二个变量 age 的值从小到大排序。

arrange(birthwt, bwt, age)

如果想把数据框依照某个变量的值从大到小进行排序,能够借助函数 desc() 实现。

arrange(birthwt, desc(bwt))
# 等价于
arrange(birthwt, - bwt)

3. 应用 select() 抉择列

函数 select() 用于抉择数据框中的列(变量)。

# 上面的命令抉择数据框外面的 bwt、age、race 和 smoke 这 4 个变量组成新的数据框。select(birthwt, bwt, age, race, smoke)

请留神,MASS 包里有一个同名函数 select(),如果同时加载了 dplyr 包和 MASS 包,R 会默认应用较后加载的包里的函数。为了防止混同,咱们能够应用符号 :: 特地指明应用某一个包里的函数,例如 dplyr::select()。之后咱们将会对函数 select() 作进一步介绍。

4. 应用 mutate() 增加新变量

函数 mutate() 用于在数据框中创立新的变量。上面的命令将数据集 birthwt 里的变量 lwt(单位:lb)乘以系数 0.4536 后生成新的变量 lwt.kg(1lb ≈ 0.4536kg)。

# 当然如果想要用新变量替换原来的变量,只需把新变量命名为原来的变量名:mutate(birthwt, lwt.kg = lwt*0.4536)

5. 应用 summarise() 计算统计量

函数 summarise() 能够用于计算数据框中某个变量的指定统计量。

例如,计算变量 bwt 的样本均值和样本标准差:

summarise(birthwt, Mean.bwt = mean(bwt), Sd.bwt = sd(bwt))

6. 应用 group\_by() 拆分数据框

函数 group_by() 能够将数据框依照某一个或某几个分类变量拆分成多个数据框。例如:

group_by(birthwt, race)
str(group_by(birthwt, race))

# ============ 输入 =============
grouped_df [189 × 10] (S3: grouped_df/tbl_df/tbl/data.frame)
 $ low  : int [1:189] 0 0 0 0 0 0 0 0 0 0 ...
 $ age  : int [1:189] 19 33 20 21 18 21 22 17 29 26 ...
 $ lwt  : int [1:189] 182 155 105 108 107 124 118 103 123 113 ...
 $ race : int [1:189] 2 3 1 1 1 3 1 3 1 1 ...
 $ smoke: int [1:189] 0 0 1 1 1 0 0 0 1 1 ...
 $ ptl  : int [1:189] 0 0 0 0 0 0 0 0 0 0 ...
 $ ht   : int [1:189] 0 0 0 0 0 0 0 0 0 0 ...
 $ ui   : int [1:189] 1 0 0 1 1 0 0 0 0 0 ...
 $ ftv  : int [1:189] 0 3 1 2 0 0 1 1 1 0 ...
 $ bwt  : int [1:189] 2523 2551 2557 2594 2600 2622 2637 2637 2663 2665 ...
 - attr(*, "groups")= tibble [3 × 2] (S3: tbl_df/tbl/data.frame)
  ..$ race : int [1:3] 1 2 3
  ..$ .rows: list<int> [1:3] 
  .. ..$ : int [1:96] 3 4 5 7 9 10 15 16 18 20 ...
  .. ..$ : int [1:26] 1 17 29 30 31 33 35 41 43 70 ...
  .. ..$ : int [1:67] 2 6 8 11 12 13 14 19 21 24 ...
  .. ..@ ptype: int(0) 
  ..- attr(*, ".drop")= logi TRUE

函数 group\_by() 不会扭转数据框的外观,而会 扭转它与其余 dplyr 动词函数的作用形式。因而,下面的输入后果看上去和原来的数据框没有什么差异,但本质上是不同的。最实质的差异是多了一个分组属性(Groups),即下面的后果蕴含了 3 个数据框,别离对应于变量 race 的 3 个类别。

你还可能留神到下面输入对象的格局(grouped_df [189 × 10] (S3: grouped_df/tbl_df/tbl/data.frame))。与 R/Rstudio 上不同,notebook 这里把它显示成了 A grouped_df: 189 × 10(而非 # A tibble: 189 x 10),理论它依然蕴含 tibble(留神其中的 - attr(*, "groups")= tibble [3 × 2] (S3: tbl_df/tbl/data.frame))。另外,它没有显示 Groups 属性信息,理论应为 # Groups: race [3]

tibble 是 tidyverse 系列包(包含 dplyr 包)提供的一种相似数据框的格局。绝对于传统的数据框,tibble 在很多方面具备劣势,感兴趣的读者能够参阅函数 tibble() 的帮忙文档。咱们能够用函数 as_tibble() 将传统的数据框转换为 tibble,也能够用函数 as.data.frame() 将 tibble 转换成传统的数据框。

as_tibble(birthwt)

上面咱们将会看到,把函数 group\_by() 和 summarise() 联结应用能不便地对变量进行分组统计。

7. 应用传递符 %>% 组合多个操作

咱们常常须要对一个数据框做一系列的操作,前面一个操作的输出须要用前一个操作的输入后果。

# 第一步把数据框 birthwt 外面的变量 race 转换成因子并给各个程度增加标签,把新的数据框命名为 birthwt1
birthwt1 <- mutate(birthwt, 
                   race = factor(race, labels = c("white", "black", "other")))
# 第二步把数据框 birthwt1 依照变量 race 分组,把分组后的对象命名为 birthwt.group;birthwt.group <- group_by(birthwt1, race)
# 第三步对于分组对象 birthwt.group 计算各组中变量 bwt 的平均值。summarise(birthwt.group, mean(bwt))

这种办法的最大毛病是须要为每个两头后果建设一个变量。在很多状况下,比方在下面的示例中,这些两头变量其实是没有什么实际意义的。咱们须要给这些两头变量命名,而且这些两头变量会保留在工作空间中占用内存。传递操作符 %>% 将该符号之前的对象传递给符号前面的函数并作为函数的第一个参数值。例如:

c(2, 4, 6, 8) %>% matrix(nrow = 2)

因为 dplyr 包外面的函数 第一个参数总是数据框,所以这些函数配合传递操作符解决数据框十分不便。上面用传递操作符改写下面的命令:

birthwt %>%
  mutate(race = factor(race, labels = c("white", "black", "other"))) %>%
  group_by(race) %>%
  summarise(mean(bwt))

上述代码的重点在于动词函数,而不是函数中的参数。在浏览这一串代码组合时,能够将它们当成一系列的规定动作。

我的项目实战

epiDisplay 包里的数据集 Planning 来自 20 世纪 80 年代中期泰国的一项计划生育考察钻研,请通过其帮助文件查看数据信息并整顿该数据集。

library(epiDisplay)
data(Planning)
print(des(Planning))

names(Planning) <- tolower(names(Planning))   # 把变量名变为小写字母
summary(Planning)

table(duplicated(Planning$id))       # 查看是否有反复 id;# FALSE  TRUE 
#   250     1
which(duplicated(Planning$id))       # 找出反复 id 的行号;把 XXXXXX 替换成正确的代码
#   216
Planning$id # 验证下
Planning$id[216] <- 216  # 修改反复 id;

library(dplyr)
Planning <- mutate(
  Planning,
  relig = ifelse(relig == 9, NA, relig),     # 将变量 relig 中的 9 变成 NA
  ped = ifelse(ped == 0 | ped == 9, NA, ped), # 将变量 ped 中的 0 和 9 变成 NA
  income = ifelse(income == 9, NA, income),   # 将变量 income 中的 9 变成 NA
  am = ifelse(am == 99, NA, am),           # 将变量 am 中的 99 变成 NA
  reason = ifelse(reason == 9, NA, reason),  # 将变量 reason 中的 9 变成 NA
  bps = ifelse(bps == 0 | bps == 999, NA, bps), # 将变量 bps 中的 0 和 999 变成 NA
  bpd = ifelse(bpd == 0 | bpd == 999, NA, bpd), # 将变量 bpd 中的 0 和 999 变成 NA
  wt = ifelse(wt == 0 | wt > 99, NA, wt),    # 将变量 wt 中的 0 和大于 99 的值变成 NA
  ht = ifelse(ht == 0 | ht > 300, NA, ht)    # 将变量 ht 中的 0 和大于 300 的值变成 NA;)
退出移动版