Annie
Annie

Reputation: 683

Count leading zeros between the decimal point and first nonzero digit

Suppose if we have a number 1.000633, I want to count number of zeros after the decimal point until first nonzero digit in the fraction, the answer should be 3. For 0.002 the answer should be 2.

There is no such function in R that could help. I have explored at Ndec function in package DescTools but it does not do the job.

Upvotes: 29

Views: 3794

Answers (9)

DuesserBaest
DuesserBaest

Reputation: 2169

Similar to @MatthewPeter solution. If you use the ceiling() rather than the floor() function and then subtract 1 you do not face issues with 1*10**x numbers such as (0.1, 0.01, 0.001, ...).

x |>                  # input vector of numeric values
  abs() %%            # take the absolute value (delete sign of numbers)
  1 |>                # do numbers modulo 1 
                      # (delete everything before the decimal point)
  log10() |>          # use log10 to count the numbers after the period
  abs() |>            # flip sign, as we want the positive numbers
  ceiling() -         # take the ceiling of the numbers. 
                      # this will solve the 1*10**x issue
  1                   # substract 1 since we actually
                      # wanted the floor of the values

Data:

x <- c(0.000633,0.003,0.1,0.001,0.00633044,10.25,111.00012,-0.02)
# [1] 3 2 0 2 2 0 3 1

Upvotes: 0

Jared Rieck
Jared Rieck

Reputation: 81

Just want to add that all above solutions I tried had issues with numbers such as 0.00001 which format into scientific notation unless you are careful to specify that they don't. I ended on the following solution:

leading_zero <- function(x)  {
  if (x < 0.001){
    x <- as.character(format(x,scientific=FALSE))
  }
  nlead <- attr(regexpr("(?<=\\.)0+|$", x, perl = TRUE), "match.length") # leading zeros
  nlead
}

Upvotes: 1

Onyambu
Onyambu

Reputation: 79228

You can use sub since we do not need to jump. Thus no need of gsub

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

where

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

Upvotes: 0

Sotos
Sotos

Reputation: 51592

Another way using str_count from stringr package,

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

EDIT: This counts all zeros after decimal and until first non-zero value.

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

Upvotes: 7

RHertel
RHertel

Reputation: 23788

Here's another possibility:

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

Example:

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

Upvotes: 17

MatthewPeter
MatthewPeter

Reputation: 79

floor( -log10( eps + abs(x) - floor( abs( x ) ) ) )

Upvotes: 6

akrun
akrun

Reputation: 887118

We can use sub

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

Or using stringi

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

Just to check if it works with any strange cases

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

data

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

Upvotes: 9

David Arenburg
David Arenburg

Reputation: 92292

Using regexpr and its match.length argument

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

Upvotes: 24

zx8754
zx8754

Reputation: 56149

Using rle function:

#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

Upvotes: 7

Related Questions