【问题标题】:Find position of first non-zero decimal查找第一个非零小数的位置
【发布时间】:2017-04-18 18:42:54
【问题描述】:

假设我有以下本地宏:

loc a = 12.000923

我想获取第一个非零小数的小数位(本例中为4)。

有很多方法可以实现这一点。一种是把a当作一个字符串,找到.的位置:

loc a = 12.000923
loc b = strpos(string(`a'), ".")
di "`b'"

由于我得到第一个非零元素,因此可以进一步循环小数并计数。当然,这似乎不是一个非常优雅的方法。

你能提出一个更好的方法来解决这个问题吗?也许是正则表达式?

【问题讨论】:

    标签: regex string stata


    【解决方案1】:

    好吧,我不知道 Stata,但根据 documentation\.(0+)? 是支持的,在 Stata 中转换这 2 行 JavaScript 函数应该不难。

    返回第一个非零小数的位置,如果没有小数则返回-1。

    function getNonZeroDecimalPosition(v) {
      var v2 = v.replace(/\.(0+)?/, "")
      return v2.length !== v.length ? v.length - v2.length : -1
    }
    

    说明

    我们从输入字符串中删除一个点,后跟可选的连续零。 原始输入字符串和这个新字符串的长度之间的差异给出了第一个非零小数的位置

    Demo

    示例片段

    function getNonZeroDecimalPosition(v) {
      var v2 = v.replace(/\.(0+)?/, "")
      return v2.length !== v.length ? v.length - v2.length : -1
    }
    
    var samples = [
      "loc a = 12.00012",
      "loc b = 12",
      "loc c = 12.012",
      "loc d = 1.000012",
      "loc e = -10.00012",
      "loc f = -10.05012",
      "loc g = 0.0012"
    ]
    
    samples.forEach(function(sample) {
      console.log(getNonZeroDecimalPosition(sample))
    })

    【讨论】:

      【解决方案2】:

      您可以在mata 的一行中做到这一点,而无需使用正则表达式:

      foreach x in 124.000923 65.020923 1.000022030 0.0090843 .00000425 {
          mata: selectindex(tokens(tokens(st_local("x"), ".")[selectindex(tokens(st_local("x"), ".") :== ".") + 1], "0") :!= "0")[1]
      }
      
      4
      2
      5
      3
      6
      

      下面,你可以看到详细的步骤:

      . local x = 124.000823
      
      . mata:
      
      : /* Step 1: break Stata's local macro x in tokens using . as a parsing char */
      : a = tokens(st_local("x"), ".")
      
      : a
                  1        2        3
          +----------------------------+
        1 |     124        .   000823  |
          +----------------------------+
      
      : /* Step 2: tokenize the string in a[1,3] using 0 as a parsing char */
      : b = tokens(a[3], "0")
      
      : b
               1     2     3     4
          +-------------------------+
        1 |    0     0     0   823  |
          +-------------------------+
      
      : /* Step 3: find which values are different from zero */
      : c = b :!= "0"
      
      : c
             1   2   3   4
          +-----------------+
        1 |  0   0   0   1  |
          +-----------------+
      
      : /* Step 4: find the first index position where this is true */
      : d = selectindex(c :!= 0)[1]
      
      : d
        4
      
      : end
      

      您还可以在步骤2 中使用 同样的逻辑。

      这是.之后的索引值:

      . mata: 
      
      : k = selectindex(a :== ".") + 1
      
      : k
        3
      
      : end
      

      在这种情况下,步骤2 变为:

      . mata: 
      
      : 
      : b = tokens(a[k], "0")
      
      : b
               1     2     3     4
          +-------------------------+
        1 |    0     0     0   823  |
          +-------------------------+
      
      : end
      

      对于没有小数的意外情况:

      foreach x in 124.000923 65.020923 1.000022030 12 0.0090843 .00000425 {
          if strmatch("`x'", "*.*") mata: selectindex(tokens(tokens(st_local("x"), ".")[selectindex(tokens(st_local("x"), ".") :== ".") + 1], "0") :!= "0")[1]
          else display "  0"
      }
      
      4
      2
      5
      0
      3
      6
      

      【讨论】:

        【解决方案3】:

        直截了当的答案使用正则表达式和命令来处理字符串。 可以选择所有小数,找到第一个非0的小数,最后找到它的位置:

        loc v  = "123.000923"
        
        loc v2 = regexr("`v'", "^[0-9]*[/.]", "")      // 000923
        loc v3 = regexr("`v'", "^[0-9]*[/.][0]*", "")  // 923
        loc first = substr("`v3'", 1, 1)               // 9
        loc first_pos = strpos("`v2'", "`first'")      // 4: position of 9 in 000923
        
        di "`v2'"
        di "`v3'"
        di "`first'" 
        di "`first_pos'" 
        

        一步相当于:

        loc first_pos2 = strpos(regexr("`v'", "^[0-9]*[/.]", ""), substr(regexr("`v'", "^[0-9]*[/.][0]*", ""), 1, 1))
        di "`first_pos2'"
        

        另一个答案中建议的替代方法是将从 0 清除的小数块的长度与未清除的小数块的长度进行比较。 这一步是:

         loc first_pos3 = strlen(regexr("`v'", "^[0-9]*[/.]", "")) - strlen(regexr("`v'", "^[0-9]*[/.][0]*", "")) + 1
         di "`first_pos3'" 
        

        【讨论】:

        • 您是否尝试在 Stata 中翻译我的解决方案?我很好奇这是否可能;)
        • 它在某种程度上是相似的,主要区别在于我不是比较两个长度,而是我直接使用strpos从零中找到第一个小数的位置,但是两种方法都可以,谢谢!
        • Okthanx ;) 为了优化你的代码,第一个正则表达式可以替换为^\d+\.,第二个正则表达式替换为^0+
        【解决方案4】:

        不使用正则表达式,而是使用 log10(将数字视为数字),此函数将:

        • 对于 >= 1 或
        • 或者(更具体地说,根据您的要求),对于介于 1 和 -1 之间的数字,返回第一个非零数字出现的小数点右侧的位数,并返回负数。
        digitsFromDecimal = (n) => {
            dFD = Math.log10(Math.abs(n)) | 0;
            if (n >= 1 || n <= -1) { dFD++; }
            return dFD;
        }
        
        var x = [118.8161330, 11.10501660, 9.254180571, -1.245501523, 1, 0, 0.864931613, 0.097007836, -0.010880074, 0.009066729];
        x.forEach(element => {
            console.log(`${element}, Digits from Decimal: ${digitsFromDecimal(element)}`);
        });
        
        // Output
        // 118.816133, Digits from Decimal: 3
        // 11.1050166, Digits from Decimal: 2
        // 9.254180571, Digits from  Decimal: 1
        // -1.245501523, Digits from Decimal: 1
        // 1, Digits from Decimal: 1
        // 0, Digits from Decimal: 0
        // 0.864931613, Digits from Decimal: 0
        // 0.097007836, Digits from Decimal: -1
        // -0.010880074, Digits from Decimal: -1
        // 0.009066729, Digits from Decimal: -2
        

        【讨论】:

          【解决方案5】:

          Pearly 的 Mata 解决方案非常讨人喜欢,但应注意“根本没有小数”的“意外”情况

          此外,正则表达式可以在令人难忘的 1 行中创建,这不是一个太糟糕的选择。

          loc v  = "123.000923"
          capture local x = regexm("`v'","(\.0*)")*length(regexs(0))
          

          下面的代码测试了更多的 v 值。

          foreach v in 124.000923 605.20923 1.10022030 0.0090843 .00000425 12 .000125 {
          capture local x = regexm("`v'","(\.0*)")*length(regexs(0))
          di "`v': The wanted number = `x'"
          }
          

          【讨论】:

            猜你喜欢
            • 2013-01-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-08-01
            • 2015-08-30
            • 2016-03-02
            • 1970-01-01
            • 2020-02-14
            相关资源
            最近更新 更多