Wie runde ich auf die nächsten 10 (oder 100 oder X) auf?


89

Ich schreibe eine Funktion zum Zeichnen von Daten. Ich möchte eine schöne runde Zahl für die y-Achse angeben max, die größer als das Maximum des Datensatzes ist.

Insbesondere möchte ich eine Funktion foo, die Folgendes ausführt:

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

Ich bin so weit gekommen

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

zum Runden auf die nächsten 10, dies funktioniert jedoch nicht für beliebige Rundungsintervalle.

Gibt es einen besseren Weg, dies in R zu tun?


Das R-Standardverhalten beim Plotten besteht darin, die Plotgrenzen ~ 4% außerhalb des Datenbereichs in jeder Richtung festzulegen. Wenn dies für Sie nicht zufriedenstellend ist, schreiben Sie vielleicht einfach etwas, das um einen höheren oder niedrigeren Prozentsatz ausgeht?
Joran

@joran danke für die Info, aber ich möchte mehrere Diagramme, die alle die gleichen Achsengrenzen und Ticks haben, und ich bin nicht sicher, wie dies hilft.
Abe

1
Nun, ich tappe hier im Dunkeln herum, da ich nicht den ganzen Hintergrund kenne. Ihr foo wird auf das nächste X aufgerundet, wenn Sie nur einen weiteren Parameter X hinzufügen und beide Zehner durch X ersetzen. Oder Sie könnten Facettierung verwenden.
Joran

15
Suchen Sie ?pretty?
Hadley

Warum ist foo(4)==5und nicht 10?
James

Antworten:


64

Wenn Sie nur auf die nächste Potenz von 10 aufrunden möchten, definieren Sie einfach:

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

Dies funktioniert tatsächlich auch, wenn x ein Vektor ist:

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

..aber wenn Sie auf eine "schöne" Zahl runden möchten, müssen Sie zuerst definieren, was eine "schöne" Zahl ist. Im Folgenden können Sie "nice" als einen Vektor mit netten Basiswerten von 1 bis 10 definieren. Der Standardwert ist die geraden Zahlen 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]]]]
}

Das obige funktioniert nicht, wenn x ein Vektor ist - momentan zu spät am Abend :)

> 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

[[BEARBEITEN]]

Wenn die Frage lautet, wie auf einen bestimmten nächsten Wert (wie 10 oder 100) gerundet werden soll, erscheint die Antwort von James am besten geeignet. In meiner Version können Sie einen beliebigen Wert nehmen und ihn automatisch auf einen einigermaßen "netten" Wert runden. Einige andere gute Möglichkeiten des obigen "schönen" Vektors sind:1:10, c(1,5,10), seq(1, 10, 0.1)

Wenn Sie beispielsweise einen Wertebereich in Ihrem Diagramm haben, [3996.225, 40001.893]sollte der automatische Weg sowohl die Größe des Bereichs als auch die Größe der Zahlen berücksichtigen. Und wie Hadley bemerkt hat , pretty()könnte die Funktion genau das sein, was Sie wollen.


1
Vectorize(roundUpNice)ist ziemlich schnell =) +1 Wie auch immer.
mbq

Ich habe mich gefragt, ob es eine Möglichkeit gibt, die roundUp-Funktion auf die Hälfte des Endergebnisses zu bearbeiten, wenn der ursprüngliche Wert niedriger ist. Zum Beispiel würde eine Zahl zwischen 101-500 auf 500 und Zahlen 501-999 auf 1000 aufrunden. statt auf 1000 zu runden?
helen.h

Ändern Sie einfach den schönen Vektor:roundUpNice(501, nice=c(5, 10)) # 1000
Tommy

1
Nur eine Warnung, da Sie in Ihrer Funktion mit Logarithmen arbeiten, hätte ich 2 Fälle, einen wo x < 0und - xim Protokoll angewendet, bevor Sie den -Rücken setzen. Ich würde auch eine Ausnahme für die Situation hinzufügen, in derx = 0
Yohan Obadia

