【问题标题】:several substitutions in one line R一行中的多个替换 R
【发布时间】:2015-08-23 19:07:17
【问题描述】:

我在 R 的数据框中有一列,其值为“-1”、“0”、“1”。我想分别用“no”、“maybe”和“yes”替换这些值。我将使用 sub 来做到这一点。

我可以写一个条件函数,然后代码:

    df[col] <- lapply(df[col], conditional_function_substitution)

我也可以一次替换一个(例如三个中的第一个):

   df[col] <- lapply(df[col], sub, pattern = '-1', replacement = "no")

我想知道它是否可以在一行中完成?比如:

   df[col] <- lapply(df[col], sub, pattern = c('-1','0','1'), replacement = c('no','maybe','yes')

感谢您的洞察!

【问题讨论】:

    标签: r string


    【解决方案1】:

    通过将 2 添加到 -1、0 和 1,您可以将索引放入所需结果的向量中:

    c("no", "maybe", "yes")[dat + 2]
    # [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"  
    

    相关选项可以利用match 函数来计算索引:

    c("no", "maybe", "yes")[match(dat, -1:1)]
    # [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"  
    

    或者,您可以使用命名向量进行重新编码:

    unname(c("-1"="no", "0"="maybe", "1"="yes")[as.character(dat)])
    # [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"   
    

    您也可以使用嵌套的ifelse

    ifelse(dat == -1, "no", ifelse(dat == 0, "maybe", "yes"))
    # [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"   
    

    如果您不介意加载新包,car 包中的 Recode 函数会这样做:

    library(car)
    Recode(dat, "-1='no'; 0='maybe'; 1='yes'")
    # [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"  
    

    数据

    dat <- c(-1, 1, 0, 1, 1, -1)
    

    请注意,如果dat 存储为字符串,则除第一个之外的所有操作都可以使用;首先你需要使用as.numeric(dat)

    如果代码清晰是您的主要目标,那么您应该选择您认为最容易理解的一个 - 我个人会选择第二个或最后一个,但这是个人偏好。

    如果您对代码速度感兴趣,那么您可以对解决方案进行基准测试。这是我提出的五个选项的基准,还包括目前作为其他答案发布的另外两个解决方案,以长度为 100k 的随机向量为基准:

    set.seed(144)
    dat <- sample(c(-1, 0, 1), replace=TRUE, 100000)
    opt1 <- function(dat) c("no", "maybe", "yes")[dat + 2]
    opt2 <- function(dat) c("no", "maybe", "yes")[match(dat, -1:1)]
    opt3 <- function(dat) unname(c("-1"="no", "0"="maybe", "1"="yes")[as.character(dat)])
    opt4 <- function(dat) ifelse(dat == -1, "no", ifelse(dat == 0, "maybe", "yes"))
    opt5 <- function(dat) Recode(dat, "-1='no'; 0='maybe'; 1='yes'")
    AnandaMahto <- function(dat) factor(dat, levels = c(-1, 0, 1), labels = c("no", "maybe", "yes"))
    hrbrmstr <- function(dat) sapply(as.character(dat), switch, `-1`="no", `0`="maybe", `1`="yes", USE.NAMES=FALSE)
    library(microbenchmark)
    microbenchmark(opt1(dat), opt2(dat), opt3(dat), opt4(dat), opt5(dat), AnandaMahto(dat), hrbrmstr(dat))
    # Unit: milliseconds
    #              expr        min         lq       mean     median         uq        max neval
    #         opt1(dat)   1.513500   2.553022   2.763685   2.656010   2.837673   4.384149   100
    #         opt2(dat)   2.153438   3.013502   3.251850   3.117058   3.269230   5.851234   100
    #         opt3(dat)  59.716271  61.890470  64.978685  62.509046  63.723048 144.708757   100
    #         opt4(dat)  62.934734  64.715815  71.181477  65.652195  71.123384 123.840577   100
    #         opt5(dat)  82.976441  84.849147  89.071808  85.752429  88.473162 155.347273   100
    #  AnandaMahto(dat)  57.267227  58.643889  60.508402  59.065642  60.368913  80.852157   100
    #     hrbrmstr(dat) 137.883307 148.626496 158.051220 153.441243 162.594752 228.271336   100
    

    前两个选项似乎比任何其他选项都快一个数量级以上,尽管向量必须非常大,或者您需要对任何一个重复操作多次这会有所作为。

    正如@AnandaMahto 所指出的,如果我们输入字符而不是数字输入,这些结果在性质上会有所不同:

    set.seed(144)
    dat <- sample(c("-1", "0", "1"), replace=TRUE, 100000)
    opt1 <- function(dat) c("no", "maybe", "yes")[as.numeric(dat) + 2]
    opt2 <- function(dat) c("no", "maybe", "yes")[match(dat, -1:1)]
    opt3 <- function(dat) unname(c("-1"="no", "0"="maybe", "1"="yes")[as.character(dat)])
    opt4 <- function(dat) ifelse(dat == -1, "no", ifelse(dat == 0, "maybe", "yes"))
    opt5 <- function(dat) Recode(dat, "-1='no'; 0='maybe'; 1='yes'")
    AnandaMahto <- function(dat) factor(dat, levels = c(-1, 0, 1), labels = c("no", "maybe", "yes"))
    hrbrmstr <- function(dat) sapply(dat, switch, `-1`="no", `0`="maybe", `1`="yes", USE.NAMES=FALSE)
    library(microbenchmark)
    microbenchmark(opt1(dat), opt2(dat), opt3(dat), opt4(dat), opt5(dat), AnandaMahto(dat), hrbrmstr(dat))
    # Unit: milliseconds
    #              expr       min        lq       mean     median         uq        max neval
    #         opt1(dat)  8.397194  9.519075  10.784108   9.693706  10.163203   55.78417   100
    #         opt2(dat)  2.281438  3.091418   4.231162   3.210794   3.436038   49.39879   100
    #         opt3(dat)  3.606863  5.481115   6.466393   5.720282   6.344651   48.47924   100
    #         opt4(dat) 66.819638 69.996704  74.596960  71.290522  73.404043  127.52415   100
    #         opt5(dat) 32.897019 35.701401  38.488489  36.336489  38.950272   88.20915   100
    #  AnandaMahto(dat)  1.329443  2.114504   2.824306   2.275736   2.493907   46.19333   100
    #     hrbrmstr(dat) 81.898572 91.043729 154.331766 100.006203 141.425717 1594.17447   100
    

    现在,@AnandaMahto 提出的factor 解决方案是最快的,其次是使用match 的向量索引和命名向量查找。同样,所有运行时都足够快,以至于您需要一个大向量或多次运行才能使这些都起作用。

    【讨论】:

    • @AnandaMahto 已更新。由于 OP 没有表示对速度有任何特别的兴趣,我认为他们可能应该只选择他们最清楚的一个。我已经包含了一个基准,以防他们真的关心速度。
    • 我一方面考虑速度,另一方面考虑可读性和扩展的容易程度。有趣的是,当输入在数字和字符之间更改时,速度有多么不同。
    【解决方案2】:

    factor 通常用于此类任务,并导致一些非常容易阅读的代码:

    vec <- c(0, 1, -1, -1, 1, 0)
    vec
    # [1]  0  1 -1 -1  1  0
    
    factor(vec, levels = c(-1, 0, 1), labels = c("no", "maybe", "yes"))
    # [1] maybe yes   no    no    yes   maybe
    # Levels: no maybe yes
    

    如果您只想要字符输出,请将其包装在 as.character 中。


    如果列值已经是字符串,则只需修改factor 中的levels 参数以使用as.character

    vec2 <- as.character(c(0, 1, -1, -1, 1, 0))
    vec2
    # [1] "0"  "1"  "-1" "-1" "1"  "0" 
    
    factor(vec2, levels = as.character(c(-1, 0, 1)), labels = c("no", "maybe", "yes"))
    # [1] maybe yes   no    no    yes   maybe
    # Levels: no maybe yes
    

    【讨论】:

      【解决方案3】:

      这也可能是switch 的恶意应用程序:

      set.seed(1492)
      thing <- sample(c(-1, 0, 1), 100, replace=TRUE)
      sapply(as.character(thing), switch, `-1`="no", `0`="maybe", `1`="yes", USE.NAMES=FALSE))
      

      如果它们实际上已经是字符,则可以省略 as.character() 位。

      注意:我不一定推荐这个,只是展示所有可能的方法(这更像是一种摆脱ifelse 段落曲折迷宫的方法)。

      IMO factors 是要走的路。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 2021-01-05
        • 2019-01-04
        • 1970-01-01
        • 1970-01-01
        • 2019-02-14
        • 1970-01-01
        相关资源
        最近更新 更多