Подсчет ведущих нулей между десятичной точкой и первой ненулевой цифрой

Предположим, если у нас есть число 1,000633, я хочу подсчитать количество нулей после запятой до первой ненулевой цифры в дроби, ответ должен быть 3. Для 0,002 ответ должен быть 2.

В R нет такой функции, которая могла бы помочь. Я изучил функцию Ndec в пакете DescTools, но она не работает.


person Annie    schedule 22.02.2016    source источник


Ответы (7)


Использование regexpr и его аргумента match.length

attr(regexpr("(?<=\\.)0+", x, perl = TRUE), "match.length")
person David Arenburg    schedule 22.02.2016
comment
@DavidArenburg Да, я не думал о возможных вариантах, когда что-то пошло не так. Спасибо за ответ. - person akrun; 22.02.2016
comment
@Энни, см. редактирование, я использовал regexpr вместо gregexpr, чтобы избежать sapply. Теперь он полностью векторизован и намного быстрее. - person David Arenburg; 22.02.2016
comment
Для x <- 10.2 это возвращает -1 вместо 0. Мне пришлось вставить оператор ifelse в свое решение, чтобы зафиксировать случай, когда без него не получится. Это может быть причиной того, что вы считаете мою реализацию сложной. С другой стороны, возможно, вы могли бы также рассмотреть возможность захвата таких случаев, чтобы ваше решение также работало для любого числа. - person RHertel; 22.02.2016
comment
@RHertel всегда возвращает -1 при отсутствии совпадения. Это обозначение regexpr для несоответствия. Мое решение работает для любого числа. - person David Arenburg; 22.02.2016
comment
Хорошо, я это понимаю - так же, как я понял, почему мой исходный пост нуждался в исправлении. Я просто не уверен, соответствует ли это запрошенному выходу OP (... количество нулей после запятой до первой ненулевой цифры...). Мне кажется, что отрицательное количество нулей не имеет особого смысла. - person RHertel; 22.02.2016
comment
@RHertel это можно легко исправить векторизованным способом, если OP желает, но в этом случае -1 или 0 кажутся мне одинаково подходящими для отсутствия совпадения. - person David Arenburg; 22.02.2016
comment
Справедливо. Ведь решает ОП. - person RHertel; 22.02.2016
comment
@RHertel Просто используйте (?<=\\.)0+|$ как регулярное выражение, если вы хотите получить 0 вместо -1. - person maaartinus; 23.02.2016

Вот еще одна возможность:

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
person RHertel    schedule 22.02.2016
comment
@zx8754 теперь лучше ..? - person RHertel; 22.02.2016
comment
Мне понравилось это решение даже с выпуском 0,001. - person zx8754; 22.02.2016
comment
Я думаю, вы забыли векторизовать его, так как теперь он работает только с одним вектором длины ... Может быть, это должно быть ifelse(round(y) == y, -y-1, -ceiling(y)) ? - person David Arenburg; 22.02.2016
comment
Не столбцы, а просто несколько значений, например x <- c(0.1, 1.0, 1.001) - person David Arenburg; 22.02.2016
comment
Интересно, почему у меня есть два комментария под моим ответом с содержанием, которое не работает. На самом деле, это работает. - person RHertel; 22.02.2016
comment
Не знаю, почему вы так агрессивны. Мой второй комментарий был относительно моего предыдущего комментария. Я пытался помочь вам векторизовать его. Не уверен, какой смысл писать такую ​​сложную реализацию, если это означало только одно значение. - person David Arenburg; 22.02.2016
comment
В письменной форме легко неправильно истолковать вещи. Если вы внимательно прочитаете то, что я написал, я не думаю, что вы найдете что-то агрессивное. И если вы это сделаете, это не было моим намерением. Теперь, эмоции в сторону, относительно вашей критики, что этот ответ сложен: может быть в глазах смотрящего, является ли математическое выражение более сложным, чем некоторые формулы регулярных выражений. Я предпочитаю математику, особенно когда рассматриваемый объект — число. Это функция, занимающая две строки. Сложно? Я так не думаю. - person RHertel; 22.02.2016
comment
y = log10(abs(x) %% 1) тоже работает. Думаю, чтобы сделать его векторизованным, y = -log10(abs(x) %% 1); ceiling(y) - ( (y %% 1) < 10^-options()$digits ) или использовать какой-то другой порог. Вероятно, все еще есть крайний случай или два - person Frank; 22.02.2016
comment
stackoverflow.com/questions/35553244/ - person Roland; 23.02.2016
comment
@Roland Спасибо за ссылку на ваш комментарий. Этот ответ, безусловно, заслуживает одобрения. - person RHertel; 23.02.2016
comment
Вы можете увеличить количество обнаруживаемых цифр, используя это решение, изменив значение по умолчанию для допуска в all.equal(), например, на .Machine$double.eps. - person Scott Kaiser; 24.03.2020

