Comment arrondir au 10 (ou 100 ou X) le plus proche?


93

J'écris une fonction pour tracer des données. Je voudrais spécifier un joli nombre rond pour l'axe ymax qui est supérieur au maximum de l'ensemble de données.

Plus précisément, je voudrais une fonction fooqui effectue les opérations suivantes:

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

Je suis allé aussi loin que

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

pour arrondir à la dizaine la plus proche, mais cela ne fonctionne pas pour les intervalles d'arrondi arbitraires.

Y a-t-il une meilleure façon de faire cela dans R?


Le comportement par défaut de R lors du traçage est de définir les limites du tracé ~ 4% au-delà de la plage des données dans chaque direction. Si cela ne vous satisfait pas, écrivez peut-être simplement quelque chose qui sort d'un% supérieur ou inférieur?
joran

@joran merci pour l'info, mais je veux plusieurs parcelles qui ont toutes les mêmes limites d'axe et les mêmes graduations et je ne sais pas comment cela aide.
Abe

1
Eh bien, je suis en quelque sorte à tâtons dans le noir ici, car je ne connais pas tout le contexte. Votre toto arrondira au X le plus proche si vous ajoutez simplement un autre paramètre X et remplacez les deux 10 par X. Ou vous pouvez utiliser la facette.
joran

15
Cherchez-vous ?pretty?
hadley

Pourquoi foo(4)==5et non 10?
James

Réponses:


64

Si vous souhaitez simplement arrondir à la puissance 10 la plus proche, définissez simplement:

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

Cela fonctionne également lorsque x est un vecteur:

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

..mais si vous voulez arrondir à un "joli" nombre, vous devez d'abord définir ce qu'est un "joli" nombre. Ce qui suit nous permet de définir «gentil» comme un vecteur avec de jolies valeurs de base de 1 à 10. La valeur par défaut est définie sur les nombres pairs plus 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]]]]
}

Ce qui précède ne fonctionne pas lorsque x est un vecteur - trop tard dans la soirée en ce moment :)

> 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

[[ÉDITER]]

Si la question est de savoir comment arrondir à une valeur spécifiée la plus proche (comme 10 ou 100), alors la réponse de James semble la plus appropriée. Ma version vous permet de prendre n'importe quelle valeur et de l'arrondir automatiquement à une valeur raisonnablement "agréable". Voici quelques autres bons choix du "joli" vecteur ci-dessus:1:10, c(1,5,10), seq(1, 10, 0.1)

Si vous avez une plage de valeurs dans votre tracé, par exemple, [3996.225, 40001.893]la méthode automatique doit prendre en compte à la fois la taille de la plage et l'amplitude des nombres. Et comme l'a noté Hadley , la pretty()fonction peut être ce que vous voulez.


1
Vectorize(roundUpNice)est assez rapide =) +1 Quoi qu'il en soit.
mbq

Je me demandais s'il existe un moyen de modifier la fonction roundUp à la moitié du résultat final si la valeur d'origine est inférieure? par exemple, un nombre entre 101-500 arrondirait à 500 et les nombres 501-999 arrondiraient à 1000? au lieu d'arrondir à 1000?
helen.h

Changez simplement le beau vecteur:roundUpNice(501, nice=c(5, 10)) # 1000
Tommy

1
Juste un avertissement puisque vous travaillez avec des logarithmes dans votre fonction, j'aurais eu 2 cas, un où x < 0et appliquer - xdans le journal avant de remettre le -dos. J'ajouterais également une exception pour la situation oùx = 0
Yohan Obadia

La jolie fonction a très bien fonctionné pour mes besoins (pour définir une jolie limite sur l'axe X d'un graphique)
Joon

132

La plyrbibliothèque a une fonction round_anyassez générique pour effectuer toutes sortes d'arrondis. Par exemple

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

Pour un dplyrremplacement, voir: stackoverflow.com/a/46489816/435093
slhck

46

La fonction d'arrondi dans R attribue une signification spéciale au paramètre chiffres s'il est négatif.

round (x, chiffres = 0)

Arrondir à un nombre négatif de chiffres signifie arrondir à une puissance de dix, donc par exemple arrondir (x, chiffres = -2) arrondit à la centaine la plus proche.

Cela signifie qu'une fonction comme celle-ci est assez proche de ce que vous demandez.

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

