【问题标题】:change multiple columns based on a condition and column name string根据条件和列名字符串更改多个列
【发布时间】:2023-04-05 19:29:01
【问题描述】:

我有一个非常稀疏的数据集 - 下面是格式的示例。我想根据下面解释的逻辑对特定列进行更改

# create dummy data set
pb=c('1','0','0','0','0','1','Not_ans','1','0','Not_ans')
qa=c('1','1','0','0','1','0','Not_ans','1','Not_ans','Not_ans')
#zy=c('1','Not_ans','0','1','Not_ans','0','1','1','1','Not_ans')

#sub questions for pb
pb.abr=c('1','0','0','0','0','1','0','1','0','0')
pb.ras=c('0','0','0','0','1','0','0','1','0','0')
pb.sfg=c('1','0','0','0','0','0','0','1','0','0')

#sub questions for qa
qa.fgs=c('1','0','0','0','0','0','0','1','0','0')
qa.sdf=c('0','1','0','0','0','0','0','0','0','0')
qa.tyu=c('0','0','0','0','1','0','0','1','0','0')

df=data.frame(pb,qa,pb.abr,pb.ras,pb.sfg,qa.fgs,qa.sdf,qa.tyu)
df

        pb      qa pb.abr pb.ras pb.sfg qa.fgs qa.sdf qa.tyu
1        1       1      1      0      1      1      0      0
2        0       1      0      0      0      0      1      0
3        0       0      0      0      0      0      0      0
4        0       0      0      0      0      0      0      0
5        0       1      0      1      0      0      0      1
6        1       0      1      0      0      0      0      0
7  Not_ans Not_ans      0      0      0      0      0      0
8        1       1      1      1      1      1      0      1
9        0 Not_ans      0      0      0      0      0      0
10 Not_ans Not_ans      0      0      0      0      0      0

pb 和 qa 两列称为基列,它们还有更多的子列,命名约定为 pb。和质量保证。 - 所以我们看到 pa 有 3 个子列, qa 有 3 个子列。我想根据基列(pa 或 qa)的条件对这些子列进行更改。

条件是如果列pb =='Not_ans' 然后使所有子列(pb.abr、pb.ras 和 pb.sfg)='Not_applicable'

我如何编写实现此目的的函数?我在其中指定基列名称,即pb,并在下面指定子列示例'pb.' - 会不会像下面这样,但它不会给出结果

data.frame(ifelse(df['base_q']=='Not_ans',
df[ , grepl( paste('base_q','.') , names(df) )]=='Not_applicable',df[,grepl( 
paste('base_q','.') , names(df)) ])

我如何编写一个将基列编号作为输入的通用函数,例如此处的 1,2 - 应用该函数,即无论 pb 是 Not_ans,它都会将 sub_columns (pb.abr,pb.ras,pb.sfg) 更改为不适用,然后移至第 2 列 (qa) 并应用相同的逻辑?

【问题讨论】:

    标签: r if-statement dplyr


    【解决方案1】:

    你可以用

    yf=function(df,v){
       df[df[v]=='Not_ans',][,names(df)[substr(names(df),1,nchar(v)+1)==paste0(v,'.')]]='Not_applicable'
       return(df)
     }
    yf(df,'pb')
            pb      qa         pb.abr         pb.ras         pb.sfg qa.fgs qa.sdf qa.tyu
    1        1       1              1              0              1      1      0      0
    2        0       1              0              0              0      0      1      0
    3        0       0              0              0              0      0      0      0
    4        0       0              0              0              0      0      0      0
    5        0       1              0              1              0      0      0      1
    6        1       0              1              0              0      0      0      0
    7  Not_ans Not_ans Not_applicable Not_applicable Not_applicable      0      0      0
    8        1       1              1              1              1      1      0      1
    9        0 Not_ans              0              0              0      0      0      0
    10 Not_ans Not_ans Not_applicable Not_applicable Not_applicable      0      0      0
    

    数据输入

    df=data.frame(pb,qa,pb.abr,pb.ras,pb.sfg,qa.fgs,qa.sdf,qa.tyu,stringsAsFactors = F) 
    # notice stringsAsFactors 
    

    【讨论】:

    • 您好上面的解决方案有效 - 但是我是否使用 lapply 来应用它的列数。就像在上面的数据框中一样-我显示了两个基本列,但在我的原始数据框中,我有 100 个基本问题列和 1000+s 个子问题列。命名约定如下 ('baseQ+".") 但并非全部。使用输入代替功能是否有意义 - 基本问题列号和子问题的命名逻辑
    • @Pb89 如果你知道你的基列,你可以应用它只需要修改带有 for 循环的函数
    【解决方案2】:

    一种方法如下。您可以在var 中指定要在mutate_at() 中应用一个(或多个函数)的列。这里我使用contains() 来指定列名。然后,我将 pb == "Not_ans" 时的列中的数值替换为 "Not_applicable"。

    mutate_at(df, 
              vars(contains("pb.")),
              .funs = funs(ifelse(pb == "Not_ans",
                                  "Not_applicable",
                                  .)))
    
    #        pb      qa         pb.abr         pb.ras         pb.sfg qa.fgs qa.sdf qa.tyu
    #1        1       1              2              1              2      1      0      0
    #2        0       1              1              1              1      0      1      0
    #3        0       0              1              1              1      0      0      0
    #4        0       0              1              1              1      0      0      0
    #5        0       1              1              2              1      0      0      1
    #6        1       0              2              1              1      0      0      0
    #7  Not_ans Not_ans Not_applicable Not_applicable Not_applicable      0      0      0
    #8        1       1              2              2              2      1      0      1
    #9        0 Not_ans              1              1              1      0      0      0
    #10 Not_ans Not_ans Not_applicable Not_applicable Not_applicable      0      0      0
    

    如果您想对pbqa 应用相同的任务,您可以使用两次mutate_at()

    mutate_at(df, 
              vars(contains("pb.")),
              .funs = funs(ifelse(pb == "Not_ans",
                                  "Not_applicable",
                                  .))) %>%
    mutate_at(vars(contains("qa.")),
              .funs = funs(ifelse(qa == "Not_ans", "Not_applicable",.)))
    

    【讨论】:

    • 再次,我是否使用lapply 将其应用于多个基本 q 列?
    • @Pb89 您不必使用lapply()。基本上,您将 if_else() 应用于所有以“pb”开头的列。
    • 不,我的意思是我必须运行 pb 的代码,然后再运行 qa。如何将此变异应用于第 1,2 列,最终更改 pb 和 qa 的子列
    • 然后,您可以再运行一次 mutate。在第二次,您更改列名。所以它会像mutate_at(vars(contains("qa.")), .funs = funs(ifelse(qa == "Not_ans", "Not_applicable",.)))
    • 就像我提到的,如果它是 2 列,就会这样做。但我有 100 多个列,例如 pb 和 qa。也不能一一适用
    【解决方案3】:

    根据@Wen-Ben 给出的答案 - 以下代码有效 -

    yf=function(df,v,y){ for(i in v:y) { df[df[i]=='Not_ans',][,names(df)[substr(names(df),1,nchar(colnames(df)[i])+1)==paste0(colnames(df)[i],'.')]]='Not_applicable' } return(df) }

    【讨论】:

      猜你喜欢
      • 2021-03-02
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-17
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多