修改列
修改列,即修改数据框的列,计算新列。
创建列——mutate()
用dplyr 包中的mutate() 创建或修改列,返回原数据框并增加新列;
若改用transmute() 则只
返回增加的新列,新列默认加在最后一列,参数.before, .after 可以设置新列的位置。
在同一个mutate() 中可以同时创建或计算多个列,它们是从前往后依次计算,所以可以使用前面
新创建的列,例如
修改多列 - across()与选择列语法结合
- 应用函数到所有列
将所有列转化为字符型:
df %>%
mutate(across(everything(), as.character))
- 应用函数到满足条件的列
对所有数值列做归一化:
rescale = function(x) {
rng = range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
df %>%
mutate(across(where(is.numeric), rescale))
- 应用函数到指定的列
将iris 中的length 和width 测量单位从厘米变成毫米:
as_tibble(iris) %>%
mutate(across(contains("Length") | contains("Width"), ~ .x * 10))
4. 替换NA
- replace_na()
实现用某个值替换一列中的所有NA 值,该函数接受一个命名列表,其成分为列名= 替换值:
实现用某个值替换一列中的所有NA 值,该函数接受一个命名列表,其成分为列名= 替换值:
- fill()——填充
用前一个(或后一个)非缺失值填充NA。
gap_data %>%
fill(site, species)
5. 重新编码
- 两类别情形:if_else()
df %>%
mutate(sex = if_else(sex == " 男", "M", "F"))
- 多类别情形:case_when()
用case_when() 做更多条件下的重新编码,避免使用很多if_else() 嵌套:
df %>%
mutate(math = case_when(math >= 75 ~ "High",
math >= 60 ~ "Middle",
TRUE ~ "Low"))
case_when() 中用的是公式形式,
- 左边是返回TRUE 或FALSE 的表达式或函数
- 右边是若左边表达式为TRUE,则重新编码的值,也可以是表达式或函数
- 每个分支条件将从上到下的计算,并接受第一个TRUE 条件
- 最后一个分支直接用TRUE 表示若其他条件都不为TRUE 时怎么做
- 更强大的重新编码函数 ——sjmisc 包rec()
sjmisc 包实现了对变量做数据变换,如重新编码、二分或分组变量、设置与替换缺失值等;sjmisc 包也支持标签化数据。
rec(), 可以将变量的旧值重新编码为新值,基本格式
rec(x, rec, append, ...)
- x:为数据框(或向量);
- append:默认为TRUE, 则返回包含重编码新列的数据框,FALSE 则只返回重编码的新列;
- rec:设置重编码模式,即哪些旧值被哪些新值取代,具体如下:
1.1 重编码对:每个重编码对用“;” 隔开,例如rec="1=1; 2=4; 3=2; 4=3"
1.2 多值:多个旧值(逗号分隔) 重编码为一个新值,例如rec="1,2=1; 3,4=2"
1.3 值范围:用冒号表示值范围,例如rec="1:4=1; 5:8=2"
1.4 数值型值范围: 带小数部分的数值向量, 值范围内的所有值将被重新编码, 例如rec="1:2.5=1; 2.6:3=2"8
1.5 “min” 和“max’’:最小值和最大值分别用min 和max 表示,例如rec = "min:4=1; 5:max=2"(min 和max 也可以作为新值,如5:7=max, 表示将5~7 编码为max(x))
1.6 “else’’:所有未设定的其他值,用else 表示,例如rec="3=1; 1=2; else=3"
1.7 “copy’’:else 可以结合copy 一起使用,表示所有未设定的其他值保持原样(从原数值copy),例如rec="3=1; 1=2; else=copy"
1.8 NAs:NA 既可以作为旧值,也可以作为新值,例如rec="NA=1; 3:5=NA"
1.9 “rev’’:设置反转值顺序
2.1 非捕获值:不匹配的值将设置为NA, 除非使用else 和copy.
library(sjmisc)
df %>%
rec(math, rec = "min:59= 不及格; 60:74= 中; 75:85= 良; 85:max= 优",
append = FALSE) %>%
frq() # 频率表
筛选行
即按行选择数据子集,包括过滤行、对行切片、删除行
- filter()
提供筛选条件给filter() 则返回满足该条件的行。筛选条件可以是长度同行数的逻辑向量,更一般的是基于能返回这样逻辑向量的列表达式。
df_dup %>%
filter(sex == " 女", (is.na(english) | math > 80))
df_dup %>%
filter(between(math, 70, 80)) # 闭区间
- 在限定列范围内根据条件筛选行
if_any() 和if_all()
2.1 限定列范围内,筛选” 所有值都满足某条件的行”, 使用if_all()
选出第4-6 列范围内,所有值都> 75 的行
df %>%
filter(if_all(4:6, ~ .x > 75))
2.2 限定列范围内,筛选” 存在值满足某条件的行” if_any()
选出所有列范围内,存在值包含“bl” 的行
starwars %>%
filter(if_any(everything(), ~ str_detect(.x, "bl")))
选出数值列范围内,存在值> 90 的行
df %>%
filter(if_any(where(is.numeric), ~ .x > 90))
- 对行切片: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)
- 删除行
(1) 删除重复行:dplyr 包中的distinct() 删除重复行(只保留第1 个,删除其余)。
df_dup %>%
distinct()
(2)删除包含NA 的行:drop_na()删除所有包含NA 的行
- 对行排序-arrange() 对行排序,默认是递增,递减加"—"
分组汇总
mutate() 是在所有行上执行
- 创建分组 - group_by()
group_keys(df_grp) # 分组键值(唯一识别分组)
group_indices(df_grp) # 查看每一行属于哪一分组
group_rows(df_grp) # 查看每一组包含哪些行
ungroup(df_grp) # 解除分组
其他分组函数
- 真正将数据框分割为多个分组:group_split(), 返回列表,其每个成分是一个分组数据框
- 将数据框分组(group_by),再做嵌套(nest),生成嵌套数据框:group_nest()
- purrr 风格的分组迭代:将函数.f 依次应用到分组数据框.data 的每个分组上
group_map(.data, .f, ...): 返回列表
group_walk(.data, .f, ...): 不返回,只关心副作用
group_modify(.data, .f, ...): 返回修改后的分组数据框
分组是一种强大的数据思维,当您想分组并分别操作(包括汇总)每组数据时,应该优先采用group_by() + 操作,而不是分割数据+ 循环迭代。
汇总-summarise()
结果只保留分组列唯一值和新创建的汇总列
(1) summarise()
- n(): 观测数
- n_distinct(var): 变量var 的唯一值数目
- sum(var), max(var), min(var), . . .
- mean(var), median(var), sd(var), IQR(var), . . .
与across连用可以对所选择的列做汇总
(2) 对某些列做汇总
df %>%
group_by(class, sex) %>%
summarise(across(contains("h"), mean, na.rm = TRUE))
(3) 对所有列做汇总
df %>%
select(-name) %>%
group_by(class, sex) %>%
summarise(across(everything(), mean, na.rm = TRUE))
(4) 对满足条件的列做多种汇总
df_grp = df %>%
group_by(class) %>%
summarise(across(where(is.numeric),
list(sum=sum, mean=mean, min=min), na.rm = TRUE))
可读性不好,再来个宽变长:
df_grp %>%
pivot_longer(-class, names_to = c("Vars", ".value"), names_sep = "_")
(5) 支持多返回值的汇总函数
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
可读性不好,再来个长变宽:
df_q %>%
pivot_wider(names_from = q, values_from = math_qs, names_prefix = "q_")
3. 分组计数—count()
用count() 按分类变量class 和sex 分组,并按分组大小排序:
df %>%
count(class, sex, sort = TRUE)
对已分组的数据框,用tally() 计数:
df %>%
group_by(math_level = cut(math, breaks = c(0, 60, 75, 80, 100), right = FALSE)) %>%
tally()
注:count() 和tally() 都有参数wt 设置加权计数。
用add_count() 和add_tally() 可为数据集增加一列按分组变量分组的计数:
df %>%
add_count(class, sex)
6 按行汇总—rowwise() 函数
使用rowwise() 后并不是真的改变数据框,只是创建了按行元信息,改变了数据框的操作逻辑
rf = df %>%
rowwise()
rf %>%
mutate(total = sum(c(chinese, math, english)))
c_across() 是为按行方式(rowwise) 在选定的列范围汇总数据而设计的,它没有提供.fns参数,只能选择列。
rf %>%
mutate(total = sum(c_across(where(is.numeric))))
只是做按行求和或均值,直接用rowSums() / rowMeans() 速度更快
rowwise 行化操作的缺点是速度相对更慢,更建议用1.6.2 节讲到的pmap() 逐行迭代。
总结逐行迭代
iris[1:4] %>% # apply
mutate(avg = apply(., 1, mean))
iris[1:4] %>% # rowwise (慢)
rowwise() %>%
mutate(avg = mean(c_across()))
iris[1:4] %>% # pmap
mutate(avg = pmap_dbl(., ~ mean(c(...))))
iris[1:4] %>% # asplit(逐行分割) + map
mutate(avg = map_dbl(asplit(., 1), mean))
窗口函数
函数有: cumsum()、cummean()、rank()、lead()、lag()
-
排名和排序函数
min_rank():从小到大排名(ties.method="min") -
移位函数
lag(): 取前一个值,数据整体右移一位,相当于将时间轴滞后一个单位
lead(): 取后一个值,数据整体左移一位,相当于将时间轴超前一个单位 -
累计汇总
cumany(x): 用来选择遇到第一个满足条件之后的所有行
cumany(!x): 用来选择遇到第一个不满足条件之后的所有行
cumall(x): 用来选择所有行直到遇到第一个不满足条件的行
cumall(!x): 用来选择所有行直到遇到第一个满足条件的行
选择第一次透支之后的所有行
dt %>%
filter(cumany(balance < 0))
选择所有行直到第一次透支
dt %>%
filter(cumall(!(balance < 0)))
滑窗迭代—slide_*()
窗口函数的典型应用包括滑动平均、累计和以及更复杂如滑动回归.
slider 包提供了slide_()* 系列函数实现滑窗迭代,其基本格式为:
slide_(.x, .f, ..., .before, .after, .step, .complete)*
- .x: 为窗口所要滑过的向量
*.f: 要应用于每个窗口的函数,支持purrr 风格公式写法
*...: 用来传递.f 的其他参数
*.before, .after: 设置窗口范围当前元往前、往后几个元,可以取Inf(往前、往后所有元)
*.step: 每次函数调用,窗口往前移动的步长
*.complete: 设置两端处是否保留不完整窗口,默认为FALSE
金融时间序列数据经常需要计算滑动平均,比如计算sales 的3 日滑动平均:
library(slider)
dt %>%
mutate(avg_3 = slide_dbl(sales, mean, .before = 1, .after = 1))
计算sales 真正的3 日滑动平均:
dt %>%
mutate(avg_3 = slide_index_dbl(sales, day, mean, .before = 1, .after = 1))
涉及日期时,需要重点测试结果
slide_index(.x, .i, .f, ...)
参数.i 用来传递索引向量,实现根据“.i 的当前元+ 其前/后若干元” 创建相应的.x 的滑动窗口。
在自定义函数中整洁计算
- 数据屏蔽:使得可以不用带数据框(环境变量)名字,就能使用数据框内的变量(数据变量),便于在数据集内计算值
数据屏蔽为直接使用带来了代码简洁,但作为函数参数时的间接使用,正常是环境变量,要想作为数据变量使用,则需要用两个大括号括起来{{var}}
var_summary = function(data, var) {
data %>%
summarise(n = n(), mean = mean({{var}}))
}
mtcars %>%
group_by(cyl) %>%
var_summary(mpg)
若是字符向量形式,想作为数据变量,则需要在函数体中使用.data[[var]],这里.data 是代替数据集的代词:
var_summary = function(data, var) {
data %>%
summarise(n = n(), mean = mean(.data[[var]]))
}
mtcars %>%
group_by(cyl) %>%
var_summary("mpg")
- 整洁选择:即各种选择列语法,便于使用数据集中的列
需要用两个大括号括起来{{var}}
summarise_mean = function(data, vars) {
data %>%
summarise(n = n(), across({{vars}}, mean))
}
mtcars %>%
group_by(cyl) %>%
summarise_mean(where(is.numeric))
若是字符向量形式,则需要借助函数all_of() 或any_of(),取决于你的选择:
image.png创建tidyverse风格的整洁函数,另一种做法是使用引用与反引用机制
额外的两个步骤:
- 用enquo()让函数自动引用其参数
- 用‘’‘!!’反引用该参数
需要传递多个参数时,需要使用特殊参数“...”
网友评论