Мы можем использовать 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))
person akrun    schedule 22.02.2016
comment
@Энни, не могли бы вы проверить это еще раз. Судя по приведенному мной примеру, это не считается - person akrun; 22.02.2016
comment
Еще раз спасибо, попробуйте с str1 ‹- as.character(10.000633). - person Annie; 22.02.2016
comment
Кстати, библиотека (stringi) nchar (stri_extract (str1, regex = (?‹=\\.) 0+)) Это работает как по волшебству :). Спасибо большое. - person Annie; 22.02.2016
comment
Вероятно, вам нужно отредактировать свое первое решение, так как оно неверно. - person David Arenburg; 22.02.2016
comment
@DavidArenburg Да, я забыл о + - person akrun; 22.02.2016
comment
@DavidArenburg В результате я получаю 3 - person akrun; 22.02.2016
comment
@akrun там может быть любое число, и это должно работать для всех чисел. Почти у всех есть комментарий под своими ответами с возможными проблемами, не только у вас. См. здесь и здесь например - person David Arenburg; 22.02.2016
comment
Что вы имеете в виду под: Хорошо, Яап тоже в сети? - person Jaap; 22.02.2016
comment
просто разрешите цифры, кроме 0, в остальной части числа, например "[^\\.]+\\.(0+)[^0]{1}.*", и он будет найден (хотя я все же предпочитаю подход numeric RHertel). Дело в точном решении, а не в голосовании - person Cath; 22.02.2016
comment
@Jaap Это означает you are online. Я сказал что-то плохое? - person akrun; 22.02.2016
comment
Нет, я просто обнаружил, что это странное заявление в комментарии, адресованном @Cath. - person Jaap; 22.02.2016
comment
Вы должны сделать то же самое с решением stringi, так как оно также считает NA за два символа... Извините за назойливость :) - person David Arenburg; 22.02.2016
comment
@DavidArenburg В вашем решении возвращается -1 - person akrun; 22.02.2016
comment
Да, при отсутствии совпадений дает -1 - person David Arenburg; 22.02.2016

Использование функции 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
person zx8754    schedule 22.02.2016

Другой способ, используя 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
person Sotos    schedule 22.02.2016
comment
Вот это да. Этот вопрос быстро обострился! :). Я пошел с двумя случаями, упомянутыми OP. Я пересмотрю как можно скорее. Спасибо @DavidArenburg - person Sotos; 23.02.2016
comment
А как насчет y ‹- 0,00001 ? - person Scott Kaiser; 24.03.2020

Вы можете использовать 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))
person Onyambu    schedule 05.03.2018

person    schedule
comment
Добро пожаловать в Stack Overflow, и спасибо за ответ на этот вопрос. Поскольку код без комментариев, как правило, не очень информативен, мы хотели бы, чтобы вы добавили некоторое объяснение того, как это отвечает на вопрос. Спасибо! - person Toby Speight; 22.02.2016
comment
Да, это лучшее решение здесь. Однако вы должны учитывать целочисленные значения журнала, например: count0 <- function(x, tol = .Machine$double.eps ^ 0.5) { x <- abs(x); y <- -log10(x - floor(x)); floor(y) - (y %% 1 < tol) } - person Roland; 23.02.2016