Die hübsche Funktion funktionierte sehr gut für meine Bedürfnisse (um eine hübsche Grenze für die X-Achse eines Graphen
Joon

131

Die plyrBibliothek hat eine Funktion round_any, die ziemlich allgemein ist, um alle Arten von Rundungen durchzuführen. Beispielsweise

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

Einen dplyrErsatz finden Sie unter: stackoverflow.com/a/46489816/435093
slhck

46

Die Rundungsfunktion in R weist dem Ziffernparameter eine besondere Bedeutung zu, wenn er negativ ist.

rund (x, Ziffern = 0)

Das Runden auf eine negative Anzahl von Ziffern bedeutet das Runden auf eine Zehnerpotenz, also rundet beispielsweise (x, Ziffern = -2) auf die nächsten hundert.

Dies bedeutet, dass eine Funktion wie die folgende dem , was Sie verlangen, ziemlich nahe kommt .

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

Die Ausgabe sieht wie folgt aus

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

2
Genial! Ihre Antwort sollte als die richtige markiert sein!
Alessandro Jacopson

+1. Allerdings @Alessandro die Funktionen in meiner Antwort sind wesentlich vielseitiger: Sie abrunden können ANY Zahl nach oben oder nach unten zu jedem Intervall.
Theforestecologist

@ theforestecologist danke für den Hinweis! Aus persönlichen Gründen bevorzuge ich normalerweise eine in die Sprache integrierte Lösung anstelle einer benutzerdefinierten.
Alessandro Jacopson

27

Wie wäre es mit:

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

Welches gibt:

> 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 Die Frage ist etwas verwirrend. Ich habe dies geschrieben und mich dabei auf die "willkürliche X" -Anforderung konzentriert, aber es ist klar, dass nicht alle erwarteten Werte durch "eine einzige Runde bis zur nächsten X" -Lösung erzeugt werden konnten. Es scheint, dass das OP Werte für eine Achse erzeugen möchte, daher prettyist dies wahrscheinlich die beste Option.
James

1
Offensichtlich zu spät zu dieser Party, aber wäre es nicht to * ceiling(x / to)sauberer?
Jared

25

Wenn Sie dem Ziffernargument von round () eine negative Zahl hinzufügen, rundet R diese auf die Vielfachen von 10, 100 usw.

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

17

Runden Sie JEDE Zahl nach oben / unten auf ein beliebiges Intervall

Mit dem Modulo-Operator können Sie Zahlen einfach auf ein bestimmtes Intervall runden %%.

Die Funktion:

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

Beispiele:

> 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

Wie es funktioniert:

Der Modulo-Operator %%bestimmt den Rest der Division der ersten Zahl durch die zweite. Das Hinzufügen oder Subtrahieren dieses Intervalls zu Ihrer interessierenden Anzahl kann die Zahl im Wesentlichen auf ein Intervall Ihrer Wahl "runden".

> 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

Aktualisieren:

Die praktische Version mit zwei Argumenten:

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

Positive yWerte roundUp, während negative yWerte roundDown:

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

Oder....

Funktion, die UP oder DOWN basierend auf Standardrundungsregeln automatisch rundet :

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

Rundet automatisch auf, wenn der xWert >zwischen den nachfolgenden Instanzen des Rundungswerts liegt 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

Ich wurde gefragt, wie man Roundin Excel zu VBA konvertiert :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 Ich war mir nicht sicher, ob du jemals meine Antwort sagst oder nicht, seit ich 4 Jahre nach deiner Anfrage gepostet habe, aber ich denke, du findest meine Antwort vielleicht ziemlich elegant und sehr VIELSEITIG. Ich hoffe es hilft!
Theforestecologist

7

In Bezug auf die Aufrundung auf die Vielzahl einer beliebigen Zahl , z. B. 10, ist hier eine einfache Alternative zu James 'Antwort.

Es funktioniert für jede reelle Zahl, die aufgerundet wird ( from) und jede echte positive Zahl, die auf ( to) aufgerundet wird :

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

Beispiel:

> 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

Ich denke, Ihr Code funktioniert mit einer kleinen Modifikation einfach großartig:

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

Und Ihre Beispiele lauten:

> 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

Ich habe Ihre Funktion auf zwei Arten geändert:

  • zweites Argument hinzugefügt (für Ihr angegebenes X )
  • Wenn Sie eine größere Zahl wünschen, haben Sie einen kleinen Wert hinzugefügt (Sie =1e-09können ihn gerne ändern!)max(x)

1

Wenn Sie immer eine Zahl runden möchten bis zur nächsten X, können Sie die Verwendung ceilingFunktion:

#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

Und falls Sie immer rund wollen nach unten , verwenden Sie die floorFunktion. Wenn Sie einfach auf das nächste Z auf- oder abrunden möchten, verwenden Sie roundstattdessen.

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

0

Sie finden eine aktualisierte Version von Tommys Antwort , die mehrere Fälle berücksichtigt:

  • Wahl zwischen Unter- und Obergrenze
  • Unter Berücksichtigung von negativen und Nullwerten
  • zwei verschiedene schöne Skalen für den Fall, dass die Funktion unterschiedliche kleine und große Zahlen runden soll. Beispiel: 4 würde auf 0 gerundet, während 400 auf 400 gerundet würde.

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

Die Standardausgabe davon ist eine Abrundung:> round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
Jessi

Ich denke, ein Teil des Problems ist, dass nice_bigund nice_smallrückwärts definiert werden (wenn wir sie in der Funktion umdrehen, round.up.nice(4.5)wird 4), aber es rundet immer noch ab.
Jessi

0

Ich habe dies versucht, ohne eine externe Bibliothek oder kryptische Funktionen zu verwenden, und es funktioniert!

Hoffe es hilft jemandem.

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.