【问题标题】:how to truncate numbers maximally while avoiding duplicates?如何在避免重复的同时最大限度地截断数字?
【发布时间】:2020-01-04 17:54:41
【问题描述】:

我有一个排序的数字序列,例如

x <- c(1, 2, 2.5, 3, 3.0001, 3.0002, 4)

我想将它们格式化为字符串,使输出尽可能短,但避免重复。例如,x 可接受的输出是:

sprintf("%.5g", x)
#> [1] "1"      "2"      "2.5"    "3"      "3.0001" "3.0002" "4"     

但这会失败:

y <- c(1, 2, 2.5, 3, 3.00001, 3.00002, 4)
sprintf("%.5g", y)
#> [1] "1"   "2"   "2.5" "3"   "3"   "3"   "4"  

我会对可能的替代解决方案感兴趣(下面有一个简单的解决方案)。

更新:使用sprintf("%s", x) 的问题如下,有时,我的输入将是一组非常长的数字,我想截断它们 - 这么长因为它们不会变得不独特。

例如如果我有

x <- c(1.00001, 2.00001)

我的理想输出是"1", "2"。但是如果我有

x <- c(1.00001, 1.00002)

那我需要"1.00001", "1.00002"

【问题讨论】:

    标签: r string type-conversion


    【解决方案1】:

    我的理解是,如果您只使用as.character(x),那将不是您所需要的。如果你有:

    x <- c(1.00001, 2.00001)
    

    结果是:

    [1] "1.00001" "2.00001"
    

    而不是所需的“1”和“2”,因为每个在被截断后都是唯一的。

    我不确定这是否是您的想法(见下文),但希望对您有所帮助。一种可能性是在截断后计算每个级别的值的数量,并在它们不唯一时包括未截断的值。

    df <- data.frame(
      x = c(1,2,2.5,3,3.0001, 3.0002, 4)
    )
    
    library(dplyr)
    
    df <- df %>%
      mutate(res = trunc(x)) %>%
      group_by(res) %>%
      mutate(n = n()) %>%
      ungroup() %>%
      mutate(res = ifelse(n > 1, as.character(x), as.character(res)))
    
    df$res
    [1] "1"      "2"      "2.5"    "3"      "3.0001" "3.0002" "4" 
    

    编辑:如果对使用dplyr 不感兴趣,您可以获得重复值的索引(基于它们的截断等效项),然后截断那些。这将是一个基本的 R 替代方案:

    x = c(1,2,2.5,3,3.0001, 3.0002, 4.0001)
    indx <- which(!duplicated(trunc(x)))
    x[indx] <- as.character(trunc(x[indx]))
    x
    [1] "1"      "2"      "2.5"    "3"      "3.0001" "3.0002" "4"  
    

    【讨论】:

    • 我认为这种方法是正确的,虽然拉入 dplyr 是矫枉过正,为什么不直接使用 anyDuplicated()
    • 谢谢 - 我编辑了 base R 的解决方案。这看起来更接近您的想法吗?
    【解决方案2】:

    一种可能性是sprintf("%s", x) 或等效

    as.character(x)
    > [1] "1"      "2"      "2.5"    "3"      "3.0001" "3.0002" "4"     
    

    虽然我不确定何时以及如何出错。在我的电脑上,它似乎可以解决大约 1e-14 的差异。

    【讨论】:

      【解决方案3】:

      这就是我最后所做的。这使得数字尽可能精确,但仅此而已。

      function (num) {
        for (digits in seq(0, 22L)) {
          res <- formatC(num, digits = digits, width = -1)
          if (anyDuplicated(res) == 0L) break
        }
        if (anyDuplicated(res) > 0L) stop(
              "Could not format breaks to avoid duplicates")
      
        return(res)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-26
        • 1970-01-01
        • 2015-12-09
        • 1970-01-01
        • 1970-01-01
        • 2012-08-26
        相关资源
        最近更新 更多