Как округлить до ближайших 10 (или 100 или X)?


96

Я пишу функцию для построения данных. Я хотел бы указать красивое круглое число для оси Y max, которое больше максимума набора данных.

В частности, мне нужна функция, fooкоторая выполняет следующее:

foo(4) == 5
foo(6.1) == 10 #maybe 7 would be better
foo(30.1) == 40
foo(100.1) == 110 

Я дошел до

foo <- function(x) ceiling(max(x)/10)*10

для округления до ближайшего 10, но это не работает для произвольных интервалов округления.

Есть ли лучший способ сделать это в R?


Поведение R по умолчанию при построении графика заключается в установке пределов графика на ~ 4% за пределами диапазона данных в каждом направлении. Если вас это не устраивает, может быть, просто напишите что-нибудь, что выходит на более высокий или более низкий%?
Joran

@joran спасибо за информацию, но мне нужно несколько графиков с одинаковыми пределами оси и отметками, и я не уверен, как это помогает.
Abe

1
Ну, я здесь как бы нащупываю в темноте, так как не знаю всего фона. Ваш foo будет округлен до ближайшего X, если вы просто добавите еще один параметр X и замените обе 10 на X. Или вы можете использовать фасетирование.
Joran

15
Вы ищете ?pretty?
hadley

Почему так foo(4)==5и нет 10?
Джеймс

Ответы:


65

Если вы просто хотите округлить до ближайшей степени 10, просто определите:

roundUp <- function(x) 10^ceiling(log10(x))

На самом деле это также работает, когда x - вектор:

> roundUp(c(0.0023, 3.99, 10, 1003))
[1] 1e-02 1e+01 1e+01 1e+04

..но если вы хотите округлить до "хорошего" числа, вам сначала нужно определить, что такое "хорошее" число. Следующее позволяет нам определить «хороший» как вектор с хорошими базовыми значениями от 1 до 10. По умолчанию заданы четные числа плюс 5.

roundUpNice <- function(x, nice=c(1,2,4,5,6,8,10)) {
    if(length(x) != 1) stop("'x' must be of length 1")
    10^floor(log10(x)) * nice[[which(x <= 10^floor(log10(x)) * nice)[[1]]]]
}

Вышеупомянутое не работает, когда x - вектор - слишком поздно вечером прямо сейчас :)

> roundUpNice(0.0322)
[1] 0.04
> roundUpNice(3.22)
[1] 4
> roundUpNice(32.2)
[1] 40
> roundUpNice(42.2)
[1] 50
> roundUpNice(422.2)
[1] 500

[[РЕДАКТИРОВАТЬ]]

Если вопрос заключается в том, как округлить до указанного ближайшего значения (например, 10 или 100), то ответ Джеймса кажется наиболее подходящим. Моя версия позволяет вам взять любое значение и автоматически округлить его до разумно «хорошего» значения. Вот еще несколько хороших вариантов "хорошего" вектора, приведенного выше:1:10, c(1,5,10), seq(1, 10, 0.1)

Если у вас есть диапазон значений на вашем графике, например, [3996.225, 40001.893]тогда автоматический способ должен учитывать как размер диапазона, так и величину чисел. И, как заметил Хэдли , pretty()функция может быть такой, какой вы хотите.


1
Vectorize(roundUpNice)довольно быстро =) +1 Все равно.
mbq

Мне было интересно, есть ли способ отредактировать функцию roundUp до половины конечного результата, если исходное значение ниже? например, число от 101-500 округляется до 500, а числа 501-999 округляются до 1000? вместо округления всего до 1000?
helen.h

Просто измените красивый вектор:roundUpNice(501, nice=c(5, 10)) # 1000
Tommy

1
Просто предупреждение, поскольку вы работаете с логарифмами в своей функции, у меня было бы 2 случая, один, где x < 0и применить - xв журнале, прежде чем -возвращать обратно. Я бы также добавил исключение для ситуации, когдаx = 0
Йохан Обадиа

Эта функция очень хорошо сработала для моих нужд (чтобы установить довольно ограниченное значение по оси X графика)
Джун

132

В plyrбиблиотеке есть round_anyдовольно общая функция для всех видов округления. Например

library(plyr)
round_any(132.1, 10)               # returns 130
round_any(132.1, 10, f = ceiling)  # returns 140
round_any(132.1, 5, f = ceiling)   # returns 135

Для dplyrзамены см .: stackoverflow.com/a/46489816/435093
slhck

46

Функция округления в R придает особое значение параметру digits, если он отрицательный.

круглый (x, цифры = 0)

Округление до отрицательного числа цифр означает округление до степени десяти, поэтому, например, round (x, digits = -2) округляется до ближайшей сотни.

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

