Фиксированные и десятичные разряды положительного числа

Я хочу написать функцию для подсчета количества фиксированных цифр и количества десятичных цифр каждого положительного числа. Для целых чисел: например, для числа 459 я хотел бы видеть

fixed=3 and decimal=0

как результат функции. Для десятичных чисел: например, для числа 12,657 хотелось бы видеть

fixed=2 and decimal=3

(потому что 12 состоит из двух цифр, а 657 — из трех цифр). Для чисел меньше 1 я хотел бы видеть

0 

как исправленный, например для числа 0,4056 я хотел бы видеть

fixed=0 and decimal=4 

или для числа 0,13 я хотел бы видеть

fixed=0 and decimal=2

Общая идея у меня следующая:

digits<-function(x){
  length(strsplit(as.character(x),split="")[[1]])
}

Я хочу расширить свой код как новый, чтобы он работал, как я объяснил выше.


person Rojer    schedule 08.06.2020    source источник
comment
скажем, если у вас есть вектор v1 <- c(12, 12.657, 12.1), каким будет вывод, т.е. значения печати равны v1# [1] 12.000 12.657 12.100   -  person akrun    schedule 09.06.2020
comment
Мой ввод не является вектором. У меня есть число в качестве ввода. Для вашего примера: для 12 я хотел бы видеть фиксированное = 2 и dcimal = 0, для 12.657 я хотел бы видеть фиксированное = 2 и десятичное число = 3, и, наконец, для 12.1 в качестве входных данных я хотел бы видеть фиксированное = 2 и десятичное число =1 на выходе.   -  person Rojer    schedule 09.06.2020
comment
Это числовой формат или как строка   -  person akrun    schedule 09.06.2020
comment
С помощью функции as.character мы можем преобразовать число в строку, а затем работать со строкой.   -  person Rojer    schedule 09.06.2020
comment
Это незначительно, но мой ввод не является вектором может быть недальновидным: сделайте так, чтобы ваша функция работала с одиночными значениями и векторами произвольной длины сначала, и эта функция будет быть значительно более эффективными и адаптируемыми к большему количеству ситуаций. (Использование sapply все время может стать утомительным, когда оно должно было быть векторизовано заранее.)   -  person r2evans    schedule 09.06.2020
comment
Предполагая, что введенное число имеет конечное количество цифр, мой ответ ниже должен работать правильно. Он проверяет, что вы ввели действительное число, и учитывает как отрицательные числа, так и числа от -1 до 1. С наилучшими пожеланиями!   -  person Joshua Mire    schedule 09.06.2020
comment
Фундаментальная проблема с этим (как было упомянуто по крайней мере один раз) заключается в том, что компьютеры несовершенны при хранении чисел с плавающей запятой. Чтобы получить интуитивно понятный вывод, вам нужно будет установить некоторые правила, чтобы отсечь известные аномалии, которые вы увидите. Например, когда format(0.3/0.1, digits=17) показывает вам 2.999...96, вы говорите, что пять девяток, за которыми следует что-то, нужно округлить в большую сторону? А как насчет 1.000...1, что пять нулей, за которыми следует что-то, нужно округлить в меньшую сторону? Отсюда скользкий склон может только круче.   -  person r2evans    schedule 09.06.2020


Ответы (3)


Кажется, вы застряли в надежде, что это можно сделать. Хорошо, вот грубый способ:

fun <- function(x){
  stopifnot(is.numeric(as.numeric(x)))
  s = nchar(unlist(strsplit(as.character(x),".",fixed = TRUE)))
  if(as.numeric(x) < 1) s[1] <- s[1]-1
  setNames(as.list(s),c("fixed","decimal"))
}

CORRECT:

fun(10.234)
$fixed
[1] 2

$decimal
[1] 3

fun(-10.234)
$fixed
[1] 2

$decimal
[1] 3

fun(0.2346)
$fixed
[1] 0

$decimal
[1] 4

> fun(-0.2346)
$fixed
[1] 0

$decimal
[1] 4

INCORRECT: Note that fixed + decimal <=15!!!

fun(-10000567.2346678901876)
$fixed
[1] 8

$decimal
[1] 7 ## This is incorrect

Правильное значение:

fun("-10000567.2346678901876") # Note that the input x is a string
$fixed
[1] 8

