Reputation: 683
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
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
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
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
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
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
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
str1 <- as.character(c(1.000633, 0.002, 0.000633,
10.000633, 3.0069006))
Upvotes: 9
Reputation: 92292
Using regexpr
and its match.length
argument
attr(regexpr("(?<=\\.)0+", x, perl = TRUE), "match.length")
Upvotes: 24
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