找到第一个非零小数的位置

时间:2017-04-18 18:42:55

标签: regex string stata

假设我有以下本地宏:

loc a = 12.000923

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

有很多方法可以实现这一目标。一种方法是将a视为字符串并找到.的位置:

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

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

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

5 个答案:

答案 0 :(得分:5)

您可以在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

答案 1 :(得分:3)

好吧,我不知道Stata,但根据documentation\.(0+)?被支持,并且在Stata中转换这两行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 :(得分:1)

直接的答案使用正则表达式和命令来处理字符串。 可以选择所有小数,找到第一个非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'" 

答案 3 :(得分:1)

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'"
}

答案 4 :(得分:0)

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

  • 对于大于等于1的数字或小于等于-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