Подтвердить что ты не робот

Как вернуть число десятичных знаков в R

Я работаю в R. У меня есть ряд координат в десятичных градусах, и я хотел бы отсортировать эти координаты на сколько десятичных знаков эти числа (т.е. я хочу сбросить координаты, у которых слишком мало десятичных знаков).
Есть ли функция в R, которая может возвращать число десятичных знаков, число которых есть, что я мог бы включить в функцию записи?
Пример ввода:

AniSom4     -17.23300000        -65.81700

AniSom5     -18.15000000        -63.86700

AniSom6       1.42444444        -75.86972

AniSom7       2.41700000        -76.81700

AniLac9       8.6000000        -71.15000

AniLac5      -0.4000000        -78.00000

В идеале я бы написал script, который отменил бы AniLac9 и AniLac 5, потому что эти координаты не были записаны с достаточной точностью. Я хотел бы отбросить координаты, для которых как долгота, так и широта имеют менее 3 ненулевых десятичных значений.

4b9b3361

Ответ 1

Вы можете легко написать небольшую функцию для этой задачи, например:

decimalplaces <- function(x) {
    if ((x %% 1) != 0) {
        nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed=TRUE)[[1]][[2]])
    } else {
        return(0)
    }
}

и запустить:

> decimalplaces(23.43234525)
[1] 8
> decimalplaces(334.3410000000000000)
[1] 3
> decimalplaces(2.000)
[1] 0

Обновление (3 апреля 2018 г.) для отчета об адресе @owen88 об ошибке из-за округления чисел с плавающей запятой двойной точности - замена проверки x %% 1:

decimalplaces <- function(x) {
    if (abs(x - round(x)) > .Machine$double.eps^0.5) {
        nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed = TRUE)[[1]][[2]])
    } else {
        return(0)
    }
}

Ответ 2

Вот один из способов. Он проверяет первые 20 мест после десятичной точки, но вы можете отрегулировать число 20, если у вас есть что-то еще в виду.

x <- pi
match(TRUE, round(x, 1:20) == x)

Вот еще один способ.

nchar(strsplit(as.character(x), "\\.")[[1]][2])

Ответ 3

Ролевая по римскому предложению:

num.decimals <- function(x) {
    stopifnot(class(x)=="numeric")
    x <- sub("0+$","",x)
    x <- sub("^.+[.]","",x)
    nchar(x)
}
x <- "5.2300000"
num.decimals(x)

Если ваши данные не гарантированы надлежащей формы, вы должны сделать больше проверок, чтобы другие символы не подкрались.

Ответ 4

В [R] нет разницы между 2.30000 и 2.3, оба округляются до 2.3, поэтому одно не более точное, чем другое, если это то, что вы хотите проверить. С другой стороны, если это не то, что вы имели в виду: если вы действительно хотите это сделать, вы можете использовать 1) умножить на 10, 2) использовать функцию floor() 3) делить на 10 4) проверить равенство с оригиналом. (Однако имейте в виду, что сравнение float для равенства - это плохая практика, убедитесь, что это действительно то, что вы хотите)

Ответ 5

Для общего приложения здесь используется модификация кода daroczig для обработки векторов:

decimalplaces <- function(x) {
    y = x[!is.na(x)]
    if (length(y) == 0) {
      return(0)
    }
    if (any((y %% 1) != 0)) {
      info = strsplit(sub('0+$', '', as.character(y)), ".", fixed=TRUE)
      info = info[sapply(info, FUN=length) == 2]
      dec = nchar(unlist(info))[seq(2, length(info), 2)]
      return(max(dec, na.rm=T))
    } else {
      return(0)
    }
}

В общем, могут возникнуть проблемы с тем, как число с плавающей запятой хранится как двоичное. Попробуйте следующее:

> sprintf("%1.128f", 0.00000000001)
[1] "0.00000000000999999999999999939458150688409432405023835599422454833984375000000000000000000000000000000000000000000000000000000000"

Сколько десятичных знаков мы имеем сейчас?

Ответ 6

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

К сожалению, даже обновленное решение @daroczig не помогло мне проверить, имеет ли число менее 8 десятичных цифр.

@daroczig код:

decimalplaces <- function(x) {
    if (abs(x - round(x)) > .Machine$double.eps^0.5) {
        nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed = TRUE)[[1]][[2]])
    } else {
        return(0)
    }
}

В моем случае получены следующие результаты

NUMBER / NUMBER OF DECIMAL DIGITS AS PRODUCED BY THE CODE ABOVE
[1] "0.0000437 7"
[1] "0.000195 6"
[1] "0.00025 20"
[1] "0.000193 6"
[1] "0.000115 6"
[1] "0.00012501 8"
[1] "0.00012701 20"

и т.д.

До сих пор удалось выполнить необходимые тесты с помощью следующего неуклюжего кода:

