【问题标题】:R mutate multiple columns with if statementR用if语句改变多列
【发布时间】:2018-06-18 17:29:01
【问题描述】:

我有这样的数据:

cols <- c("X01_01","X01_01_p", "X01_02","X01_02_p", "X01_03","X01_03_p", "X01_04", "X01_05","X01_06")
set.seed(111)
values <- replicate(9, sample(1:5, 4, replace = TRUE)) 
df <- as.data.frame(values)  

所以我的 df 看起来像这样:

    X01_01 X01_01_p X01_02 X01_02_p X01_03 X01_03_p X01_04 X01_05 X01_06
1      3        2      3        1      1        3      5      4      3
2      4        3      1        1      5        2      2      3      3
3      2        1      3        1      2        2      4      1      2
4      3        3      3        3      4        2      2      3      4

我有一些列用于突变(不是全部)和新列的名称。

cols_to_mutate <- c("X01_01_p","X01_02_p", "X01_03_p", "X01_04", "X01_05","X01_06")
new_cols <- c("X01_01_n","X01_02_n", "X01_03_n", "X01_04_n", "X01_05_n","X01_06_n")

每个突变都是一样的:

  • 如果值为 1 或 2,则新值必须为 0
  • 如果值为 3,则新值必须为 0.5
  • 如果值为 4 或 5,则新值必须为 1

我的 df 最终看起来像这样:

    X01_01 X01_01_p X01_02 X01_02_p X01_03 X01_03_p X01_04 X01_05 X01_06 X01_01_n X01_02_n X01_03_n X01_04_n X01_05_n X01_06_n
1      3        2      3        1      1        3      5      4      3      0.0      0.0      0.5        1      1.0      0.5
2      4        3      1        1      5        2      2      3      3      0.5      0.0      0.0        0      0.5      0.5
3      2        1      3        1      2        2      4      1      2      0.0      0.0      0.0        1      0.0      0.0
4      3        3      3        3      4        2      2      3      4      0.5      0.5      0.0        0      0.5      1.0

在“硬编码”中,我可以写很多这样的行:

df <- mutate(df, X01_01_n = ifelse(X01_01_p <= 2, 0, (ifelse(X01_01_p == 3, 0.5, 1))))
df <- mutate(df, X01_02_n = ifelse(X01_02_p <= 2, 0, (ifelse(X01_02_p == 3, 0.5, 1))))

当然,我正在寻找一种更花哨和更快的方法来做到这一点,但是我搜索了又搜索,但没有找到解决方案。我试过了:

df <- cbind(df,apply(df[,cols_to_mutate],2, function(x) if (x < 3) { 0} else if (x > 3) {1} else {.5}))

但这不起作用。任何想法都会很棒!

【问题讨论】:

    标签: r if-statement dplyr


    【解决方案1】:

    如果保留之前的列并在原地进行变异并不重要,则可以在用于变异的函数中使用mutate_at 和case_when。

    case_when 正在使用来自dplyr 的between 函数来设置条件,然后用~ 分配一个值。最后一个参数T ~ NA_real_ 将NA 分配给不符合任何条件的任何观察结果。

    library(tidyverse)
    
    cols_to_mutate <- c("X01_01_p","X01_02_p", "X01_03_p", "X01_04", "X01_05","X01_06")
    
    df %>%
      mutate_at(cols_to_mutate, function(x) {
        case_when(
          between(x, 1, 2) ~ 0,
          x == 3 ~ 0.5,
          between(x, 4, 5) ~ 1,
          T ~ NA_real_
        )
      })
    #>   X01_01 X01_01_p X01_02 X01_02_p X01_03 X01_03_p X01_04 X01_05 X01_06
    #> 1      3      0.0      3      0.0      1      0.5      1    1.0    0.5
    #> 2      4      0.5      1      0.0      5      0.0      0    0.5    0.5
    #> 3      2      0.0      3      0.0      2      0.0      1    0.0    0.0
    #> 4      3      0.5      3      0.5      4      0.0      0    0.5    1.0
    

    如果 有必要保留原始列并为重新调整的列赋予新名称,这里有一些 rlang + purrr 技巧。我所做的是imaped 在数据框的列上。如果名称在要变异的列列表中,我使用与上面相同的case_when,并输出带有两列的tibble:一是原始列,其名称使用quo_name和@987654336分配@ 运算符,另一个是新值列,名称相同但附加了_n。如果它不是要变异的列,它只会返回原始列的tibble。通过使用imap_dfc,将所有列重新绑定到一个数据帧中。

    df %>%
      imap_dfc(function(x, name) {
        if (name %in% cols_to_mutate) {
          new_vals <- case_when(
            between(x, 1, 2) ~ 0,
            x == 3 ~ 0.5,
            between(x, 4, 5) ~ 1,
            T ~ NA_real_
          )
          tibble(!!quo_name(name) := x, !!quo_name(paste0(name, "_n")) := new_vals)
        } else {
          tibble(!!quo_name(name) := x)
        }
      })
    #> # A tibble: 4 x 15
    #>   X01_01 X01_01_p X01_01_p_n X01_02 X01_02_p X01_02_p_n X01_03 X01_03_p
    #>    <int>    <int>      <dbl>  <int>    <int>      <dbl>  <int>    <int>
    #> 1      3        2        0        3        1        0        1        3
    #> 2      4        3        0.5      1        1        0        5        2
    #> 3      2        1        0        3        1        0        2        2
    #> 4      3        3        0.5      3        3        0.5      4        2
    #> # ... with 7 more variables: X01_03_p_n <dbl>, X01_04 <int>,
    #> #   X01_04_n <dbl>, X01_05 <int>, X01_05_n <dbl>, X01_06 <int>,
    #> #   X01_06_n <dbl>
    

    【讨论】:

      【解决方案2】:

      你可以做这样的事情,假设你的数字只取值 1 到 5。

      map_marlein <- function(x) {
        if (any(!x %in% 1:5)) {
          stop("Needs numbers from 1-5")
        }
        as.integer(cut(x, c(0,2,3, 10))) / 2 - 0.5
      }
      
      df[, paste0(names(df), "_n")] <- lapply(df[, names(df)], map_marlein)
      df
        X01_01 X01_01_p X01_02 X01_02_p X01_03 X01_03_p X01_04 X01_05 X01_06 X01_01_n X01_01_p_n X01_02_n X01_02_p_n X01_03_n X01_03_p_n X01_04_n X01_05_n X01_06_n
      1      3        2      3        1      1        3      5      4      3      0.5        0.0      0.5        0.0        0        0.5        1      1.0      0.5
      2      4        3      1        1      5        2      2      3      3      1.0        0.5      0.0        0.0        1        0.0        0      0.5      0.5
      3      2        1      3        1      2        2      4      1      2      0.0        0.0      0.5        0.0        0        0.0        1      0.0      0.0
      4      3        3      3        3      4        2      2      3      4      0.5        0.5      0.5        0.5        1        0.0        0      0.5      1.0
      

      【讨论】:

      • 感谢 Snoram 的帮助。但是您的解决方案不允许我选择要变异的列。而且我有一些不需要计算的列。使用您的解决方案,我的 df(比我的示例大得多)将包含太多列。但感谢您的分享和思考!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-06
      • 1970-01-01
      相关资源
      最近更新 更多