美文网首页js css html
R语言编程-Tidyverse 书籍-第二章(2)

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

作者: Hello育种 | 来源:发表于2022-10-07 01:30 被阅读0次

    修改列

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

    创建列——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() 中用的是公式形式,

    • 左边是返回TRUE 或FALSE 的表达式或函数
    • 右边是若左边表达式为TRUE,则重新编码的值,也可以是表达式或函数
    • 每个分支条件将从上到下的计算,并接受第一个TRUE 条件
    • 最后一个分支直接用TRUE 表示若其他条件都不为TRUE 时怎么做
    1. 更强大的重新编码函数 ——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() # 频率表
    

    筛选行

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

    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_*()
      该系列函数的共同参数:
    • 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. 删除行
      (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_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()

    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)*

    • .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风格的整洁函数,另一种做法是使用引用与反引用机制

    额外的两个步骤:

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

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

    相关文章

      网友评论

        本文标题:R语言编程-Tidyverse 书籍-第二章(2)

        本文链接:https://www.haomeiwen.com/subject/qsvsprtx.html