if (abs(x*10^8 - floor(as.numeric(as.character(x*10^8)))) > .Machine$double.eps*10^8) 
   {
   print("The number has more than 8 decimal digits")
   }

PS: Возможно, я что-то упускаю из-за того, что не могу получить корень .Machine$double.eps, поэтому, пожалуйста, будьте осторожны

Ответ 7

Другой вклад, полностью сохраняя в виде числовых представлений без преобразования в символ:

countdecimals <- function(x) 
{
  n <- 0
  while (!isTRUE(all.equal(floor(x),x)) & n <= 1e6) { x <- x*10; n <- n+1 }
  return (n)
}

Ответ 8

Не уверен, почему этот простой подход не использовался выше (загрузите канал из tidyverse/magrittr).

count_decimals = function(x) {
  #length zero input
  if (length(x) == 0) return(numeric())

  #count decimals
  x_nchr = x %>% abs() %>% as.character() %>% nchar() %>% as.numeric()
  x_int = floor(x) %>% abs() %>% nchar()
  x_nchr = x_nchr - 1 - x_int
  x_nchr[x_nchr < 0] = 0

  x_nchr
}
> #tests
> c(1, 1.1, 1.12, 1.123, 1.1234, 1.1, 1.10, 1.100, 1.1000) %>% count_decimals()
[1] 0 1 2 3 4 1 1 1 1
> c(1.1, 12.1, 123.1, 1234.1, 1234.12, 1234.123, 1234.1234) %>% count_decimals()
[1] 1 1 1 1 2 3 4
> seq(0, 1000, by = 100) %>% count_decimals()
 [1] 0 0 0 0 0 0 0 0 0 0 0
> c(100.1234, -100.1234) %>% count_decimals()
[1] 4 4
> c() %>% count_decimals()
numeric(0)

Так что R, кажется, внутренне не различает получение 1.000 и 1 изначально. Поэтому, если у каждого есть входной вектор с различными десятичными числами, можно увидеть, сколько цифр у него было изначально (как минимум), взяв максимальное значение числа десятичных знаков.

Отредактировано: исправлены ошибки

Ответ 9

Интересный вопрос. Вот еще одна настройка работы вышеупомянутых респондентов, векторизация и расширение для обработки цифр слева от десятичной точки. Протестировано против отрицательных цифр, что дало бы неправильный результат для предыдущего подхода strsplit().

Если требуется только подсчитать числа справа, аргумент trailingonly может быть установлен на TRUE.

nd1 <- function(xx,places=15,trailingonly=F) {
  xx<-abs(xx); 
  if(length(xx)>1) {
    fn<-sys.function();
    return(sapply(xx,fn,places=places,trailingonly=trailingonly))};
  if(xx %in% 0:9) return(!trailingonly+0); 
  mtch0<-round(xx,nds <- 0:places); 
  out <- nds[match(TRUE,mtch0==xx)]; 
  if(trailingonly) return(out); 
  mtch1 <- floor(xx*10^-nds); 
  out + nds[match(TRUE,mtch1==0)]
}

Вот версия strsplit().

nd2 <- function(xx,trailingonly=F,...) if(length(xx)>1) {
  fn<-sys.function();
  return(sapply(xx,fn,trailingonly=trailingonly))
  } else {
    sum(c(nchar(strsplit(as.character(abs(xx)),'\\.')[[1]][ifelse(trailingonly, 2, T)]),0),na.rm=T);
  }

Строковая версия отключается на 15 цифр (на самом деле, не уверен, почему другой аргумент помещается на один... причина, по которой он превысил, состоит в том, что он подсчитывает цифры в обоих направлениях, поэтому он может увеличиться до двух размер, если число достаточно велико). Вероятно, есть опция форматирования as.character(), которая может дать nd2() эквивалентную опцию аргументу places nd1().

nd1(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0));
# 2  2  1  3  1  4 16 17  1
nd2(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0));
# 2  2  1  3  1  4 15 15  1

nd1() быстрее.

rowSums(replicate(10,system.time(replicate(100,nd1(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0))))));
rowSums(replicate(10,system.time(replicate(100,nd2(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0))))));

Ответ 10

Векторное решение, основанное на функции Дарокцига (также может работать с грязными столбцами, содержащими строки и цифры):

decimalplaces_vec <- function(x) {

  vector <- c()
  for (i in 1:length(x)){

    if(!is.na(as.numeric(x[i]))){

      if ((as.numeric(x[i]) %% 1) != 0) {
        vector <- c(vector, nchar(strsplit(sub('0+$', '', as.character(x[i])), ".", fixed=TRUE)[[1]][[2]]))


      }else{
        vector <- c(vector, 0)
      }
    }else{
      vector <- c(vector, NA)
    }
  }
  return(max(vector))
}