как вернуть количество знаков после запятой в R - PullRequest
15 голосов
/ 03 марта 2011

Я работаю в 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

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

Ответы [ 10 ]

27 голосов
/ 03 марта 2011

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

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)
    }
}
9 голосов
/ 03 марта 2011

Поднимаясь по предложению Романа:

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

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

9 голосов
/ 03 марта 2011

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

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

Вот другой способ.

nchar(strsplit(as.character(x), "\\.")[[1]][2])
1 голос
/ 14 июля 2018

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

countdecimals <- function(x) 
{
  n <- 0
  while (!isTRUE(all.equal(floor(x),x)) & n <= 1e6) { x <- x*10; n <- n+1 }
  return (n)
}
1 голос
/ 03 мая 2018

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

К сожалению, даже обновленное решение @ 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, поэтому, пожалуйста, будьте осторожны

1 голос
/ 13 октября 2016

Для обычного применения, вот модификация кода Дароцига для обработки векторов:

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"

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

1 голос
/ 03 марта 2011

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

0 голосов
/ 23 апреля 2019

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

count_decimals = function(x) {
  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
}
> #test
> 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

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

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

0 голосов
/ 22 февраля 2019

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

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))
}
0 голосов
/ 27 января 2018

Интересный вопрос. Вот еще один твик работы вышеупомянутых респондентов, векторизованный и расширенный для обработки цифр слева от десятичной точки. Проверено на наличие отрицательных цифр, которые дают неверный результат для предыдущего подхода 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))))));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...