foo <- function(x)
{
    round(x+5,-1)
}

Результат выглядит следующим образом

foo(4)
[1] 10
foo(6.1)
[1] 10
foo(30.1)
[1] 40
foo(100.1)
[1] 110

2
Потрясающие! Ваш ответ должен быть отмечен как правильный!
Алессандро Якопсон

+1. Однако @Alessandro функции в моем ответе намного более универсальны: вы можете округлить ЛЮБОЕ число вверх ИЛИ до ЛЮБОГО интервала.
theforestecologist

@theforestecologist спасибо за подсказку! По личному вкусу я обычно предпочитаю встроенное решение языка, а не индивидуальное.
Алессандро Якопсон

27

Как насчет:

roundUp <- function(x,to=10)
{
  to*(x%/%to + as.logical(x%%to))
}

Который дает:

> roundUp(c(4,6.1,30.1,100.1))
[1]  10  10  40 110
> roundUp(4,5)
[1] 5
> roundUp(12,7)
[1] 14

1
@daroczig Вопрос немного сбивает с толку, я написал это, сосредоточив внимание на требовании «произвольного X», но ясно, что все ожидаемые значения не могут быть получены с помощью решения «одного раунда до ближайшего X». Кажется, что OP хочет создавать значения для оси, поэтому pretty, вероятно, это лучший вариант.
Джеймс

1
Очевидно, опоздал на эту вечеринку, но разве не to * ceiling(x / to)будет чище?
Джаред 04

27

Если вы добавите отрицательное число к аргументу-разряду round (), R округлит его до кратных 10, 100 и т. Д.

    round(9, digits = -1) 
    [1] 10    
    round(89, digits = -1) 
    [1] 90
    round(89, digits = -2) 
    [1] 100

17

Округлить ЛЮБОЕ число вверх / вниз до ЛЮБОГО интервала

Вы можете легко округлить числа до определенного интервала, используя оператор по модулю %% .

Функция:

round.choose <- function(x, roundTo, dir = 1) {
  if(dir == 1) {  ##ROUND UP
    x + (roundTo - x %% roundTo)
  } else {
    if(dir == 0) {  ##ROUND DOWN
      x - (x %% roundTo)
    }
  }
}

Примеры:

> round.choose(17,5,1)   #round 17 UP to the next 5th
[1] 20
> round.choose(17,5,0)   #round 17 DOWN to the next 5th
[1] 15
> round.choose(17,2,1)   #round 17 UP to the next even number
[1] 18
> round.choose(17,2,0)   #round 17 DOWN to the next even number
[1] 16

Как это работает:

Оператор по модулю %%определяет остаток от деления первого числа на второе. Добавление или вычитание этого интервала к интересующему вас числу может существенно «округлить» число до выбранного вами интервала.

> 7 + (5 - 7 %% 5)       #round UP to the nearest 5
[1] 10
> 7 + (10 - 7 %% 10)     #round UP to the nearest 10
[1] 10
> 7 + (2 - 7 %% 2)       #round UP to the nearest even number
[1] 8
> 7 + (100 - 7 %% 100)   #round UP to the nearest 100
[1] 100
> 7 + (4 - 7 %% 4)       #round UP to the nearest interval of 4
[1] 8
> 7 + (4.5 - 7 %% 4.5)   #round UP to the nearest interval of 4.5
[1] 9

> 7 - (7 %% 5)           #round DOWN to the nearest 5
[1] 5
> 7 - (7 %% 10)          #round DOWN to the nearest 10
[1] 0
> 7 - (7 %% 2)           #round DOWN to the nearest even number
[1] 6

Обновить:

Удобная версия с двумя аргументами:

rounder <- function(x,y) {
  if(y >= 0) { x + (y - x %% y)}
  else { x - (x %% abs(y))}
}

Положительные yзначения roundUp, отрицательные yзначения roundDown:

 # rounder(7, -4.5) = 4.5, while rounder(7, 4.5) = 9.

Или....

Функция, которая автоматически округляет ВВЕРХ или ВНИЗ на основе стандартных правил округления:

Round <- function(x,y) {
  if((y - x %% y) <= x %% y) { x + (y - x %% y)}
  else { x - (x %% y)}
}

Автоматически округляет в большую сторону, если xзначение находится >на полпути между последующими экземплярами значения округления y:

# Round(1.3,1) = 1 while Round(1.6,1) = 2
# Round(1.024,0.05) = 1 while Round(1.03,0.05) = 1.05

