Як округлити до найближчого 10 (або 100 або X)?


95

Я пишу функцію для побудови даних. Я хотів би вказати приємне кругле число для осі 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 дякую за інформацію, але я хочу кілька графіків, які мають однакові обмеження осі та галочки, і я не впевнений, як це допомагає.
Abe,

1
Ну, я тут якось намацую в темряві, оскільки я не знаю всіх передісторій. Ваш foo округлиться до найближчого X, якщо ви просто додасте ще один параметр X і заміните обидва десятих на X. Або ви можете скористатися фасетуванням.
Джоран

15
Ви шукаєте ?pretty?
hadley

Чому так foo(4)==5і ні 10?
Джеймс,

Відповіді:


65

Якщо ви просто хочете округлити до найближчого рівня 10, тоді просто визначте:

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

Це насправді також працює, коли х є вектором:

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

..але якщо ви хочете округлити до "приємного" числа, спочатку потрібно визначити, що таке "приємне" число. Далі ми можемо визначити "nice" як вектор із приємними базовими значеннями від 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]]]]
}

Вищевказане не працює, коли х є вектором - зараз пізно ввечері :)

> 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
Томмі

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 надає спеціальне значення параметру цифр, якщо він від’ємний.

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

Округлення до від'ємного числа цифр означає округлення до степеня десять, так, наприклад, округлення (x, цифри = -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". Здається, що ОП хоче отримати значення для осі, тому pretty, мабуть, найкращий варіант.
Джеймс,

1
Очевидно, що пізно на цю вечірку, але чи не to * ceiling(x / to)буде чистішим?
Джаред

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

Як це працює:

Оператор за модулем %%визначає залишок від ділення першого числа на 2-е. Додавання або віднімання цього інтервалу до вашої цікавої кількості може по суті "округлити" число до вибраного вами інтервалу.

> 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
jessi

Я думаю , що частина проблеми полягає в тому, що 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.