js css html

R语言编程-Tidyverse 书籍-第二章(2)

2022-10-07  本文已影响0人  Hello育种

修改列

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

创建列——mutate()

用dplyr 包中的mutate() 创建或修改列,返回原数据框并增加新列;
若改用transmute() 则只
返回增加的新列,新列默认加在最后一列,参数.before, .after 可以设置新列的位置。

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

修改多列 - across()与选择列语法结合

  1. 应用函数到所有列
    将所有列转化为字符型:
df %>%
    mutate(across(everything(), as.character))
  1. 应用函数到满足条件的列
    所有数值列做归一化:
rescale = function(x) {
         rng = range(x, na.rm = TRUE)
         (x - rng[1]) / (rng[2] - rng[1])
}
df %>%
       mutate(across(where(is.numeric), rescale))
  1. 应用函数到指定的列
    将iris 中的length 和width 测量单位从厘米变成毫米:
as_tibble(iris) %>%
    mutate(across(contains("Length") | contains("Width"), ~ .x * 10))

4. 替换NA

  1. replace_na()
    实现用某个值替换一列中的所有NA 值,该函数接受一个命名列表,其成分为列名= 替换值:
实现用某个值替换一列中的所有NA 值,该函数接受一个命名列表,其成分为列名= 替换值:
  1. fill()——填充
    用前一个(或后一个)非缺失值填充NA。
gap_data %>%
fill(site, species)

5. 重新编码

  1. 两类别情形:if_else()
df %>%
   mutate(sex = if_else(sex == " 男", "M", "F"))
  1. 多类别情形:case_when()
    用case_when() 做更多条件下的重新编码,避免使用很多if_else() 嵌套:
df %>%
  mutate(math = case_when(math >= 75 ~ "High",
                                               math >= 60 ~ "Middle",
                                             TRUE ~ "Low"))

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

  1. 更强大的重新编码函数 ——sjmisc 包rec()
    sjmisc 包实现了对变量做数据变换,如重新编码、二分或分组变量、设置与替换缺失值等;sjmisc 包也支持标签化数据。
    rec(), 可以将变量的旧值重新编码为新值,基本格式
    rec(x, rec, append, ...)
library(sjmisc)
df %>%
rec(math, rec = "min:59= 不及格; 60:74= 中; 75:85= 良; 85:max= 优",
append = FALSE) %>%
frq() # 频率表

筛选行

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

  1. filter()
    提供筛选条件给filter() 则返回满足该条件的行。筛选条件可以是长度同行数的逻辑向量,更一般的是基于能返回这样逻辑向量的列表达式。
df_dup %>%
   filter(sex == " 女", (is.na(english) | math > 80))

df_dup %>%
     filter(between(math, 70, 80)) # 闭区间

  1. 在限定列范围内根据条件筛选行
    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))
  1. 对行切片:slice_*()
    该系列函数的共同参数:

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. 删除行
    (1) 删除重复行:dplyr 包中的distinct() 删除重复行(只保留第1 个,删除其余)。
df_dup %>%
   distinct()

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

  1. 对行排序-arrange() 对行排序,默认是递增,递减加"—"

分组汇总

mutate() 是在所有行上执行

  1. 创建分组 - group_by()
group_keys(df_grp) # 分组键值(唯一识别分组)
group_indices(df_grp) # 查看每一行属于哪一分组
group_rows(df_grp) # 查看每一组包含哪些行
ungroup(df_grp) # 解除分组

其他分组函数

分组是一种强大的数据思维,当您想分组并分别操作(包括汇总)每组数据时,应该优先采用group_by() + 操作,而不是分割数据+ 循环迭代。

汇总-summarise()

结果只保留分组列唯一值和新创建的汇总列
(1) summarise()

与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()

  1. 排名和排序函数
    min_rank():从小到大排名(ties.method="min")

  2. 移位函数
    lag(): 取前一个值,数据整体右移一位,相当于将时间轴滞后一个单位
    lead(): 取后一个值,数据整体左移一位,相当于将时间轴超前一个单位

  3. 累计汇总
    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)*

金融时间序列数据经常需要计算滑动平均,比如计算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_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")
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风格的整洁函数,另一种做法是使用引用与反引用机制

额外的两个步骤:

  1. 用enquo()让函数自动引用其参数
  2. 用‘’‘!!’反引用该参数

需要传递多个参数时,需要使用特殊参数“...”

上一篇下一篇

猜你喜欢

热点阅读