$decimal
[1] 13
person Onyambu    schedule 08.06.2020
comment
Спасибо за ваш код и важный регистр фиксированной + десятичной дроби = 15, который вы нам сказали. Почему ваш код правильно работает для удовольствия (-10000567.2346678901876), но не работает для развлечения (-10000567.2346678901876)? @Онямбу - person Rojer; 09.06.2020
comment
Как мы можем исправить ваш код, чтобы он правильно работал с числовыми входами, такими как 10000567.2346678901876? @Онямбу - person Rojer; 09.06.2020
comment
Ваш код не работает для -1‹x‹0. Например, в вашем коде fun(-0.2346) дает в качестве вывода fixed=1, что неверно. Для удовольствия (-0,2346) мы бы увидели fixed=0 в качестве вывода. @Онямбу - person Rojer; 09.06.2020

Я не думаю, что это можно сделать. Мы не можем предполагать, что простое числовое значение точно представлено в компьютере. Большинство значений с плавающей запятой, конечно, не могут.

Введите 0,3 в консоли R:

> 0.3
[1] 0.3

Выглядит хорошо, не так ли? Но теперь давайте сделаем это:

> print(0.3, digits=22)
[1] 0.29999999999999999

По сути, если вы преобразуете число с плавающей запятой в строку, вы определяете, насколько точны вы хотите. Компьютер не может дать вам такую ​​точность, потому что он хранит все числа в битах и, таким образом, никогда не дает вам абсолютной точности. Даже если вы видите число как 0,3 и думаете, что оно имеет 0 фиксированных цифр и 1 десятичную из-за этого, это потому, что R решил напечатать его таким образом, а не потому, что это число представлено в памяти компьютера.

Другие ответы доказывают, что функция может обрабатывать простые случаи. Должен признаться, я узнал, что R проделывает невероятную работу по интерпретации чисел. Но мы должны соблюдать осторожность! Как только мы начинаем преобразовывать числа, такая функция не может гарантировать значимых результатов.

person Jan    schedule 08.06.2020
comment
Я понимаю, что вы объяснили. Но для вашего примера, который равен 0,3, я хотел бы видеть фиксированный = 0 и десятичный = 1 в качестве вывода. - person Rojer; 09.06.2020
comment
Этот код: digits‹-function(x){ length(strsplit(as.character(x),split=)[[1]]) } работает для целых чисел. Я хочу расширить его для десятичных чисел. - person Rojer; 09.06.2020
comment
Разница в том, что целые (то есть целые) числа могут быть представлены в компьютере как есть. Десятичная не может. - person Jan; 09.06.2020
comment
Я думаю, например, что для числа 12.456 мы можем преобразовать его в строку, используя функцию as.character(). Будет 12.456. Затем мы можем разделить строку на две части, учитывая точку (.), Я имею в виду 12 и 456, а затем найти цифры (длину) каждой части. - person Rojer; 09.06.2020
comment
@vahid Что вы должны знать, так это то, что вы не можете полностью хранить десятичную дробь в компьютере. Это всего лишь приближение. Следовательно, время от времени вы будете получать неверные результаты - person Onyambu; 09.06.2020

ОТРЕДАКТИРОВАНО (на основе комментариев):

Следующая функция будет работать для чисел с 15 или менее значащими цифрами:

digits<-function(x){
  ## make sure x is a number, if not stop 
  tryCatch(
    {
      x/1
    },
    error=function(cond) {
      stop('x must be a number')
    }
  )
  ## use nchar() and strsplit() to determine number of digits before decimal point
  fixed<-nchar(strsplit(as.character(x),"\\.")[[1]][1])
  ## check if negative
  if(substr(strsplit(as.character(x),"\\.")[[1]][1],1,1)=="-"){fixed<-fixed-1}
  ## check if -1<x<1
  if(as.numeric(strsplit(as.character(x),"\\.")[[1]][1])==0){fixed<-fixed-1}
  ## use nchar() and strsplit() to determine number of digits after decimal point
  decimal<-nchar(strsplit(as.character(x),"\\.")[[1]][2])
  ## for integers, replace NA decimal result with 0
  if(is.na(nchar(strsplit(as.character(x),"\\.")[[1]][2]))){decimal<-0}
  ## return results
  print(paste0("fixed: ",fixed," and decimal: ", decimal))
}

Если вы хотите считать отрицательные знаки (-) цифрой, вам нужно удалить:

## check if negative
if(substr(strsplit(as.character(x),"\\.")[[1]][1],1,1)=="-"){fixed<-fixed-1}

Примечание. Ограничение на 15 или менее значащих цифр основано на 8-байтовом представлении с плавающей запятой.

Единственный способ, которым я смог преодолеть это, - использовать приведенный ниже код.

