dplyr 创建一个具有其他列的复杂用户定义函数的新列
Posted
技术标签:
【中文标题】dplyr 创建一个具有其他列的复杂用户定义函数的新列【英文标题】:dplyr create a new column with a complex user defined function of other columns 【发布时间】:2021-02-28 09:37:57 【问题描述】:我有一个大型数据框,其中包含 40 个问题的回答(代表以下 3 个问题),并且需要计算一个新列,该列是这 40 个回答的复杂函数。由于几乎不可能在mutate
中写出函数,因此我尝试创建一个可以在mutate
中使用的函数f
df <- data.frame(Sex = c(rep("F", 5), rep("M", 5)),
Q1 = sample(0:10, 10, replace=T),
Q2 = sample(0:10, 10, replace=T),
Q3 = sample(0:10, 10, replace=T)
)
f <- function(q1, q2, q3)
y <- q1 + (q2^2) - (q3^3)
return(y)
现在使用mutate
创建一个新列可以正常工作。:
df %>%
mutate(newcol = f(Q1, Q2, Q3))
Sex Q1 Q2 Q3 newcol
1 F 10 6 3 19
2 F 0 9 9 -648
3 F 8 1 2 1
4 F 0 4 7 -327
5 F 6 4 1 21
6 M 8 3 3 -10
7 M 2 2 0 6
8 M 10 0 3 -17
9 M 6 9 3 60
10 M 1 7 2 42
也一样
df$newcol <- mapply(f, df$Q1, df$Q2, df$Q3)
但如果我在f
中包含一个简单的if
atatement,如下所示
f <- function(q1, q2, q3)
y <- q1 + (q2^2) - (q3^3)
if(y<0)
y <- -y
return(y)
我的手上立刻就有了灾难:
df %>%
+ mutate(newcol = f(Q1, Q2, Q3))
Sex Q1 Q2 Q3 newcol
1 F 10 6 3 19
2 F 0 9 9 -648
3 F 8 1 2 1
4 F 0 4 7 -327
5 F 6 4 1 21
6 M 8 3 3 -10
7 M 2 2 0 6
8 M 10 0 3 -17
9 M 6 9 3 60
10 M 1 7 2 42
Warning message:
Problem with `mutate()` input `newcol`.
i the condition has length > 1 and only the first element will be used
i Input `newcol` is `f(Q1, Q2, Q3)`.
然而,
df$newcol <- mapply(f, df$Q1, df$Q2, df$Q3)
df
Sex Q1 Q2 Q3 newcol
1 F 10 6 3 19
2 F 0 9 9 648
3 F 8 1 2 1
4 F 0 4 7 327
5 F 6 4 1 21
6 M 8 3 3 10
7 M 2 2 0 6
8 M 10 0 3 17
9 M 6 9 3 60
10 M 1 7 2 42
继续工作。 不幸的是,我的函数中有很多 if,并且有 40 个不同的参数要传递给函数,mapply 的输入变得巨大。如何使用预定义的向量将我的问题传递给 mapply,比如
questions <- c("df$Q1", "df$Q2", "df$Q3")
df$newcol <- mapply(f, questions)
密切相关:我如何定义一个有 40 个参数的函数而不使它跑出页面?
我完全有可能找错了树,如果是这样,我应该如何解决我的问题?
在此先感谢
托马斯·飞利浦
附:这是真正的标准
if(!is.na(df[i, "Q1_Daily_Mean"]) & df[i, "Q1_Daily_Mean"] >= THRESHOLD_MDD_GAD)
anxiety <- TRUE
if(!is.na(df[i, "Q2_Daily_Mean"]) & df[i, "Q2_Daily_Mean"] >= THRESHOLD_MDD_GAD)
worry <- TRUE
if(anxiety && worry)
anxiety_and_worry <- TRUE
if(!is.na(df[i, "Q3_Daily_Mean"]) & df[i, "Q3_Daily_Mean"] >= THRESHOLD_MDD_GAD )
agitation <- TRUE
if(!is.na(df[i, "Q10_Daily_Mean"]) & df[i, "Q10_Daily_Mean"] >= THRESHOLD_MDD_GAD )
anger <- TRUE
if(!is.na(df[i, "Q2_Weekly"]) & df[i, "Q2_Weekly"] >= THRESHOLD_MDD_GAD )
physical_fatigue <- TRUE
if(!is.na(df[i, "Q5_Weekly"]) & df[i, "Q5_Weekly"] >= THRESHOLD_MDD_GAD )
no_concentration <- TRUE
if(!is.na(df[i, "Q7_Weekly"]) & df[i, "Q7_Weekly"] >= THRESHOLD_MDD_GAD )
disturbed_sleep <- TRUE
if(!is.na(df[i, "Q13_Weekly"]) & !is.na(df[i, "Q14_Weekly"]) &
!is.na(df[i, "Q15_Weekly"]) & !is.na(df[i, "Q16_Weekly"]) &
!is.na(df[i, "Q17_Weekly"]) &
max( df[i, "Q13_Weekly"], df[i, "Q14_Weekly"],
df[i, "Q15_Weekly"], df[i, "Q16_Weekly"],
df[i, "Q17_Weekly"] ) >= THRESHOLD_MDD_GAD)
max_function <- TRUE
sum_of_symptoms_7 <- anxiety + worry + agitation + anger +
physical_fatigue + no_concentration + disturbed_sleep
if (anxiety_and_worry && (sum_of_symptoms_7 >= CRITERIA_NEEDED_GAD) && max_function)
# Generalized Anxiety Disorder
df[i, GAD_DESCRIPTPR_EML] <- TRUE
【问题讨论】:
y <- abs(y)
应该修复它或使用ifelse(y < 0, -y, y)
如果主要关心的是参数的数量(40 确实过多!)考虑tidying 你的数据:有两列,一列用于问题编号,一列用于响应,而不是每个问题一列.或者,您可以将条件作为命名列表传递,列表的名称标识新列名,列表的值给出表达式以评估以填充新列
【参考方案1】:
基本上带有if
语句的函数不是向量化的。你有两个选择。
-
使函数矢量化(使用
ifelse
或任何其他方式)并像之前一样继续使用mutate
。
library(dplyr)
library(purrr)
df %>% mutate(newcol = f(Q1, Q2, Q3))
-
如果条件太复杂并且您无法对函数进行矢量化,请使用
rowwise
或pmap
,它们一次只对一行进行操作。这与您的 mapply
尝试类似。
df %>% mutate(newcol = pmap_dbl(list(Q1, Q2, Q3), ~f(..1, ..2, ..3)))
【讨论】:
【参考方案2】:您收到“条件长度 > 1 且仅使用第一个元素”警告的原因是 if
与向量结合使用(例如,请参阅 here)。 dpylr
的 mutate
将值的“整个”向量传递给被调用的函数(即不是逐个元素的(行)元素)。这就是 if
声明被混淆的地方。
这解决了你的问题:
df <- data.frame(Sex = c(rep("F", 5), rep("M", 5)),
Q1 = sample(0:10, 10, replace=T),
Q2 = sample(0:10, 10, replace=T),
Q3 = sample(0:10, 10, replace=T)
)
f <- function(q1, q2, q3)
y <- q1 + (q2^2) - (q3^3)
y <- ifelse(y<0, -y, y)
return(y)
df %>%
mutate(newcol = f(Q1, Q2, Q3))
返回:
Sex Q1 Q2 Q3 newcol 1 F 8 6 3 17 2 F 6 0 0 6 3 F 4 5 7 314 4 F 9 5 7 309 5 F 3 5 9 701 6 M 1 10 5 24 7 M 10 5 4 29 8 M 4 0 3 23 9 M 8 4 7 319 10 M 3 6 3 12
【讨论】:
在这个非常简单的情况下它确实可以 - 谢谢。但一般情况有许多不同的条件以及它们的布尔组合。我一般如何解决这个问题? 您能否使用适合您遇到的问题的 MRE 来扩展您的问题?【参考方案3】:扩展我上面的评论:
f <- function(data, conditions)
columnNames <- names(conditions)
for (colName in columnNames)
qName <- enquo(colName)
data <- data %>% mutate(!!qName := eval(conditions[[colName]]))
data
df %>% f(list(bigQ1=expression(Q1 > 7), smallQ2=expression(Q2 < 2)))
给出,例如,
Sex Q1 Q2 Q3 bigQ1 smallQ2
1 F 2 9 9 FALSE FALSE
2 F 2 10 6 FALSE FALSE
3 F 9 4 9 TRUE FALSE
4 F 1 2 8 FALSE FALSE
5 F 5 10 2 FALSE FALSE
6 M 10 8 3 TRUE FALSE
7 M 4 8 0 FALSE FALSE
8 M 3 8 10 FALSE FALSE
9 M 5 2 6 FALSE FALSE
10 M 8 7 4 TRUE FALSE
将 df 作为函数的第一个参数传递允许管道。
【讨论】:
以上是关于dplyr 创建一个具有其他列的复杂用户定义函数的新列的主要内容,如果未能解决你的问题,请参考以下文章