在r中rowsums_【R语言新书】2.5 数据操作

本文介绍了R语言中dplyr包的使用,包括选择列、筛选行、对行排序、修改列和分组汇总等基本数据操作。通过dplyr的函数,如select、filter、arrange、mutate和summarize,可以实现复杂的数据处理任务。文章还提到了如何使用rowsums函数与其他函数结合,进行多列的选择和修改。此外,还探讨了如何删除重复行、按条件筛选行、对行排序和创建新列的方法。文章强调了dplyr函数的管道操作和不改变原始数据的特点,使数据操作更加便捷。
部署运行你感兴趣的模型镜像

2e97dd3ca89187068613597589867620.png
张敬信:《R语言编程—基于tidyverse》新书信息汇总​zhuanlan.zhihu.com
9484d0bae5f3e3198687ee99911dc315.png

用 dplyr 包实现各种数据操作,通常的数据操作无论多么复杂,往往都可以分解为若干基本数据操作步骤的组合。

共有 5 种基本数据操作:

  • select()——选择列
  • filter()/slice()——筛选行
  • arrange()—— 对行排序
  • mutate()——修改列/创建新列
  • summarize()——汇总

这些函数都可以与

  • group_by()——分组

连用,以改变数据操作的作用域:作用在整个数据框,或数据框的每个分组。

这些函数组合使用就足以完成各种数据操作,它们的相同之处是:

  • 第 1 个参数是数据框,方便管道操作
  • 根据列名访问数据框的列,且列名不用加引号
  • 返回结果是一个新数据框,不改变原数据框

从而,可以方便地实现:"将多个简单操作,依次用管道连接,实现复杂的数据操

"。

另外,若要同时对所选择的多列应用函数,还有强大的 across() 函数,它支持各种选择列语法,搭配 mutate()summarise() 使用,产生非常强大同时修改/汇总多列的效果。

2.5.1 选择列

选择列,包括对数据框做选择列、调整列序、重命名列。

下面以虚拟的学生成绩数据来演示,包含随机生成的 20 个 NA

df = read_xlsx("datas/ExamDatas_NAs.xlsx")
df

0e0bb3325862cd18bd50a7f2f858703f.png

1. 选择列语法

(1) 用列名或索引选择列

df %>%
  select(name, sex, math) # 或者select(2, 3, 5)

a4c9e8585bb1a6f6aa7c39edb5106ab4.png

(2) 借助运算符选择列

  • : 选择连续的若干列
  • ! 选择变量集合的余集(反选)
  • &| 选择变量集合的交或并
  • c() 合并多个选择

(3) 借助选择助手函数

选择指定列:

  • everything(): 选择所有列
  • last_col(): 选择最后一列,可以带参数,如 last_col(5) 选择倒数第 6 列

选择列名匹配的列:

  • starts_with(): 以某前缀开头的列名
  • ends_with(): 以某后缀结尾的列名
  • contains(): 包含某字符串的列名
  • matches(): 匹配正则表达式的列名
  • num_range(): 匹配数值范围的列名,如 num_range("x", 1:3) 匹配 x1, x2, x3

结合函数选择列:

  • where(): 应用一个函数到所有列,选择返回结果为 TRUE 的列,比如与 is.numeric 等函数连用

2. 一些选择列的示例

df %>%
  select(starts_with("m"))

067af25e177c3d32f026e5ee18b1e281.png
df %>%
  select(ends_with("e"))

bf53c9aa9ed7d7ed1fb665516c4a4d15.png
df %>%
  select(contains("a"))

157bc137c7b973ea0aecd1149eb6f1e6.png
  • 根据正则表达式匹配选择列:
df %>%
  select(matches("m.*a"))

e19096510837c758a3677c7fb7d08164.png

d6e3eb8c847b9170c635e4e67a74d583.png
  • 根据条件(逻辑判断)选择列,例如选择所有数值型的列:
df %>%
  select(where(is.numeric))

f272358ed62eae67f4cfc3fc3fbf437d.png

也可以自定义返回 TUREFALSE 的判断函数,支持 purrr 风格公式写法。例如,选择列和 > 3000 的列:

df[, 4:8] %>%
  select(where(~ sum(.x, na.rm = TRUE) > 3000))

ab19cfa7258a14a42881ad5866e96a4e.png