digits<-function(x){
  tryCatch(
    {
      as.numeric(x)/1
    },
    error=function(cond) {
      stop('x must be a number')
    }
  )
  j <- 0
  num <- x
  fixed <- 0
  decimal <- 0
  for(i in 1:nchar(x)){
    if(substr(num, i, i) == "."){
      j <- 1
    } else if(j==0){
      fixed <- fixed + 1
    } else{
      decimal <- decimal + 1
    }
  }
  if(substr(x,1,1)=="-" & substr(as.numeric(x),2,2)==0){
    fixed<-fixed-2
  }else if(substr(x,1,1)=="-"){
    fixed<-fixed-1
  }else if(substr(as.numeric(x),1,1)==0){
    fixed<-fixed-1
  }else{}
  print(paste0("fixed: ",fixed," and decimal: ", decimal))
}

Однако этот код требует, чтобы число было передано в функцию в виде строки символов, чтобы вычислить переданные 15 значащих цифр, как показано ниже:

x<-"111111111111111111111111111111111111.22222222222222222222222222222222222222222222222"
digits(x)

Что возвращает:

[1] "фиксированное: 36 и десятичное: 47"

Это затем ограничивает применение этой функции, поскольку ее нельзя использовать в канале dplyr, и вы не получите точных результатов для чисел, которые ранее были округлены. Например, используя то же число, что и выше, но сохраняя его как число вместо строки символов, вы получите следующие результаты:

x<-111111111111111111111111111111111111.22222222222222222222222222222222222222222222222
digits(x)

[1] "фиксированное: 1 и десятичное: 18"

Я надеюсь, что это хоть немного поможет!

person Joshua Mire    schedule 08.06.2020
comment
Эта функция не может работать, так как не использует аргумент x. - person Jan; 09.06.2020
comment
Когда я использую это с 1/3, я получаю fixed: 1 and decimal: 15. Но когда я проверяю с print(1/3, digits=16) и, очевидно, я получаю не 0.3333333333333330, а 0.3333333333333333 с 16 тройками. Каков истинный результат с 1/3? - person Jan; 09.06.2020
comment
Я не хочу проверять функцию для дробей вроде 1/3 или 1/7. Мне нужна функция для плавающих чисел, таких как 1,323, 0,3 или 12,34. Я имею в виду числа, которые имеют определенное количество десятичных знаков. - person Rojer; 09.06.2020
comment
@vahid Тогда это должно сработать для вас. С наилучшими пожеланиями! - person Joshua Mire; 09.06.2020
comment
@JoshuaMire, спасибо за ваш код. Оно работает. Но почему, когда я тестирую функцию для разных примеров, вывод отображается красным. Конечно, выходные данные для примеров правильные, но мне интересно, почему выходные данные не отображаются черным цветом (как обычный вывод, который я когда-либо видел), а в этом случае выходные данные отображаются красным цветом (они похожи на вывод предупреждения в R). - person Rojer; 09.06.2020
comment
@vahid Это потому, что я использовал функцию message(). Вы можете изменить это на функцию print(), если вам нужно использовать вывод как объект (или если вы предпочитаете черный вывод). Я предположил, что, поскольку вы искали возвращаемую фразу вместо вектора/списка/данных.фрейм, вы бы предпочли функцию message(). - person Joshua Mire; 09.06.2020
comment
@JoshuaMire, я понял. Вы использовали сообщение в последней строке кода. Я думаю, что из-за использования сообщения результаты были в минусе. Поменял message на print, теперь выводы черным цветом (мне нормально). - person Rojer; 09.06.2020
comment
@vahid Я думаю, ты понял это сам, пока я отвечал. Смотрите мой предыдущий ответ для более подробной информации. Если это работает для вас, пожалуйста, примите ответ, чтобы другим было легче найти решение (и потому что это помогает мне!) - person Joshua Mire; 09.06.2020
comment
@JoshuaMire, я понял, что в вашем коде есть проблема. Когда я использую цифры (67845905677.2456991266788), выходные значения фиксированы: 11 и десятичные: 4. Но, как мы знаем, в этом случае десятичное число должно быть 13, поэтому десятичное число: 4 неверно. Как сказал Онямбу в следующем, фиксированное + десятичное число должно быть меньше 15. Как мы можем решить эту проблему, чтобы функция фиксированных цифр работала правильно для случаев, когда фиксированное + десятичное число > 15? - person Rojer; 09.06.2020
comment
@vahid Мои извинения. Я обновил свой ответ своим лучшим обходным решением, однако это не сработает, если ваши числа уже сохранены, так как они уже были округлены. Извините, я не мог предоставить больше помощи. - person Joshua Mire; 09.06.2020
comment
@JoshuaMire, Большое спасибо за внимание. - person Rojer; 09.06.2020