有没有一种方法可以动态/以编程方式生成具有不同列名称和/或不同条件数量的 dplyr
中的 case_when
条件?我有一个交互式脚本,正在尝试将其转换为函数。在 case_when
语句中有很多重复的代码,我想知道是否可以在不需要再次从头编写所有内容的情况下自动化处理。
这是一个虚拟数据集:
test_df = tibble(low_A=c(5, 15, NA),
low_TOT=c(NA, 10, NA),
low_B=c(20, 25, 30),
high_A=c(NA, NA, 10),
high_TOT=c(NA, 40, NA),
high_B=c(60, 20, NA))
expected_df = tibble(low_A=c(5, 15, NA),
low_TOT=c(NA, 10, NA),
low_B=c(20, 25, 30),
ans_low=c(5, 10, 30),
high_A=c(NA, NA, 10),
high_TOT=c(NA, 40, NA),
high_B=c(60, 20, NA),
ans_high=c(60, 40, 10))
> expected_df
# A tibble: 3 x 8
low_A low_TOT low_B ans_low high_A high_TOT high_B ans_high
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 5 NA 20 5 NA NA 60 60
2 15 10 25 10 NA 40 20 40
3 NA NA 30 30 10 NA NA 10
我需要的逻辑是,如果
._TOT
列有值,则使用该列。如果没有,则尝试使用._A
列,如果还没有,则使用 ._B
列。请注意,我故意没有将._TOT
列作为组的第一列。在这种情况下,我可以使用coalesce()函数,但是我希望有一个通用解决方案,不考虑列的顺序。当然,这一切都可以通过几个
case_when
语句轻松实现。我的问题是:
- 我正在尝试创建一个通用函数,因此不希望进行交互/整洁评估。
- 我有很多类似的列。所有这些列都以
_TOT,_A,_B
结尾,但具有不同的前缀(例如:low_TOT, low_A, low_B, high_TOT, high_A, high_B,......
),而我不想一遍又一遍地重写case_when
函数。
case_when
):def my_function = function(df) {
df %>% mutate(
# If a total low doesn't exist, use A (if exists) or B (if exists)
"ans_low" := case_when(
!is.na(.data[["low_TOT"]]) ~ .data[["low_TOT"]],
!is.na(.data[["low_A"]]) ~ .data[["low_A"]],
!is.na(.data[["low_B"]]) ~ .data[["low_B"]],
),
# If a total high doesn't exist, use A (if exists) or B (if exists)
"ans_high" := case_when(
!is.na(.data[["high_TOT"]]) ~ .data[["high_TOT"]],
!is.na(.data[["high_A"]]) ~ .data[["high_R"]],
!is.na(.data[["high_B"]]) ~ .data[["high_B"]],
# Plus a whole bunch of similar case_when functions...
}
我希望得到的是一种动态生成不同条件case_when
函数的方法,这样每次就不需要编写新的case_when
函数了。利用以下特点:
- 所有三个条件都具有相同的一般形式和变量名称结构,但具有不同的前缀(例如
high_
,low_
等)。 - 它们具有相同的公式格式为
!is.na(.data[[. ]])〜.data[[.]]
,其中点(.
)是列的动态生成名称。
我想要的是像下面这样的东西:
def my_function = function(df) {
df %>% mutate(
"ans_low" := some_func(prefix="Low"),
"ans_high" := some_func(prefix="High")
}
我试图创建自己的case_when
生成器以替换标准的case_when
,如下所示,但是出现了错误。我猜测这是因为.data
在tidyverse函数之外无法正常工作?
some_func = function(prefix) {
case_when(
!is.na(.data[[ sprintf("%s_TOT", prefix) ]]) ~ .data[[ sprintf("%s_TOT", prefix) ]],
!is.na(.data[[ sprintf("%s_A", prefix) ]]) ~ .data[[ sprintf("%s_A", prefix) ]],
!is.na(.data[[ sprintf("%s_B", prefix) ]]) ~ .data[[ sprintf("%s_B", prefix) ]]
)
}
我还想知道的是如何制作一个更加通用的case_when
生成器。到目前为止,仅列出列名称(前缀)发生了变化。如果我想要:
- 改变后缀的数量和名称(例如,
high_W, high_X, high_Y, high_Z, low_W, low_X, low_Y, low_Z, .......
),并将后缀字符向量作为some_func
的参数之一。 - 修改公式的形式。现在,所有条件的公式形式都是
!is.na(.data[[ . ]]) ~ .data[[ . ]]
,但如果我想将其作为some_func
的一个参数,该怎么做呢?例如,!is.na(.data[[ . ]]) ~ sprintf("%s is missing", .)
如果只能让它与不同的前缀配合使用,我会很高兴,但如果我能理解如何使用任意(但常见)后缀和任意公式实现更通用的功能,那就太棒了,这样我就可以使用some_func(prefix, suffixes, formula)
。
coalesce()
这样的函数可能更合适。 - MrFlickcoalesce()
可能是一个潜在的答案,但我更感兴趣的是动态生成条件(is.na
只是这里的一个特定示例,而coalesce
也需要特定的列顺序)。我真的很想了解如何更好地使用dplyr进行编程,并实现更高层次的抽象/通用性。 - anonymous1acoalese()
,但它仍然存在同样的主要问题:现在我必须编写一堆coalesce
语句。我想利用列组的共同前缀,这样我就不必编写10个不同的case_when
或coalese
语句了。 - anonymous1agsub("_(TOT|[A-Z]+)$", "", ...)
在colnames()
上的结果来确定它们如何拆分?这考虑了无限多个列后缀:*_TOT
、*_A
、*_B
、*_C
、……、*_Z
、*_AA
、*_AB
等等。然后对于这些拆分("low_"
和"high_"
),按后缀排序它们的colnames()
,由str_extract("_(TOT|[A-Z]+)$")
给出;显然,您必须将"_TOT"
重新排序为第一项。然后mutate(paste0("ans_", prefix) = coalesce(everything()))
,并将所有结果cbind()
或bind_cols()
在一起。 - Greg