Come arrotondare per eccesso al 10 (o 100 o X) più vicino?


93

Sto scrivendo una funzione per tracciare i dati. Vorrei specificare un bel numero tondo per l'asse y maxche è maggiore del massimo del set di dati.

Nello specifico, vorrei una funzione fooche svolga quanto segue:

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

Sono arrivato fino a

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

per arrotondare al 10 più vicino, ma questo non funziona per intervalli di arrotondamento arbitrari.

C'è un modo migliore per farlo in R?


Il comportamento predefinito di R durante la stampa è di impostare i limiti del grafico ~ 4% oltre l'intervallo dei dati in ciascuna direzione. Se questo non ti soddisfa, forse scrivi qualcosa che esce di una% superiore o inferiore?
joran

@jorano grazie per le informazioni, ma voglio più grafici che hanno tutti gli stessi limiti e tick degli assi e non sono sicuro di come questo aiuti.
Abe

1
Bene, qui sto brancolando nell'oscurità, dato che non conosco tutto il retroscena. Il tuo pippo verrà arrotondato per eccesso alla X più vicina se aggiungi un altro parametro X e sostituisci entrambi i 10 con X. Oppure potresti usare la sfaccettatura.
joran

15
Stai cercando ?pretty?
Hadley

Perché lo è foo(4)==5e no 10?
James

Risposte:


64

Se vuoi solo arrotondare per eccesso alla potenza più vicina di 10, allora definisci semplicemente:

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

In realtà funziona anche quando x è un vettore:

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

..ma se vuoi arrotondare a un numero "carino", devi prima definire cosa sia un numero "carino". Quanto segue ci permette di definire "bello" come un vettore con bei valori di base da 1 a 10. Il valore predefinito è impostato sui numeri pari più 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]]]]
}

Quanto sopra non funziona quando x è un vettore - è troppo tardi la sera in questo momento :)

> 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

[[MODIFICARE]]

Se la domanda è come arrotondare a un valore più vicino specificato (come 10 o 100), la risposta di James sembra la più appropriata. La mia versione ti consente di prendere qualsiasi valore e di arrotondarlo automaticamente a un valore ragionevolmente "carino". Alcune altre buone scelte del vettore "carino" sopra sono:1:10, c(1,5,10), seq(1, 10, 0.1)

Se si dispone di un intervallo di valori nel grafico, ad esempio, [3996.225, 40001.893]il modo automatico dovrebbe prendere in considerazione sia la dimensione dell'intervallo che la grandezza dei numeri. E come notato da Hadley , la pretty()funzione potrebbe essere ciò che desideri.


1
Vectorize(roundUpNice)è abbastanza veloce =) +1 Comunque.
MBQ

Mi chiedevo se esiste un modo per modificare la funzione roundUp a metà del risultato finale se il valore originale è inferiore? per esempio un numero compreso tra 101-500 arrotonderebbe a 500 e i numeri 501-999 arrotonderebbero a 1000? invece di arrotondare tutto a 1000?
helen.h

Basta cambiare il bel vettore:roundUpNice(501, nice=c(5, 10)) # 1000
Tommy

1
Solo un avvertimento poiché lavori con i logaritmi nella tua funzione, avrei avuto 2 casi, uno in cui x < 0e applicato - xnel registro prima di mettere il -retro. Aggiungerei anche un'eccezione per la situazione in cuix = 0
Yohan Obadia

