【问题标题】:Generate multiple columns of variables with `dplyr` and function in a vectorized manner使用“dplyr”生成多列变量,并以矢量化方式运行
【发布时间】:2020-03-29 15:52:11
【问题描述】:

假设收集了几十个科目的问卷,我想检查每个问题的答案是否正确。为了实现这种逐列操作,我使用了mapply 函数,其中向量化的对分别是列(多个变量)和向量(多个元素),有关详细信息,请参见下面的演示代码。但是,使用这种方法,我需要手动生成变量以将其合并到原始数据框中,因此,我想知道是否有另一种方法可以使用mutate函数的多元版本实现类似的效果在 dplyr 包中?这样就不需要手动生成变量的名称,并以向量化的方式实现。

##simulated data generation with four questions
IDs<-paste0('subj',0,1:7)
Q1<-c('A','B','C','A','A','A','D')
Q2<-c('A','B','B','B','B','D','C')
Q3<-c('B','B','C','C','C','D','C')
Q4<-c('A','D','D','B','D','D','C')
vect<-data.frame(cbind(IDs,Q1,Q2,Q3,Q4))

##answer for the four questions
answer<-c('A','B','C','D')

##method with mapply function
myfunc<-function(vec,value) {
  ifelse(vec==value, TRUE, FALSE)
}

newvariable<-mapply(myfunc, vect[,2:5], answer)

colnames(newvariable)<-paste0('Q',1:4,"_ans")
newvariable
vect_new1<-cbind(vect,newvariable)
vect_new1

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    使用apply的基本方式:

    cbind(vect, `colnames<-`(t(apply(vect[-1], 1, `==`, answer)), paste0("Q", 1:4, "_Ans")))
    

    或者使用map2:

    library(tidyverse)
    
    vect %>%
      select(-IDs) %>%
      map2_dfc(answer, `==`) %>%
      set_names(~ str_c(., "_Ans")) %>%
      bind_cols(vect, .)
    

    或者

    vect %>%
      pivot_longer(Q1:Q4) %>%
      mutate(Ans = value == answer) %>%
      pivot_wider(values_from = c(value, Ans))
    

    输出

    #      IDs Q1 Q2 Q3 Q4 Q1_Ans Q2_Ans Q3_Ans Q4_Ans
    # 1 subj01  A  A  B  A   TRUE  FALSE  FALSE  FALSE
    # 2 subj02  B  B  B  D  FALSE   TRUE  FALSE   TRUE
    # 3 subj03  C  B  C  D  FALSE   TRUE   TRUE   TRUE
    # 4 subj04  A  B  C  B   TRUE   TRUE   TRUE  FALSE
    # 5 subj05  A  B  C  D   TRUE   TRUE   TRUE   TRUE
    # 6 subj06  A  D  D  D   TRUE  FALSE  FALSE   TRUE
    # 7 subj07  D  C  C  C  FALSE  FALSE   TRUE  FALSE
    

    【讨论】:

      【解决方案2】:

      一个选项可能是:

      bind_cols(vect, vect %>%
                 do(data.frame(sweep(select(., -1), 2, FUN = `==`, c("A", "B", "C", "D")))) %>%
                 rename_all(~ paste0(., "_ans")))
      
           IDs Q1 Q2 Q3 Q4 Q1_ans Q2_ans Q3_ans Q4_ans
      1 subj01  A  A  B  A   TRUE  FALSE  FALSE  FALSE
      2 subj02  B  B  B  D  FALSE   TRUE  FALSE   TRUE
      3 subj03  C  B  C  D  FALSE   TRUE   TRUE   TRUE
      4 subj04  A  B  C  B   TRUE   TRUE   TRUE  FALSE
      5 subj05  A  B  C  D   TRUE   TRUE   TRUE   TRUE
      6 subj06  A  D  D  D   TRUE  FALSE  FALSE   TRUE
      7 subj07  D  C  C  C  FALSE  FALSE   TRUE  FALSE
      

      【讨论】:

        【解决方案3】:

        您的问题与标准评估问题有关。这对于dplyr (see a blog post I wrote yesterday) 来说可能相当棘手。

        使用dplyr 会比较困难,因为它不太适合字符串中的列名。使用data.table 会更容易解决您的问题

        首先,将您的对象转换为data.table

        library(data.table)
        data.table::setDT(vect)
        

        然后,您只需要遍历您的答案向量(假设它们的顺序很好!)并使用data.table 条件替换:=

        output <- lapply(seq_len(length(answer)), function(ans) {
        
          vect[,c(paste0("Q",ans,"_ans")) := FALSE]
          vect[get(paste0("Q",ans)) == answer[ans], c(paste0("Q",ans,"_ans")) := TRUE]
        } )
        output <- output[[length(output)]]
        

        最后一行是因为您反复迭代同一数据集但返回最后一个。您只对最终迭代的输出感兴趣。

        结果是:

        output
        # IDs Q1 Q2 Q3 Q4 Q1_ans Q2_ans Q3_ans Q4_ans
        # 1: subj01  A  A  B  A   TRUE  FALSE  FALSE  FALSE
        # 2: subj02  B  B  B  D  FALSE   TRUE  FALSE   TRUE
        # 3: subj03  C  B  C  D  FALSE   TRUE   TRUE   TRUE
        # 4: subj04  A  B  C  B   TRUE   TRUE   TRUE  FALSE
        # 5: subj05  A  B  C  D   TRUE   TRUE   TRUE   TRUE
        # 6: subj06  A  D  D  D   TRUE  FALSE  FALSE   TRUE
        # 7: subj07  D  C  C  C  FALSE  FALSE   TRUE  FALSE
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-06-05
          • 2019-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多