Подсчет начальных нулей между десятичной точкой и первой ненулевой цифрой
Предположим, что если у нас есть число 1.000633, я хочу посчитать количество нулей после десятичной точки до первой ненулевой цифры в дроби, ответ должен быть 3. Для 0.002 ответ должен быть 2.
В R нет такой функции, которая могла бы помочь. Я исследовал функцию Ndec в пакете DescTools, но она не выполняет свою работу.
9 ответов
С помощью regexpr
И его match.length
аргумент
attr(regexpr("(?<=\\.)0+", x, perl = TRUE), "match.length")
Вот еще одна возможность:
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
Мы можем использовать 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))
С помощью 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
Еще один способ использования str_count
от stringr
пакет,
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
Просто хочу добавить, что все вышеперечисленные решения, которые я пробовал, имели проблемы с числами, такими как 0,00001, которые форматируются в экспоненциальную запись, если вы не позаботитесь о том, чтобы указать, что это не так. Я закончил на следующем решении:
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
}
Аналогично решению @MatthewPeter. Если вы используетеceiling()
а неfloor()
функцию, а затем вычтите 1, вы не столкнетесь с проблемами с числами 1*10**x, такими как (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
Данные:
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
Ты можешь использовать 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))