La funzione pretty ha funzionato molto bene per le mie esigenze (impostare un bel limite sull'asse X di un grafico)
Joon

132

La plyrlibreria ha una funzione round_anypiuttosto generica per eseguire tutti i tipi di arrotondamento. Per esempio

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

Per una dplyrsostituzione, vedi: stackoverflow.com/a/46489816/435093
slhck

46

La funzione round in R assegna un significato speciale al parametro digit se è negativo.

round (x, cifre = 0)

Arrotondare a un numero negativo di cifre significa arrotondare a una potenza di dieci, quindi ad esempio round (x, digits = -2) arrotonda al centinaio più vicino.

Ciò significa che una funzione come la seguente si avvicina molto a ciò che stai chiedendo.

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

L'output è simile al seguente

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

2
Eccezionale! La tua risposta dovrebbe essere contrassegnata come quella giusta!
Alessandro Jacopson

+1. Tuttavia, @Alessandro le funzioni nella mia risposta sono molto più versatili: puoi arrotondare QUALSIASI numero per eccesso O per difetto a QUALSIASI intervallo.
theforestecologist

@theforestecologist grazie per il suggerimento! Come gusto personale, di solito preferisco una soluzione integrata nel linguaggio piuttosto che una personalizzata.
Alessandro Jacopson

27

Che ne dite di:

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

Che dà:

> 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 La domanda è un po 'confusa, l'ho scritta concentrandomi sul requisito "X arbitrario", ma chiaramente tutti i valori attesi non potevano essere prodotti da "un singolo arrotondamento fino alla X più vicina". Sembra che l'OP voglia produrre valori per un asse, quindi prettyè probabilmente l'opzione migliore.
James,

1
Ovviamente in ritardo a questa festa, ma non sarebbe più to * ceiling(x / to)pulito?
jared

25

Se aggiungi un numero negativo all'argomento cifre di round (), R lo arrotonderà ai multipli di 10, 100 ecc.

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

17

Arrotonda QUALSIASI numero su / Giù a QUALSIASI intervallo

È possibile arrotondare facilmente i numeri a un intervallo specifico utilizzando l' operatore modulo %% .

La funzione:

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)
    }
  }
}

Esempi:

> 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

Come funziona:

L'operatore modulo %%determina il resto della divisione del primo numero per il secondo. L'aggiunta o la sottrazione di questo intervallo al numero di interesse può essenzialmente "arrotondare" il numero a un intervallo di tua scelta.

> 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

Aggiornare:

La comoda versione a 2 argomenti:

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

yValori positivi roundUp, mentre yvalori negativi roundDown:

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

O....

Funzione che arrotonda automaticamente UP o DOWN in base alle regole di arrotondamento standard:

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

Arrotonda automaticamente per eccesso se il xvalore è a >metà tra le istanze successive del valore di arrotondamento 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

Mi è stato chiesto come convertire Roundin VBA in 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
theforestecologist

@Abe non ero sicuro che tu dicessi o meno la mia risposta da quando ho postato 4 anni dopo che me l'hai chiesto, ma penso che potresti trovare la mia risposta abbastanza elegante e molto VERSATILE. Spero che sia d'aiuto!
theforestecologist

7

Per quanto riguarda l' arrotondamento alla molteplicità di un numero arbitrario , ad esempio 10, ecco una semplice alternativa alla risposta di James.

Funziona per qualsiasi numero reale arrotondato per eccesso ( from) e qualsiasi numero positivo reale arrotondato per eccesso a ( to):

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

Esempio:

> 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

Penso che il tuo codice funzioni alla grande con una piccola modifica:

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

E i tuoi esempi corrono:

> 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

Ho modificato la tua funzione in due modi:

  • aggiunto secondo argomento (per la X specificata )
  • aggiunto un piccolo valore ( =1e-09, sentiti libero di modificare!) al max(x)se vuoi un numero più grande

1

Se hai sempre voglia di arrotondare un numero fino al X più vicino, è possibile utilizzare la ceilingfunzione di:

#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

Allo stesso modo, se vuoi sempre arrotondare per difetto , usa la floorfunzione. Se vuoi semplicemente arrotondare per eccesso o per difetto alla Z più vicina, usa roundinvece.

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

0

Troverai una versione aggiornata della risposta di Tommy che tiene conto di diversi casi:

  • Scegliere tra limite inferiore o superiore
  • Tenendo conto dei valori negativi e zero
  • due scale gradevoli diverse nel caso in cui si desideri che la funzione arrotondi in modo diverso numeri piccoli e grandi. Esempio: 4 sarebbe arrotondato a 0 mentre 400 sarebbe arrotondato a 400.

Di seguito il codice:

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)
    }
  }
}

L'output predefinito di questo è un arrotondamento per difetto:> round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
jessi

Penso che parte del problema sia che nice_bige nice_smallsono definiti al contrario, (se li capovolgiamo nella funzione, round.up.nice(4.5)diventa 4) ma si arrotonda comunque per difetto.
jessi

0

L'ho provato senza utilizzare alcuna libreria esterna o funzionalità criptiche e funziona!

Spero che aiuti qualcuno.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.