【问题标题】:Nested if-else loops in RR中的嵌套if-else循环
【发布时间】:2017-12-04 00:20:32
【问题描述】:

我有一个名为“crimes”的数据框,其中包含一个“pre_rate”列,表示某项法律实施之前的犯罪率。我想使用嵌套的 if-else 循环将每个速率放在“rate_category”列中。我有以下代码:

crimes$rate_category = 
  with(crimes, ifelse(pre_rate > 0.26 && pre_rate < 0.87, 1,
    ifelse(pre_rate > 1.04 && pre_rate < 1.94, 2, 
      ifelse(pre_rate > 2.03 && pre_rate < 2.96, 3, 
        ifelse(pre_rate > 3.10 && pre_rate < 3.82, 4, 
          ifelse(pre_rate > 4.20 && pre_rate < 11.00, 5, "NA"))))))
crimes

这是一个可重现的例子:

pre_rate = c(0.27, 1.91, 2.81, 3.21, 4.80) 
crimes = data.frame(pre_rate)   
crimes

但是,当我使用原始数据框运行循环时,“rate_category”列中的所有级别都错误地设置为 1。上面的循环似乎有什么问题?

【问题讨论】:

  • 小心使用&amp;&amp;:它只比较向量的第一个元素。 &amp; 进行元素比较,这可能是您想要的。
  • 另外我认为在你的最终ifelse 中有一个字符"NA" 会通过将其转换为字符来搞乱列。只需使用NA,不要加引号。
  • 你的目标真的是有差距吗?例如,您是否希望 2 的 pre-rate 为 NA?
  • @Hugh 所有的比率都是小数点后 8 位,所以我只是将它们四舍五入到百分之一。会有差距,但不会包含任何相关数据。

标签: r if-statement


【解决方案1】:

我建议不要使用嵌套 ifelse 语句,而是使用 case_when。它更容易阅读/遵循。但是正如@Marius 提到的那样,您的问题是&amp;&amp; 而不是使用&amp;

library(tidyverse)
crimes <- data.frame(pre_rate = c(0.27, 1.91, 2.81, 3.21, 4.80))

crimes %>% 
  mutate(rate_category = case_when(pre_rate > 0.26 & pre_rate < 0.87 ~ 1,
                                   pre_rate > 1.04 & pre_rate < 1.94 ~ 2,
                                   pre_rate > 2.03 & pre_rate < 2.96 ~ 3,
                                   pre_rate > 3.10 & pre_rate < 3.82 ~ 4,
                                   pre_rate > 4.20 & pre_rate < 11.00 ~ 5))

【讨论】:

  • between() 在这里可能有用。
  • @Hugh 根据帮助页面,?dplyr::between x &gt;= left &amp; x &lt;= right 的快捷方式,但 OP 要求 strict 不等式。 between()data.table 实现有一个参数来强制执行严格的不等式,例如,data.table::between(x, lower, upper, incbounds = FALSE)
  • 正确。然而,从 OP 的评论来看,这种区别似乎并不重要。
【解决方案2】:

为什么不在两个向量中定义下界和上界,然后依靠索引?使用这种方法,就不用多次写pre_rate &gt; num1 &amp; pre_rate &lt; num2了。

lowB <- c(0.26, 1.04, 2.03, 3.10, 4.2)
uppB <- c(0.87, 1.94, 2.96, 3.82, 11)

myCategory <- 1:5 ## this can be whatever categories you'd like

crimes$rate_category <- with(crimes, myCategory[pre_rate > lowB & pre_rate < uppB])

【讨论】:

    【解决方案3】:

    可以使用 non-equi joinupdate on join 代替多个嵌套的 ifelse()

    # OP's sample data set with one out-of-bounds value appended
    crimes = data.frame(pre_rate = c(0.27, 1.91, 2.81, 3.21, 4.80, 1.0))   
    
    library(data.table)
    # specify categories, lower, and upper bounds
    bounds <- data.table(
      cat = 1:5,
      lower = c(0.26, 1.04, 2.03, 3.10, 4.2),
      upper = c(0.87, 1.94, 2.96, 3.82, 11)
    )
    # non-equi join and update on join
    setDT(crimes)[bounds, on = .(pre_rate > lower, pre_rate < upper), rate_category := cat][]
    
       pre_rate rate_category
    1:     0.27             1
    2:     1.91             2
    3:     2.81             3
    4:     3.21             4
    5:     4.80             5
    6:     1.00            NA
    

    请注意,任何给定间隔之外的pre-rate 值会自动获得NA rate_category

    【讨论】:

      【解决方案4】:

      您可以使用代数方法来解决您的问题,它应该比您的 ifelse 更快:

      pre_rate = c(0.27, 1.91, 2.81, 3.21, 4.80) 
      crimes = data.frame(pre_rate)   
      crimes$rate = (pre_rate > 0.26 & pre_rate < 0.87)*1 + 
        (pre_rate > 1.04 & pre_rate < 1.94)* 2 + 
        (pre_rate > 2.03 & pre_rate < 2.96)* 3 + 
        (pre_rate > 3.10 & pre_rate < 3.82)* 4 + 
        (pre_rate > 4.20 & pre_rate < 11.00)* 5
      

      这里的想法是从表达式中获取真值或假值,然后乘以您将其视为类别的数字。唯一的区别是你不会在这里得到不匹配的 NA,而是你会得到一个零,你当然可以改变它。还要添加,如果您想对 cme​​ts 中提到的结果进行矢量化(逐个元素匹配)结果,请使用“&”。

      输出:

      #> crimes
      # pre_rate rate
      #1     0.27    1
      #2     1.91    2
      #3     2.81    3
      #4     3.21    4
      #5     4.80    5
      

      【讨论】:

      • 您也可以只包含&lt; 关系并省略严格递增编码的系数。
      【解决方案5】:

      如果您的数据不包含空白,而您只需要索引,则可以使用.bincode

      crimes$rate_category <- .bincode(crimes$pre_rate,
                                       breaks = c(-Inf, 1, 2, 3, 4, Inf))
      

      如果您想要每个间隔的特定值,您可以通过 data.table 包使用滚动连接:

      library(magrittr)
      library(data.table)
      
      rate_category_by_pre_rate <- 
        data.table(rate_category = c("foo", "bar", "foobar", "baz", "foobie"),
                   pre_rate = c(1, 2, 3, 4, 11)) %>%
        setkey(pre_rate)
      
      crimes %>%
        as.data.table %>%
        setkey(pre_rate) %>%
        rate_category_by_pre_rate[., roll = -Inf]
      
      #>    rate_category pre_rate
      #> 1:           foo     0.27
      #> 2:           bar     1.91
      #> 3:        foobar     2.81
      #> 4:           baz     3.21
      #> 5:        foobie     4.80
      

      但是,在您的情况下,您可能只需要 ceilingpre_rate 的值向上取整并将其上限为 5:

      crimes$rate_category <- pmin(ceiling(crimes$pre_rate), 5)
      
      #>   pre_rate rate_category
      #> 1     0.27             1
      #> 2     1.91             2
      #> 3     2.81             3
      #> 4     3.21             4
      #> 5     4.80             5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-20
        • 2018-07-08
        • 2023-03-22
        • 2020-10-15
        • 2021-12-18
        • 2023-03-30
        • 1970-01-01
        相关资源
        最近更新 更多