再比如,结合 n_distinct() 选择唯一值数目 < 10 的列:

df %>%
  select(where(~ n_distinct(.x) < 10))

4df718b6450372bfdc4a9e62b656fc48.png

da952b237e06c41613ef9239f97350d4.png

3. 用 - 删除列

df %>%
  select(-c(name, chinese, science)) # 或者select(-ends_with("e"))

a277b3fd4e25b6d767e30802f24bab59.png
df %>%
  select(math, everything(), -ends_with("e"))

e0714de56d26263f546c10b7ac1c1241.png

注意-ends_with() 要放在 everything() 后面,否则删除的列就全回来了。

4. 调整列的顺序

列是根据被选择的顺序排列:

df %>%
  select(ends_with("e"), math, name, class, sex)

e97a42708ea2fbd06b11f041caef3a63.png

everything() 返回未被选择的所有列,将某一列移到第一列时很方便:

df %>%
  select(math, everything())

c5e3b616ba357fd20a64c9fd7ae4ea1c.png

relocate() 函数,将选择的列移到某列之前或之后,基本语法为:

relocate(.data, ..., .before, .after)

例如,将数值列移到 name 列的后面:

df %>%
  relocate(where(is.numeric), .after = name)

7c42ecda6b6a0084408f41ca68503f63.png

b5cde34b769af8af786a8f6e5aac5eb5.png

5. 重命名列

set_names() 为所有列设置新列名:

df %>%
  set_names(" 班级", " 姓名", " 性别", " 语文", " 数学", " 英语", " 品德", " 科学")

8f3de42d90d7982047de3f28b4fe9fe4.png

rename() 只修改部分列名,格式为:新名= 旧名

df %>%
  rename(数学= math, 科学= science)

93f742a790a9318f97ff4175b080a8c4.png

b42cf412041d1a3b142f3737cac502cb.png

7b82cf773c341653700b71afe16e49b7.png

2.5.2 筛选行

筛选行,即按行选择数据子集,包括删除行、对行切片、过滤行。

先创建一个包含重复行的数据框:

set.seed(123)
df_dup <- df %>%
  slice_sample(n = 60, replace = TRUE)

1. 删除行

(1) 删除重复行

dplyr 包中的 distinct() 删除重复行(只保留第 1 个,删除其余)。

df_dup %>%
  distinct()

73d5e75fddcf56f03be1bf77af2e1130.png

也可以只根据某些列判定重复:

df_dup %>%
  distinct(sex, math, .keep_all = TRUE)    # 只根据sex 和math 判定重复

78fea345c590916f70b1d872b14519f7.png

注:默认只返回选择的列,要返回所有列,需要设置参数 .keep_all = TRUE

(2) 删除包含 NA 的行

tidyr 包中的 drop_na() 删除所有包含 NA 的行:

df_dup %>%
  drop_na()

e552c64b03191f72036a641d08686542.png

57ec888626e125d70391b2a7bde38f62.png

也可以只删除某些列包含 NA 的行:

df_dup %>%
  drop_na(sex:math)

4fbae6de690ae48f504fae1cf996c472.png

2. 对行切片:slice_*()

slice 就是对行切片的意思,该系列函数的共同参数:

  • n: 用来指定要选择的行数
  • prop: 用来指定选择的行比例
slice(df, 3:7)                   # 选择3-7 行
slice_head(df, n, prop)          # 从前面开始选择若干行
slice_tail(df, n, prop)          # 从后面开始选择若干行
slice_min(df, order_by, n, prop) # 根据order_by 选择最小的若干行
slice_max(df, order_by, n, prop) # 根据order_by 选择最大的若干行
slice_sample(df, n, prop)        # 随机选择若干行

选择 math 列值中前 5 大的行:

df %>%
  slice_max(math, n = 5)

5ac394a5677e6beeba15a1afe700b13d.png

d903cde1a414b8b7aa901ce05f92febf.png

3. 用 filter() 根据值或条件筛选行

df_dup %>%
  filter(sex == " 男", math > 80)

4ec7a3668b31a83ff0b0e67faaa303af.png

:多个条件之间用“,” 隔开,相当于 and.

df_dup %>%
  filter(sex == " 女", (is.na(english) | math > 80))