La sortie ressemble à ce qui suit

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

2
Impressionnant! Votre réponse doit être marquée comme la bonne!
Alessandro Jacopson

+1. Cependant, @Alessandro les fonctions de ma réponse sont beaucoup plus polyvalentes: vous pouvez arrondir TOUT nombre vers le haut OU vers le bas à N'IMPORTE QUEL intervalle.
theforestecologist

@theforestecologist merci pour l'indice! En tant que goût personnel, je préfère généralement une solution intégrée au langage plutôt qu'une solution personnalisée.
Alessandro Jacopson

27

Que diriez-vous:

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

Qui donne:

> 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 question est un peu déroutante, j'ai écrit ceci en se concentrant sur l'exigence "arbitraire X", mais clairement toutes les valeurs attendues ne pouvaient pas être produites par "un seul arrondi au X le plus proche". Il semble que l'OP veuille produire des valeurs pour un axe, c'est donc prettyprobablement la meilleure option.
James

1
Évidemment en retard à cette fête, mais ne serait-il pas to * ceiling(x / to)plus propre?
jared

25

Si vous ajoutez un nombre négatif à l'argument chiffres de round (), R l'arrondira aux multiples de 10, 100 etc.

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

17

Arrondir N'IMPORTE QUEL nombre haut / bas à N'IMPORTE QUEL intervalle

Vous pouvez facilement arrondir les nombres à un intervalle spécifique à l'aide de l' opérateur modulo %% .

La fonction:

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

Exemples:

> 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

Comment ça fonctionne:

L'opérateur modulo %%détermine le reste de la division du premier nombre par le deuxième. Ajouter ou soustraire cet intervalle à votre nombre d'intérêt peut essentiellement «arrondir» le nombre à un intervalle de votre choix.

> 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

Mettre à jour:

La version pratique à 2 arguments:

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

yValeurs positives roundUp, tandis que yvaleurs négatives roundDown:

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

Ou....

Fonction qui arrondit automatiquement HAUT ou BAS selon les règles d'arrondi standard:

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

Arrondit automatiquement si la xvaleur est à >mi - chemin entre les instances suivantes de la valeur d'arrondi 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

On m'a demandé comment convertir Rounden VBA dans 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, je ne savais pas si vous disiez ma réponse ou non depuis que j'ai posté 4 ans après que vous ayez demandé, mais je pense que vous pourriez trouver ma réponse assez élégante et très POLYVALENTE. J'espère que ça aide!
theforestecologist

7

En ce qui concerne l' arrondissement à la multiplicité d'un nombre arbitraire , par exemple 10, voici une alternative simple à la réponse de James.

Cela fonctionne pour tout nombre réel arrondi ( from) et tout nombre réel positif arrondi à ( to):

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

Exemple:

> 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

Je pense que votre code fonctionne très bien avec une petite modification:

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

Et vos exemples courent:

> 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

J'ai modifié votre fonction de deux manières:

  • ajouté un deuxième argument (pour votre X spécifié )
  • a ajouté une petite valeur ( =1e-09, n'hésitez pas à modifier!) à la max(x)si vous voulez un plus grand nombre

1

Si vous voulez toujours arrondir un nombre jusqu'à la plus proche X, vous pouvez utiliser la ceilingfonction:

#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

De même, si vous voulez toujours à arrondir vers le bas , utilisez la floorfonction. Si vous voulez simplement arrondir au Z le plus proche, utilisez roundplutôt.

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

0

Vous trouverez une version améliorée de la réponse de Tommy qui prend en compte plusieurs cas:

  • Choisir entre une borne inférieure ou supérieure
  • Prise en compte des valeurs négatives et nulles
  • deux échelles différentes au cas où vous voudriez que la fonction arrondisse différemment les petits et les grands nombres. Exemple: 4 serait arrondi à 0 tandis que 400 serait arrondi à 400.

Sous le code:

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

La sortie par défaut de ceci est un arrondi vers le bas:> round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
jessi

Je pense qu'une partie du problème est que nice_biget nice_smallsont définis à l'envers, (si nous les retournons dans la fonction, round.up.nice(4.5)devient 4) mais il s'arrondit toujours.
jessi

0

J'ai essayé cela sans utiliser de bibliothèque externe ou de fonctionnalités cryptiques et cela fonctionne!

J'espère que ça aide quelqu'un.

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.