gpt4 book ai didi

r - 如何为数据整理编写有效的包装器,允许在调用包装器时关闭任何包装的部分

转载 作者:行者123 更新时间:2023-12-03 16:01:01 26 4
gpt4 key购买 nike

为了简化数据整理,我编写了一个由处理数据的几个“动词函数”组成的包装函数。每个人对数据执行一项任务。但是,并非所有任务都适用于通过此过程的所有数据集,有时,对于某些数据,我可能想关闭一些“动词功能”,并跳过它们。
我试图了解是否有一种传统/规范的方法可以在 R 的包装器函数中构建这样的工作流。重要的是,一种高效的方法,既是性能方面的,也是简洁的代码。
例子
作为数据整理的一部分,我想执行几个步骤:

  • 清理列标题(使用 janitor::clean_names() )
  • 重新编码数据中的值,例如 TRUEFALSE替换为 10 (使用 gsub() )。
  • 将字符串值重新编码为小写(使用 tolower() )。
  • 基于特定的枢轴更宽 id列(使用 tidyr::pivot_wider)
  • 使用 NA 删除行值(使用 dplyr::drop_na() )

  • 玩具资料
    library(stringi)
    library(tidyr)

    set.seed(2021)

    # simulate data
    df <-
    data.frame(id = 1:20,
    isMale = rep(c("true", "false"), times = 10),
    WEIGHT = sample(50:100, 20),
    hash_Numb = stri_rand_strings(20, 5)) %>%
    cbind(., score = sample(200:800, size = 20))

    # sprinkle NAs randomly
    df[c("isMale", "WEIGHT", "hash_Numb", "score")] <-
    lapply(df[c("isMale", "WEIGHT", "hash_Numb", "score")], function(x) {
    x[sample(seq_along(x), 0.25 * length(x))] <- NA
    x
    })


    df <-
    df %>%
    tidyr::expand_grid(., Condition = c("A","B"))

    df
    #> # A tibble: 40 x 6
    #> id isMale WEIGHT hash_Numb score Condition
    #> <int> <chr> <int> <chr> <int> <chr>
    #> 1 1 <NA> 56 EvRAq NA A
    #> 2 1 <NA> 56 EvRAq NA B
    #> 3 2 false 87 <NA> 322 A
    #> 4 2 false 87 <NA> 322 B
    #> 5 3 true 95 13pXe 492 A
    #> 6 3 true 95 13pXe 492 B
    #> 7 4 <NA> 88 4WMBS 626 A
    #> 8 4 <NA> 88 4WMBS 626 B
    #> 9 5 true NA Nrl1W 396 A
    #> 10 5 true NA Nrl1W 396 B
    #> # ... with 30 more rows
    创建于 2021-03-03 由 reprex package (v0.3.0)

    数据显示了在两种条件下参加考试的 20 人的考试成绩。对于每个人,我们还知道性别 ( isMale )、以千克为单位的体重 ( WEIGHT ) 和唯一的 hash_number .
    数据清理和整理

    在将此数据发送到分析之前,需要根据我上面列出的特定步骤链对其进行清理。
    library(janitor)
    library(dplyr)

    # helper function
    convert_true_false_to_1_0 <- function(x) {

    first_pass <- gsub("^(?:TRUE)$", 1, x, ignore.case = TRUE)
    gsub("^(?:FALSE)$", 0, first_pass, ignore.case = TRUE)
    }

    # chain of steps
    df %>%
    janitor::clean_names() %>%
    mutate(across(everything(), convert_true_false_to_1_0)) %>%
    mutate(across(everything(), tolower)) %>%
    pivot_wider(names_from = condition, values_from = score) %>%
    drop_na()
    我的问题:如何将此过程包装在允许灵活关闭某些步骤的包装器中?

    我脑海中的一个想法是使用 %>%带有条件的管道,例如:
    my_wrangling_wrapper <- function(dat,
    clean_names = TRUE,
    convert_tf_to_1_0 = TRUE,
    convert_to_lower = TRUE,
    pivot_widr = TRUE,
    drp_na = TRUE){
    dat %>%
    {if (clean_names) janitor::clean_names(.) else .} %>%
    {if (convert_tf_to_1_0) mutate(., across(everything(), convert_true_false_to_1_0)) else .} %>%
    {if (convert_to_lower) mutate(., across(everything(), tolower)) else .} %>%
    {if (pivot_widr) pivot_wider(., names_from = condition, values_from = score) else .} %>%
    {if (drp_na) drop_na(.) else .}
    }
    这样,所有步骤都默认发生,除非关闭:
  • 用例 #1 -- 默认运行:

  • > my_wrangling_wrapper(dat = df)

    ## # A tibble: 6 x 6
    ## id is_male weight hash_numb a b
    ## <chr> <chr> <chr> <chr> <chr> <chr>
    ## 1 3 1 95 13pxe 492 492
    ## 2 9 1 54 hgzxp 519 519
    ## 3 12 0 72 vwetc 446 446
    ## 4 15 1 52 qadxc 501 501
    ## 5 17 1 71 g42vg 756 756
    ## 6 18 0 80 qiejd 712 712
  • 用例 #2 -- 不转换 true/false1/0并且不要掉落NA s:

  • > my_wrangling_wrapper(dat = df, convert_tf_to_1_0 = FALSE, drp_na = FALSE)

    ## # A tibble: 20 x 6
    ## id is_male weight hash_numb a b
    ## <chr> <chr> <chr> <chr> <chr> <chr>
    ## 1 1 NA 56 evraq NA NA
    ## 2 2 false 87 NA 322 322
    ## 3 3 true 95 13pxe 492 492
    ## 4 4 NA 88 4wmbs 626 626
    ## 5 5 true NA nrl1w 396 396
    ## 6 6 false NA 4oq74 386 386
    ## 7 7 true NA gg23f NA NA
    ## 8 8 false 94 NA NA NA
    ## 9 9 true 54 hgzxp 519 519
    ## 10 10 false 97 NA 371 371
    ## 11 11 true 90 NA 768 768
    ## 12 12 false 72 vwetc 446 446
    ## 13 13 NA NA jkhjh 338 338
    ## 14 14 false NA 0swem 778 778
    ## 15 15 true 52 qadxc 501 501
    ## 16 16 false 75 NA 219 219
    ## 17 17 true 71 g42vg 756 756
    ## 18 18 false 80 qiejd 712 712
    ## 19 19 NA 68 tadad NA NA
    ## 20 20 NA 53 iyw3o NA NA

    我的问题
    尽管我提出的解决方案确实有效,但我了解到在函数中不建议依赖管道运算符,因为它会减慢进程 ( see reference )。此外,由于 %>%不属于 base R ,必须有一种方法可以在没有管道的情况下实现相同的“可调整包装”功能。所以我想知道:有没有一种传统的方法来编写一个包装器函数,可以通过调整来关闭它的一些组件,而且总体上仍然保持性能高效?
    {值得一提的是 I've asked a similar question关于为 ggplot 构建包装器, 车削 geoms根据需要关闭。 The answer很好,但不适用于当前问题。}

    最佳答案

    您可以使用闭包来实现与@Waldi 答案中说明的功能序列相同的功能。就像是:

    #we build a wrapper generator providing an arbitrary number of functions to apply
    wrapperGenerator<-function(...) {
    flist<-list(...)
    function(data, conf = rep(TRUE, length(flist))) {
    if (!is.logical(conf) || (length(conf)!=length(flist)))
    stop("Wrong conf")
    for (i in seq_along(flist)) {
    if (conf[[i]])
    data<-flist[[i]](data)
    }
    data
    }
    }

    #An example for string manipulation
    wg<-wrapperGenerator(tolower, function(x) paste0(x,"_suff"), function(x) substring(x,1,5))

    #some usage
    require(stringi)
    set.seed(1)
    data<-stri_rand_strings(10,10)
    data
    #[1] "GNZuCtwed3" "CAgNlUizNm" "vDe7GN0NrL" "TbUBpfn6iP" "oemYWm1Tjg"
    #[6] "TrRF46JWfP" "uISKeFTl5s" "LqLKTtrOmx" "QiOKkCi7F8" "E3dsmnSPob"

    #Full pipeline
    wg(data)
    #[1] "gnzuc" "cagnl" "vde7g" "tbubp" "oemyw" "trrf4" "uiske" "lqlkt" "qiokk"
    #[10] "e3dsm"

    #Just the first two steps
    wg(data,c(TRUE,TRUE,FALSE))
    # [1] "gnzuctwed3_suff" "cagnluiznm_suff" "vde7gn0nrl_suff" "tbubpfn6ip_suff"
    # [5] "oemywm1tjg_suff" "trrf46jwfp_suff" "uiskeftl5s_suff" "lqlkttromx_suff"
    # [9] "qiokkci7f8_suff" "e3dsmnspob_suff"
    编辑
    添加一些关于上述工作方式的评论。 wrapperGenerator是一个返回函数的函数,它是通过提供您想要包装的函数来构建的。这里不需要数据。 wrapperGenerator的值本身是一个可以应用于实际数据的函数(示例中为 wg)。通过提供额外的 conf这个函数的参数,你告诉你要执行哪些步骤。
    闭包是 R 中非常强大的工具。 Here您发现必须阅读有关该主题的内容。

    关于r - 如何为数据整理编写有效的包装器,允许在调用包装器时关闭任何包装的部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66458188/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com