Другое правило округления, основанное на количестве цифр перед десятичной дробью - PullRequest
4 голосов
/ 13 января 2020

Скажем, у нас есть следующий числовой вектор:

y=c(0.111,1.11,11.1,111.1)

Я бы хотел, чтобы оно было округлено следующим образом:

0.11,1.11,11.1,111  

Это правило простое: мне нужно всего три цифры ! Но если число большое (например, 1111 или 11111), сохраните все цифры, но без десятичных знаков.

Конечно, должно быть более простое решение?:

lapply(y,function(x){
  ifelse(nchar(as.integer(x))<1,round(x,digits=3),
         ifelse(nchar(as.integer(x))==1,round(x,digits=2),
                ifelse(nchar(as.integer(x))==2,round(x,digits=1),
                       ifelse(nchar(as.integer(x))>2,round(x,digits=0)
                       ))))
}
)

Ответы [ 3 ]

7 голосов
/ 13 января 2020

Просто используйте signif:

y <- c(0.111,1.11,11.1,111.1,1111)
signif(y, pmax(3,trunc(log10(y)+1)))
#[1]    0.111    1.110   11.100  111.000 1111.000

Часть pmax(...) учитывает случай, когда число имеет 4 или более цифр перед десятичной дробью (log10 - это просто способ подсчитать те цифр).

3 голосов
/ 13 января 2020

Это работает?

y <- c(0.111,1.11,11.1,111.1,1111,11111)
(ry <- gsub("0+$","",round(y*100)/100))
## [1] "0.11"  "1.11"  "11.1"  "111.1" "1111"  "11111"
as.numeric(ry)
## [1]     0.11     1.11    11.10   111.10  1111.00 11111.00

Мне обычно не нравятся решения, которые преобразуются в символы, но, кажется, они делают то, что вы хотите ... (обратите внимание, что округляет как вы хотите, но печать числовых значений c заново включает нулевые значения: cat(ry,"\n") выведет числа как есть)

1 голос
/ 13 января 2020

Не ясно, идет ли речь о красивой печати (результат символ ) или округлении (результат цифра c).

Если вопрос о преобразовании в символ для красивой печати, можно использовать функцию sprintf().

Функция sprintf() понимает спецификатор преобразования g, где может быть указано число значащих цифр . Однако он не охватывает весь диапазон значений. Поэтому необходимо использовать различные спецификаторы преобразования в зависимости от размера y:

y <- c(0.0111, 0.111, 1.11, 11.1, 111.1, 1111, 11111)
sprintf(c("%3.2f", "%3.3g", "%3.0f")[cut(y, c(0, 1, 1000, Inf))], y)
[1] "0.01"  "0.11"  "1.11"  "11.1"  "111"   "1111"  "11111"

Если речь идет о округлении чисел c значений:

round(y, c(2, 1, 0)[cut(y, c(0, 10, 100, Inf))])
[1]     0.01     0.11     1.11    11.10   111.00  1111.00 11111.00

Объяснение

c("%3.2f", "%3.3g", "%3.0f")[cut(y, c(0, 1, 1000, Inf))]

является заменой для вложенного ifelse(), чтобы выбрать правильный спецификатор преобразования для каждого y:

[1] "%3.2f" "%3.2f" "%3.3g" "%3.3g" "%3.3g" "%3.0f" "%3.0f"

cut() преобразуется из числа c в коэффициент. Он делит диапазон y на интервалы и кодирует значения в y в зависимости от того, на какой интервал они попадают. Крайний левый интервал соответствует первому уровню, следующий левый - второму уровню и так далее. (от help("cut")).

Затем номер уровня используется для подстановки вектора спецификаторов преобразования для выбора соответствующего спецификатора в соответствии со значением y.

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

sprintf(as.character(cut(y, c(0, 1, 1000, Inf), labels = c("%3.2f", "%3.3g", "%3.0f"))), y)
[1] "0.01"  "0.11"  "1.11"  "11.1"  "111"   "1111"  "11111"
...