Меня спросили, как преобразовать Roundв VBA в Excel:Function ROUND(x,y) 'Function that automatically rounds UP or DOWN based on standard rounding rules. 'Automatically rounds up if the "x" value is > halfway between subsequent instances of the rounding value "y": If (y - (Evaluate("Mod(" & x & "," & y & ")"))) <= (Evaluate("Mod(" & x & "," & y & ")")) Then Ans = x + (y - (Evaluate("Mod(" & x & "," & y & ")"))) Else Ans = x - (Evaluate("Mod(" & x & "," & y & ")")) End If ROUND = Ans End Function
лесотехолог

@Abe, я не был уверен, скажете ли вы мой ответ или нет, так как я написал через 4 года после того, как вы спросили, но я думаю, что вы найдете мой ответ довольно элегантным и очень УНИВЕРСАЛЬНЫМ. Надеюсь, это поможет!
лесник

7

Что касается округления до кратности произвольного числа , например 10, то вот простая альтернатива ответу Джеймса.

Он работает для любого действительного числа, округляемого в большую сторону ( ), fromи любого реального положительного числа, округляемого в большую сторону до ( to):

> RoundUp <- function(from,to) ceiling(from/to)*to

Пример:

> RoundUp(-11,10)
[1] -10
> RoundUp(-0.1,10)
[1] 0
> RoundUp(0,10)
[1] 0
> RoundUp(8.9,10)
[1] 10
> RoundUp(135,10)
[1] 140

> RoundUp(from=c(1.3,2.4,5.6),to=1.1)  
[1] 2.2 3.3 6.6

2

Я думаю, ваш код отлично работает с небольшой модификацией:

foo <- function(x, round=10) ceiling(max(x+10^-9)/round + 1/round)*round

И ваши примеры запускаются:

> foo(4, round=1) == 5
[1] TRUE
> foo(6.1) == 10            #maybe 7 would be better
[1] TRUE
> foo(6.1, round=1) == 7    # you got 7
[1] TRUE
> foo(30.1) == 40
[1] TRUE
> foo(100.1) == 110
[1] TRUE
> # ALL in one:
> foo(c(4, 6.1, 30.1, 100))
[1] 110
> foo(c(4, 6.1, 30.1, 100), round=10)
[1] 110
> foo(c(4, 6.1, 30.1, 100), round=2.3)
[1] 101.2

Я изменил вашу функцию двумя способами:

  • добавлен второй аргумент (для вашего указанного X )
  • добавили небольшое значение (не =1e-09стесняйтесь изменять!) к, max(x)если вы хотите большее число

1

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

#Round 354 up to the nearest 100:
> X=100
> ceiling(354/X)*X
[1] 400

#Round 47 up to the nearest 30:
> Y=30
> ceiling(47/Y)*Y
[1] 60

Точно так же, если вы всегда хотите округлять в меньшую сторону , используйте floorфункцию. Если вы хотите просто округлить до ближайшего Z, используйте roundвместо этого.

> Z=5
> round(367.8/Z)*Z
[1] 370
> round(367.2/Z)*Z
[1] 365

0

Вы найдете обновленную версию ответа Томми, которая учитывает несколько случаев:

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

Ниже кода:

round.up.nice <- function(x, lower_bound = TRUE, nice_small=c(0,5,10), nice_big=c(1,2,3,4,5,6,7,8,9,10)) {
  if (abs(x) > 100) {
    nice = nice_big
  } else {
    nice = nice_small
  }
  if (lower_bound == TRUE) {
    if (x > 0) {
      return(10^floor(log10(x)) * nice[[max(which(x >= 10^floor(log10(x)) * nice))[[1]]]])
    } else if (x < 0) {
      return(- 10^floor(log10(-x)) * nice[[min(which(-x <= 10^floor(log10(-x)) * nice))[[1]]]])
    } else {
      return(0)
    }
  } else {
    if (x > 0) {
      return(10^floor(log10(x)) * nice[[min(which(x <= 10^floor(log10(x)) * nice))[[1]]]])
    } else if (x < 0) {
      return(- 10^floor(log10(-x)) * nice[[max(which(-x >= 10^floor(log10(-x)) * nice))[[1]]]])
    } else {
      return(0)
    }
  }
}

По умолчанию это округление в > round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
меньшую

Я думаю, что часть проблемы заключается в том, что nice_bigи nice_smallопределены в обратном порядке (если мы перевернем их в функции, round.up.nice(4.5)становится 4), но все равно округляется.
jessi

0

Я пробовал это без использования какой-либо внешней библиотеки или загадочных функций, и он работает!

Надеюсь, это кому-то поможет.

ceil <- function(val, multiple){
  div = val/multiple
  int_div = as.integer(div)
  return (int_div * multiple + ceiling(div - int_div) * multiple)
}

> ceil(2.1, 2.2)
[1] 2.2
> ceil(3, 2.2)
[1] 4.4
> ceil(5, 10)
[1] 10
> ceil(0, 10)
[1] 0
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.