4a5785366421fb240c3956b896c49edc.png
df_dup %>%
  filter(between(math, 70, 80))     # 闭区间

ada7fc6c60a211533ba2fdea6cd38f6b.png

4. 在限定列范围内根据条件筛选行

结合 across() 及选择列语法,可以在限定列范围内,根据应用函数得到的结果作为条件筛选行。

(1) 限定列范围内,筛选“所有值都满足某条件的行”

  • 选出所有列范围内,所有值都 > 75 的行:
df[, 4:6] %>%
  filter(across(everything(), ~ .x > 75))    # 不能套all_var

9701756c03c2f2ca587abb4c00ed4d6f.png

注:across() 等价于 all_var()

  • 选出所有列范围内,所有值都不是 NA 的行
df_dup %>%
  filter(across(everything(), ~ !is.na(.x)))

25a45a54fc6ae215c637702fe8aba996.png

1ff3498aafc1709004c110ba7f67f6cf.png

(2) 限定列范围内,筛选“存在值满足某条件的行”

在限定的列范围内,选择“存在值满足某条件的行",目前的支持还不好,暂时需要借助如下函数实现:

rowAny = function(x) rowSums(x, na.rm = TRUE) > 0

选出所有列范围内,存在值包含“bl” 的行

starwars %>%
  filter(rowAny(across(everything(), ~ str_detect(.x, "bl"))))

18771fe4092398eadc97d3174784eaba.png
  • 选出所有列范围内,存在值 > 90 的行
df[, 4:6] %>%
  filter(rowAny(across(everything(), ~ .x > 90)))

7096769921a938e3d1dc82a6380abeb9.png

2e14a11bc5e31af0d6c07a1077c7f1e5.png

347f73dedbaca3978bbd5621d76aa4b6.png

从字符列范围内,选择包含(存在)NA 的行:

df_dup %>%
  filter(rowAny(across(where(is.character), any_vars(is.na(.)))))

f7e88d38367ebb73d0c40e82e608d31e.png

2.5.3 对行排序

dplyr 包中的 arrange() 对行排序,默认是递增。

df_dup %>%
  arrange(math, sex)

660da9e066006d1922ccc6153c481e31.png

若要递减排序,套一个 desc()

df_dup %>%
  arrange(desc(math))             # 递减排序

23cd6aeb27b65a13f8d2bc22c202cef4.png

2.5.4 修改列

修改列,即修改数据框的列,计算新列。

1. 创建新列

dplyr 包中的 mutate() 创建或修改列,返回原数据框并增加新列;若改用transmute() 则只返回增加的新列。

若只给新列 1 个值,则循环使用得到值相同的一列:

df %>%
  mutate(new_col = 5)

ef5975f1354b59d59a51118524deb6f4.png

正常是以长度等于行数的向量赋值:

df %>%
  mutate(new_col = 1:n())

83ef8f7e782b2c10d673572162e1b770.png

注: n() 返回当前分组的样本数, 未分组则为总行数。

2. 计算新列

用数据框的列计算新列,若修改当前列,只需要赋值给原列名。

df %>%
  mutate(total = chinese + math + english + moral + science)

ceb10073ed9538642a7291b75525f78e.png

注意:不能用 sum(), 它会将整个列的内容都加起来,类似的还有 mean().

在同一个 mutate() 中可以同时创建或计算多个列,它们是从前往后依次计算,所以可以使用前面新创建的列,例如

  • 计算 dfmath 列的中位数
  • 创建标记 math 是否大于中位数的逻辑值列
  • as.numeric()TRUE/FALSE 转化为 1/0
df %>%
  mutate(med = median(math, na.rm = TRUE),
  label = math > med,
  label = as.numeric(label))

609c6c5063a7ce8792323b4c5263e2b3.png

3. 修改多列

结合 across() 和选择列语法可以应用函数到多列,从而实现同时修改多列。

(1) 应用函数到所有列

  • 将所有列转化为字符型:
df %>%
  mutate(across(everything(), as.character))

5eb493ca59492a5afe0cb91e8ff3661c.png

(2) 应用函数到满足条件的列

  • 对所有数值列做归一化:
rescale = function(x) {
  rng = range(x, na.rm = TRUE)
  (x - rng[1]) / (rng[2] - rng[1])
}
df %>%
  mutate(across(where(is.numeric), rescale))

