Как вернуть число десятичных знаков в 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 ненулевых десятичных значений.
Ответы
Ответ 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))
}