【问题标题】:How to make a function in R to recode a variable into new binary columns? (with ifelse statement) [duplicate]如何在 R 中创建一个函数以将变量重新编码为新的二进制列? (带有 ifelse 语句)[重复]
【发布时间】:2021-10-17 21:52:10
【问题描述】:

我想创建一个函数,使用ifelse 将变量中的值重新编码为二进制 0 和 1。假设我有这个数据集:

df <- data.frame(
        id = 1:10,
        region = rep(c("Asia", "Africa", "Europe", "America"), length = 10)
        )

这是我想要的输出:

但是,我想使用function 创建这些列,所以我只需在函数中输入数据和变量。这是据我所知:

binary <- function(data2, var, value){
        for(i in 1:nrow(data2)){        
            val <- ifelse(data2[data2[var] == value, 1, 0)
            data2 <- cbind(data2, val)
            }
        }

有谁知道如何在 R 中的 for 循环和 function 中使用 ifelse 函数?任何帮助深表感谢。谢谢。

【问题讨论】:

    标签: r function for-loop if-statement


    【解决方案1】:

    重塑

    这样做似乎有点低效;它似乎只是一个旋转/重塑操作,所以这是一个一次性的交易:

    df2 <- reshape2::dcast(df, id + region ~ region, value.var = "region")
    df2[,unique(df2$region)] <- lapply(df2[,unique(df2$region)], function(z) +!is.na(z))
    df2
    #    id  region Africa America Asia Europe
    # 1   1    Asia      0       0    1      0
    # 2   2  Africa      1       0    0      0
    # 3   3  Europe      0       0    0      1
    # 4   4 America      0       1    0      0
    # 5   5    Asia      0       0    1      0
    # 6   6  Africa      1       0    0      0
    # 7   7  Europe      0       0    0      1
    # 8   8 America      0       1    0      0
    # 9   9    Asia      0       0    1      0
    # 10 10  Africa      1       0    0      0
    

    dcast 枢轴(同时保留原始"region" 列);中间值(在dcast 之后)是

    reshape2::dcast(df, id+region~region, value.var="region")
    #    id  region Africa America Asia Europe
    # 1   1    Asia   <NA>    <NA> Asia   <NA>
    # 2   2  Africa Africa    <NA> <NA>   <NA>
    # 3   3  Europe   <NA>    <NA> <NA> Europe
    # 4   4 America   <NA> America <NA>   <NA>
    # 5   5    Asia   <NA>    <NA> Asia   <NA>
    # 6   6  Africa Africa    <NA> <NA>   <NA>
    # 7   7  Europe   <NA>    <NA> <NA> Europe
    # 8   8 America   <NA> America <NA>   <NA>
    # 9   9    Asia   <NA>    <NA> Asia   <NA>
    # 10 10  Africa Africa    <NA> <NA>   <NA>
    

    所以我们需要做的就是将它们从字符串/NAs 转换为“是或不是NA”,这是使用+!is.na(z) 完成的。

    基础 R,不重塑

    uniqregion <- unique(df$region)
    tmp <- +outer(df$region, unique(df$region), `==`)
    colnames(tmp) <- uniqregion
    tmp
    #       Asia Africa Europe America
    #  [1,]    1      0      0       0
    #  [2,]    0      1      0       0
    #  [3,]    0      0      1       0
    #  [4,]    0      0      0       1
    #  [5,]    1      0      0       0
    #  [6,]    0      1      0       0
    #  [7,]    0      0      1       0
    #  [8,]    0      0      0       1
    #  [9,]    1      0      0       0
    # [10,]    0      1      0       0
    cbind(df, tmp)
    #    id  region Asia Africa Europe America
    # 1   1    Asia    1      0      0       0
    # 2   2  Africa    0      1      0       0
    # 3   3  Europe    0      0      1       0
    # 4   4 America    0      0      0       1
    # 5   5    Asia    1      0      0       0
    # 6   6  Africa    0      1      0       0
    # 7   7  Europe    0      0      1       0
    # 8   8 America    0      0      0       1
    # 9   9    Asia    1      0      0       0
    # 10 10  Africa    0      1      0       0
    

    文字函数

    如果你真的想要一个函数来循环它,我仍然推荐 lapply 而不是 for 循环:

    binary <- function(data2, variable) {
      uniq <- unique(data2[[variable]])
      cbind(data2, as.data.frame(
        lapply(setNames(nm = uniq),
               function(z) +(z == data2[[variable]]) )
      ))
    }
    binary(df, "region")
    #    id  region Asia Africa Europe America
    # 1   1    Asia    1      0      0       0
    # 2   2  Africa    0      1      0       0
    # 3   3  Europe    0      0      1       0
    # 4   4 America    0      0      0       1
    # 5   5    Asia    1      0      0       0
    # 6   6  Africa    0      1      0       0
    # 7   7  Europe    0      0      1       0
    # 8   8 America    0      0      0       1
    # 9   9    Asia    1      0      0       0
    # 10 10  Africa    0      1      0       0
    

    (您可能会在此处考虑 不是 cbind(data2,,而只是返回 Asia:America 列,允许调用函数(用户)确定如何处理它;也许这太强迫症/泛化. 只是一个想法。)

    使用for循环的文字函数

    但如果你真的必须拥有它...

    binary2 <- function(data2, variable) {
      uniq <- unique(data2[[variable]])
      for (nm in uniq) {
        data2[[nm]] <- +(data2[[variable]] == nm)
      }
      data2
    }
    binary2(df, "region")
    #    id  region Asia Africa Europe America
    # 1   1    Asia    1      0      0       0
    # 2   2  Africa    0      1      0       0
    # 3   3  Europe    0      0      1       0
    # 4   4 America    0      0      0       1
    # 5   5    Asia    1      0      0       0
    # 6   6  Africa    0      1      0       0
    # 7   7  Europe    0      0      1       0
    # 8   8 America    0      0      0       1
    # 9   9    Asia    1      0      0       0
    # 10 10  Africa    0      1      0       0
    

    【讨论】:

      【解决方案2】:

      我在这里学到的最好的事情是将由TRUEFALSE 组成的矩阵乘以1,你会得到1's and 0's -> 太棒了:

      df %>% cbind(model.matrix(~ region + 0, .)*1)
      

      输出:

         id  region regionAfrica regionAmerica regionAsia regionEurope
      1   1    Asia            0             0          1            0
      2   2  Africa            1             0          0            0
      3   3  Europe            0             0          0            1
      4   4 America            0             1          0            0
      5   5    Asia            0             0          1            0
      6   6  Africa            1             0          0            0
      7   7  Europe            0             0          0            1
      8   8 America            0             1          0            0
      9   9    Asia            0             0          1            0
      10 10  Africa            1             0          0            0
      

      我们可以在管道框架中使用cbindsapply

      df %>% 
          mutate(region = factor(region)) %>% 
          cbind(sapply(levels(.$region), `==`, .$region)*1) 
      

      也是这样的:

      library(dplyr)
      df %>% 
          mutate(region = factor(region)) %>% 
          cbind(sapply(levels(.$region), `==`, .$region)) %>% 
          mutate(across(Africa:Europe,  ~case_when(. == TRUE ~ 1,
                                                   TRUE ~ 0)))
      
      
         id  region Africa America Asia Europe
      1   1    Asia      0       0    1      0
      2   2  Africa      1       0    0      0
      3   3  Europe      0       0    0      1
      4   4 America      0       1    0      0
      5   5    Asia      0       0    1      0
      6   6  Africa      1       0    0      0
      7   7  Europe      0       0    0      1
      8   8 America      0       1    0      0
      9   9    Asia      0       0    1      0
      10 10  Africa      1       0    0      0
      

      或函数:

      expand_factor <- function(f) {
          m <- matrix(0, length(f), nlevels(f), dimnames = list(NULL, levels(f)))
          replace(m, cbind(seq_along(f), f), 1)
      }
      df %>% 
          mutate(region = factor(region)) %>% 
          cbind(expand_factor(.$region)*1)
      
      

      【讨论】:

      • model.matrix的创新使用加倍!
      • 非常感谢 r2evans。我很感激!!!
      • (不过很有趣,我不需要*1
      【解决方案3】:

      通常最好在 R 中使用矢量化函数而不是循环。例如,您可以使用 dplyr 中的case_when 来做同样的事情,而不是编写带有循环的自定义函数:

      library(tidyverse)
      
      df %>%
        mutate(
          Asia = case_when(region == "Asia" ~ 1, TRUE ~ 0),
          Africa = case_when(region == "Africa" ~ 1, TRUE ~ 0),
          Europe = case_when(region == "Europe" ~ 1, TRUE ~ 0),
          America = case_when(region == "America" ~ 1, TRUE ~ 0)
        )
      

      或者,更简单的版本(感谢 MartinGal):

      df %>%
        mutate(Asia = +(region == "Asia"),
               Africa = +(region == "Africa"),
               Europe = +(region == "Europe"),
               America = +(region == "America"))
      

      【讨论】:

      • 将其简化为例如Asia = +(region == "Asia")
      • @MartinGal 我直到现在才知道这个措辞 - 谢谢!
      猜你喜欢
      • 1970-01-01
      • 2013-07-07
      • 1970-01-01
      • 1970-01-01
      • 2020-11-12
      • 2017-01-07
      • 1970-01-01
      • 2020-10-27
      • 1970-01-01
      相关资源
      最近更新 更多