3c4456f8b62d6201728b52c5b1612c2b.png

(3) 应用函数到指定的列

iris 中的 lengthwidth 测量单位从厘米变成毫米:

as_tibble(iris) %>%
  mutate(across(contains("Length") | contains("Width"), ~ .x * 10))

a9f115f9d46fd8d45e39335aa89ca2d6.png

3acd364888c2f22aff03426893adec2b.png

4. 替换 NA

(1) replace_na()

实现用某个值替换一列中的所有 NA 值,该函数接受一个命名列表,其成分为列名 = 替换值:

starwars %>%
  replace_na(list(hair_color = "UNKNOWN", height = mean(.$height, na.rm = TRUE)))

6ac498540e66bd0ba72d4297ad8a03b3.png

(2) fill()

用前一个(或后一个)非缺失值填充 NA。有些表在记录时,会省略与上一条记录相同的内容,如下表:

load("datas/gap_data.rda")
knitr::kable(gap_data, align="c")

0ed26537642e20ba83ee55e5e5188d55.png

tidyr 包中的 fill() 适合处理这种结构的缺失值, 默认是向下填充,即用上一个非缺失值填充:

gap_data %>%
  fill(site, species)

e7d36b4a8decebfdb62bf7831fb167ed.png

5. 重新编码

实际中,经常需要对列中的值进行重新编码。

(1) 两类别情形:if_else()

if_else() 作是/否决策以确定用哪个值做重新编码:

df %>%
  mutate(sex = if_else(sex == " 男", "M", "F"))

ee2b65f88f1cf0d7d54ed0d849d4fc1b.png

(2) 多类别情形:case_when()

case_when() 做更多条件下的重新编码,避免使用很多 if_else() 嵌套:

df %>%
  mutate(math = case_when(math >= 75 ~ "High",
                          math >= 60 ~ "Middle",
                          TRUE       ~ "Low"))

41d792860c3c5f07033f61bc46ee404e.png

case_when() 中用的是公式形式,

  • 左边是返回 TRUEFALSE 的表达式或函数
  • 右边是若左边表达式为 TRUE ,则重新编码的值,也可以是表达式或函数
  • 每个分支条件将从上到下的计算,并接受第一个 TURE 条件
  • 最后一个分支直接用 TRUE 表示若其它条件都不为 TRUE 时怎么做

(3) 更强大的重新编码函数

基于 tidyverse 设计哲学, sjmisc 包实现了对变量做数据变换,如重新编码、二分或分组变量、设置与替换缺失值等; sjmisc 包也支持标签化数据,这对操作 SPSS 或 Stata 数据集特别有用。

df7dd64b9d7a178f9c35d3006fb0f2fd.png
library(sjmisc)
df %>%
  rec(math, rec = "min:59= 不及格 ; 60:74= 中 ; 75:85= 良 ; 85:max= 优 ",
    append = FALSE) %>%
  frq()                                     # 频率表

94b9ba0a451d0749176babe1a2892d93.png

40afed25064ddab395b5343ee8f25958.png

: 新值的值标签可以在重新编码时一起设置,只需要在每个重编码对后接上中括号标签。

2.5.5 分组汇总

分组汇总,相当于 Excel 的透视表功能

对未分组的数据框,一些操作如 mutate() 是在所有行上执行 —--- 或者说,整个数据框是一个分组,所有行都属于它。

若数据框被分组,则这些操作是分别在每个分组上独立执行。可以认为是,将数据框拆分为更小的多个数据框。在每个更小的数据框上执行操作,最后再将结果合并回来。

1. 创建分组

group_by() 创建分组,只是对数据框增加了分组信息(用 group_keys() 查看),

并不是真的将数据分割为多个数据框。

df_grp = df %>%
group_by(sex)
group_keys(df_grp)         # 分组键值 ( 唯一识别分组 )
group_indices(df_grp)      # 查看每一行属于哪一分组
group_rows(df_grp)         # 查看每一组包含哪些行
ungroup(df_grp)            # 解除分组
  • 其它分组函数

真正将数据框分割为多个分组: group_split() , 返回列表,其每个成分是一个分组数据框

将数据框分组( group_by ),再做嵌套( nest ),生成嵌套数据框: group_nest()

