【问题标题】:Count leading zeros between the decimal point and first nonzero digit计算小数点和第一个非零数字之间的前导零
【发布时间】:2016-06-03 20:32:32
【问题描述】:

假设如果我们有一个数字 1.000633,我想计算小数点后的零个数,直到分数中的第一个非零数字,答案应该是 3。对于 0.002,答案应该是 2。

R 中没有这样的函数可以提供帮助。我已经探索了 DescTools 包中的 Ndec 函数,但它没有完成这项工作。

【问题讨论】:

    标签: r regex desctools


    【解决方案1】:

    使用rle函数:

    #test values
    x <- c(0.000633,0.003,0.1,0.001,0.00633044,10.25,111.00012,-0.02)
    
    #result
    sapply(x, function(i){
      myNum <- unlist(strsplit(as.character(i), ".", fixed = TRUE))[2]
      myNumRle <- rle(unlist(strsplit(myNum, "")))
      if(myNumRle$values[1] == 0) myNumRle$lengths[1] else 0
    })
    
    #output
    # [1] 3 2 0 2 2 0 3 1
    

    【讨论】:

      【解决方案2】:

      我们可以使用sub

      ifelse(grepl("\\.0", str1), 
          nchar(sub("[^\\.]+\\.(0+)[^0]+.*", "\\1", str1)), NA)
      #[1] 3 2 3 3 2
      

      或使用stringi

      library(stringi)
      r1 <- stri_extract(str1, regex="(?<=\\.)0+")
      ifelse(is.na(r1), NA, nchar(r1))
      #[1] 3 2 3 3 2
      

      只是为了检查它是否适用于任何奇怪的情况

      str2 <- "0.00A-Z"
      nchar(sub("[^\\.]+\\.(0+)[^0]+.*", "\\1", str2))
      #[1] 2
      

      数据

      str1 <- as.character(c(1.000633, 0.002, 0.000633,
                                        10.000633, 3.0069006))
      

      【讨论】:

      • 再次感谢,试试 str1
      • 您可能需要编辑您的第一个解决方案,因为它是错误的。
      • @akrun 那里可能有任何数字,这应该适用于所有数字。几乎每个人都在他们的答案下对可能的问题发表评论,而不仅仅是你。例如,请参阅 herehere
      • 什么意思:“好的,Jaap 也在线”
      • 只允许在其余数字中除 0 之外的数字,例如 "[^\\.]+\\.(0+)[^0]{1}.*" 并且它会被找到(尽管我仍然更喜欢 RHertel 的 numeric 方法)。这是一个准确的解决方案,而不是赞成
      【解决方案3】:

      这是另一种可能性:

      zeros_after_period <- function(x) {
      if (isTRUE(all.equal(round(x),x))) return (0) # y would be -Inf for integer values
      y <- log10(abs(x)-floor(abs(x)))   
      ifelse(isTRUE(all.equal(round(y),y)), -y-1, -ceiling(y))} # corrects case ending with ..01
      

      例子:

      x <- c(1.000633, 0.002, -10.01, 7.00010001, 62.01)
      sapply(x,zeros_after_period)
      #[1] 3 2 1 3 1
      

      【讨论】:

      • @zx8754 现在好多了..?
      • 我喜欢这个解决方案,即使是 0.001 问题。
      • 我想你忘了矢量化它,因为现在它只适用于长度为一个的矢量... Myabe 这应该是ifelse(round(y) == y, -y-1, -ceiling(y)) ?
      • 不是列,只是几个值,比如x &lt;- c(0.1, 1.0, 1.001)
      • 我想知道为什么我的答案下面有两个 cmets,内容是“它不起作用”。事实上,它确实有效。
      【解决方案4】:

      使用stringr 包中的str_count 的另一种方式,

       x <- as.character(1.000633)
       str_count(gsub(".*[.]","",x), "0")
       #[1] 3
      

      编辑:这会计算小数点后的所有零,直到第一个非零值。

      y <- c(1.00215, 1.010001, 50.000809058, 0.1)
      str_count(gsub(".*[.]","",gsub("(?:(0+))[1-9].*","\\1",as.character(y))),"0")
      #[1] 2 1 3 0
      

      【讨论】:

      • 哇。这个问题迅速升级! :)。我选择了OP提到的两个案例。我会尽快修改。谢谢@DavidArenburg
      • y
      【解决方案5】:

      使用regexpr 及其match.length 参数

      attr(regexpr("(?<=\\.)0+", x, perl = TRUE), "match.length")
      

      【讨论】:

      • 对于x &lt;- 10.2,这将返回-1 而不是0。我必须在我的解决方案中插入一个ifelse 语句来捕获没有它会失败的情况。这可能是您认为我的实现复杂的原因。另一方面,也许您也可以考虑捕获此类案例,以便您的解决方案也适用于任何数量。
      • @RHertel 它总是返回-1 不匹配。那是不匹配的regexpr 表示法。我的解决方案适用于 任何 号码。
      • 好吧,我明白了——正如我明白为什么我的原始帖子需要更正一样。我只是不确定这是否对应于 OP 请求的输出(“..count number of zeros after the decimal point until first nonzero digit ...”)。零的负数对我来说似乎没有多大意义。
      • @RHertel 如果需要 OP,这可以很容易地以矢量化方式修复,但在这种情况下,-1 或 0 对我来说似乎同样没问题。
      • @RHertel 只需使用 (?&lt;=\\.)0+|$ 作为正则表达式,如果你想得到 0 而不是 -1。
      【解决方案6】:
      floor( -log10( eps + abs(x) - floor( abs( x ) ) ) )
      

      【讨论】:

      • 欢迎来到 Stack Overflow,感谢您回答这个问题。因为没有 cmets 的代码往往不是很有教育意义,所以我们希望您添加一些关于它如何回答问题的解释。谢谢!
      • 是的,这是最好的解决方案。但是,您应该考虑这样的整数日志值:count0 &lt;- function(x, tol = .Machine$double.eps ^ 0.5) { x &lt;- abs(x); y &lt;- -log10(x - floor(x)); floor(y) - (y %% 1 &lt; tol) }
      【解决方案7】:

      您可以使用sub,因为我们不需要跳转。因此不需要gsub

       nchar(sub(".*\\.(0*).*","\\1",str1))
      [1] 3 2 3 3 2
      

      在哪里

      str1 <- as.character(c(1.000633, 0.002, 0.000633,
                         10.000633, 3.0069006))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-16
        • 2021-10-31
        • 1970-01-01
        相关资源
        最近更新 更多