【问题标题】:dplyr create a new column with a complex user defined function of other columnsdplyr 创建一个具有其他列的复杂用户定义函数的新列
【发布时间】: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 &lt;- abs(y) 应该修复它或使用ifelse(y &lt; 0, -y, y)
  • 如果主要关心的是参数的数量(40 确实过多!)考虑tidying 你的数据:有两列,一列用于问题编号,一列用于响应,而不是每个问题一列.或者,您可以将条件作为命名列表传递,列表的名称标识新列名,列表的值给出表达式以评估以填充新列

标签: r dplyr user-defined-functions


【解决方案1】:

基本上带有if 语句的函数不是向量化的。你有两个选择。

  1. 使函数矢量化(使用ifelse 或任何其他方式)并像之前一样继续使用mutate
library(dplyr)
library(purrr)

df %>% mutate(newcol = f(Q1, Q2, Q3))
  1. 如果条件太复杂并且您无法对函数进行矢量化,请使用rowwisepmap,它们一次只对一行进行操作。这与您的 mapply 尝试类似。
df %>% mutate(newcol = pmap_dbl(list(Q1, Q2, Q3), ~f(..1, ..2, ..3)))

【讨论】:

    【解决方案2】:

    您收到“条件长度 > 1 且仅使用第一个元素”警告的原因是 if 与向量结合使用(例如,请参阅 here)。 dpylrmutate 将值的“整个”向量传递给被调用的函数(即不是逐个元素的(行)元素)。这就是 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 作为函数的第一个参数传递允许管道。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-27
      • 2022-01-23
      • 1970-01-01
      • 2021-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多