Emm*_*man 5 r function wrapper user-defined-functions data-wrangling
为了简化数据整理,我编写了一个由处理数据的几个“动词函数”组成的包装函数。每个人对数据执行一项任务。但是,并非所有任务都适用于通过此过程的所有数据集,有时,对于某些数据,我可能想关闭一些“动词功能”,并跳过它们。
我试图了解是否有一种传统/规范的方式来在 R 的包装器函数中构建这样的工作流。重要的是,一种高效的方式,无论是性能方面还是简洁的代码。
作为数据整理的一部分,我想执行几个步骤:
janitor::clean_names())TRUE和FALSE被替换为1和0(使用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
Run Code Online (Sandbox Code Playgroud)
由reprex 包(v0.3.0)于 2021-03-03 创建
数据显示了在两种条件下进行测试的 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()
Run Code Online (Sandbox Code Playgroud)
我的问题:如何将此过程包装在允许灵活关闭某些步骤的包装器中?
我脑海中的一个想法是使用%>%带有条件的管道,例如:
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 .}
}
Run Code Online (Sandbox Code Playgroud)
这样,所有步骤都默认发生,除非关闭:
> 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
Run Code Online (Sandbox Code Playgroud)
true/转换false为1/0并且不要删除NAs:> 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
Run Code Online (Sandbox Code Playgroud)
尽管我提出的解决方案确实有效,但我了解到在函数中不建议依赖管道运算符,因为它会减慢进程(请参阅参考资料)。此外,由于%>%不是 的一部分base R,因此必须有一种方法可以在没有管道的情况下实现相同的“可调整包装”功能。所以我想知道:有没有一种传统的方法来编写一个包装器函数,可以通过调整来关闭它的一些组件,而且总体上仍然保持性能高效?
{值得一提的是,我已经问过一个关于为 构建包装器的类似问题ggplot,geoms根据需要关闭。答案很好,但不适用于当前问题。}
继续使用%>%,您可以创建一个功能序列:
library(magrittr)
my_wrangling_wrapper =
. %>%
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()
Run Code Online (Sandbox Code Playgroud)
由于此序列的行为类似于列表,因此您可以通过选择元素来决定要使用哪些步骤:
clean_names = TRUE
convert_tf_to_1_0 = TRUE
convert_to_lower = FALSE
pivot_widr = FALSE
drp_na = TRUE
my_wrangling_wrapper[c(clean_names,
convert_tf_to_1_0,
convert_to_lower,
pivot_widr,
drp_na)]
#Functional sequence with the following components:
#
# 1. janitor::clean_names(.)
# 2. mutate(., across(everything(), convert_true_false_to_1_0))
# 3. drop_na(.)
df %>% my_wrangling_wrapper[c(clean_names,
convert_tf_to_1_0,
convert_to_lower,
pivot_widr,
drp_na)]()
# id is_male weight hash_numb score
#1 1 1 51 Zm1Xx 343
#2 3 1 99 Xc2rm 703
#3 6 0 62 2r2cP 243
#4 12 0 84 llI0f 297
#5 16 0 72 AO76M 475
#6 18 0 63 zGJmW 376
Run Code Online (Sandbox Code Playgroud)
如果没有%>%,您可以使用等效的freduce解决方案:
clean_names <- function(x) janitor::clean_names(x,dat)
convert_tf_to_1_0 <- function(x) mutate(x,dat, across(everything(),
convert_true_false_to_1_0))
convert_to_lower <- function(x) mutate(x,dat, across(everything(), tolower))
pivot_widr <- function(x) pivot_wider(x,dat, names_from = condition,
values_from = score)
drp_na <- function(x) drop_na(x, dat)
my_wrangling_list <- list(clean_names, convert_tf_to_1_0, drp_na)
magrittr::freduce(df, my_wrangling_list)
Run Code Online (Sandbox Code Playgroud)
或者与%>%和freduce:
df %>% freduce(my_wrangling_list)
Run Code Online (Sandbox Code Playgroud)
我不会太担心管道开销,请参阅您引用的链接中的答案:在比较毫秒时,管道会产生影响,但当涉及更大的计算时,管道开销可以忽略不计。
| 归档时间: |
|
| 查看次数: |
208 次 |
| 最近记录: |