iris %>%
  group_nest(Species)

c0bd254d35ec08ca518fdfd0a28eb71c.png

21ef41c7ac9a77311889c4307da0a579.png
  • purrr 风格的分组迭代:将函数 .f 依次应用到分组数据框 .data 的每个分组上

- group_map(.data, .f, ...) : 返回列表

- group_walk(.data, .f, ...) : 不返回,只关心副作用

- group_modify(.data, .f, ...) : 返回修改后的分组数据框

2. 分组汇总

对数据框做分组最主要的目的就是做分组汇总,汇总就是以某种方式组合行,用 dplyr 包中的 summarise() 函数实现,结果只保留分组列唯一值和新创建的汇总列。

(1) summarise()

可以与很多自带或自定义的汇总函数连用,常用的汇总函数有:

  • n() : 观测数
  • n_distinct(var) : 变量 var 的唯一值数目
  • sum(var) , max(var) , min(var) , . . .
  • mean(var) , median(var) , sd(var) , IQR(var) , .. .
df %>%
   group_by(sex) %>%
  summarise(n = n(),
            math_avg = mean(math, na.rm = TRUE),
            math_med = median(math))

2a2c527f2d279e5b88aaf132503401a4.png

函数 summarise() ,配合 across() 可以对所选择的列做汇总。好处是可以借助辅助选择器或判断条件选择多列,还能在这些列上执行多个函数,只需要将它们放入一个列表。

(2) 对某些列做汇总

df %>%
  group_by(class, sex) %>%
  summarise(across(contains("h"), mean, na.rm = TRUE))

7de691cece42e9819bb3bfff9b3086fc.png

(3) 对所有列做汇总

df %>%
  select(-name) %>%
  group_by(class, sex) %>%
  summarise(across(everything(), mean, na.rm = TRUE))

7bc8489baf52129eb355f62a575d8015.png

(4) 对满足条件的列做多种汇总

df_grp = df %>%
  group_by(class) %>%
  summarise(across(where(is.numeric), 
                   list(sum = sum, mean = mean, min = min, na.rm = TRUE)))
df_grp

8c22967a25c3c9325c01f05b975eb1e4.png

可读性不好,再来个宽变长:

df_grp %>%
  pivot_longer(-class, names_to = c("Vars", ".value"), names_sep = "_")

1a9c5e7a740a452140e4caa80b66efc4.png

(5) 支持多返回值的汇总函数

summarise() 以前只支持一个返回值的汇总函数,如 sum, mean 等。现在也支持多返回值(返回向量值、甚至是数据框)的汇总函数,如 range(), quantile() 等。

qs = c(0.25, 0.5, 0.75)
df_q = df %>%
  group_by(sex) %>%
  summarise(math_qs = quantile(math, qs, na.rm = TRUE), q = qs)
df_q

56adaa22a19b57c8268433694527b248.png

可读性不好,再来个长变宽:

df_q %>%
  pivot_wider(names_from = q, values_from = math_qs, names_prefix = "q_")

53eeb4dff9a80ecc07584f1a03e577a5.png

3. 分组计数

count() 按分类变量 classsex 分组,并按分组大小排序:

df %>%
  count(class, sex, sort = TRUE)

1b1f3bab5f2f4825336f86f47dedd9b6.png

对已分组的数据框,用 tally() 计数:

df %>%
  group_by(math_level = cut(math,
           breaks = c(0, 60, 75, 80, 100),
           right = FALSE)) %>%
 tally()

775ea92198889423d615a53fcc99a236.png

1772ad194f67b9743e42e9eac47fc587.png

add_count()add_tally() 可为数据集增加一列按分组变量分组的计数:

df %>%
  add_count(class, sex)

e3ec1d5ed575a4b4d85b91fb819e4e46.png

0a9c3971c2d31d39aa00b33081ee2024.png

本节部分内容参阅 (Hadley Wickham 2017), (Desi Quintans 2019), Vignettes of dplyr.

主要参考文献:

  1. Hadley Wickham. R for Data Science,2017.
  2. Desi Quintans, Jeff Powell. Working in the Tidyverse.http://www.hiercourse.com/
  3. Vignettes of dplyr package.

________________________________

版权声明:原创作品,版权所有,禁止用于一切出版。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值