Bagaimana cara membulatkan ke 10 (atau 100 atau X) terdekat?

95

Saya menulis fungsi untuk memplot data. Saya ingin menentukan angka bulat yang bagus untuk sumbu y maxyang lebih besar dari maks dataset.

Secara khusus, saya menginginkan fungsi fooyang melakukan hal berikut:

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

Saya sudah sejauh ini

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

untuk pembulatan ke 10 terdekat, tetapi ini tidak berfungsi untuk interval pembulatan arbitrer.

Apakah ada cara yang lebih baik untuk melakukan ini di R?

Abe
sumber
Perilaku default R saat membuat plot adalah mengatur batas plot ~ 4% di luar rentang data di setiap arah. Jika ini tidak memuaskan Anda, mungkin tuliskan saja sesuatu yang keluar dengan% lebih tinggi atau lebih rendah?
joran
@joran terima kasih atas infonya, tapi saya ingin banyak plot yang semuanya memiliki batas sumbu dan kutu yang sama dan saya tidak yakin bagaimana ini membantu.
Abe
1
Nah, saya seperti meraba-raba dalam kegelapan di sini, karena saya tidak tahu semua latar belakangnya. Foo Anda akan membulatkan ke atas ke X terdekat jika Anda menambahkan parameter X lain dan mengganti 10 dengan X. Atau Anda dapat menggunakan faceting.
joran
15
Apakah kamu sedang mencari ?pretty?
hadley
Mengapa foo(4)==5dan tidak 10?
James

Jawaban:

65

Jika Anda hanya ingin membulatkan ke pangkat 10 terdekat, maka tentukan saja:

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

Ini sebenarnya juga berfungsi ketika x adalah vektor:

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

..tetapi jika Anda ingin membulatkan ke angka yang "bagus", Anda harus terlebih dahulu menentukan apa itu angka yang "bagus". Berikut ini memungkinkan kita mendefinisikan "bagus" sebagai vektor dengan nilai dasar yang bagus dari 1 sampai 10. Defaultnya disetel ke bilangan genap ditambah 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]]]]
}

Di atas tidak berfungsi ketika x adalah vektor - sekarang sudah larut malam :)

> 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

[[EDIT]]

Jika pertanyaannya adalah bagaimana membulatkan ke nilai terdekat yang ditentukan (seperti 10 atau 100), maka jawaban Yakobus tampaknya paling tepat. Versi saya memungkinkan Anda mengambil nilai apa pun dan secara otomatis membulatkannya ke nilai yang cukup "bagus". Beberapa pilihan bagus lainnya dari vektor "bagus" di atas adalah:1:10, c(1,5,10), seq(1, 10, 0.1)

Jika Anda memiliki rentang nilai dalam plot Anda, misalnya [3996.225, 40001.893], cara otomatis harus mempertimbangkan ukuran rentang dan besarnya angkanya. Dan seperti yang dicatat oleh Hadley , pretty()fungsinya mungkin sesuai dengan keinginan Anda.

Tommy
sumber
1
Vectorize(roundUpNice)cukup cepat =) +1 Pokoknya.
mbq
Saya bertanya-tanya apakah ada cara untuk mengedit fungsi RoundUp menjadi setengah hasil akhir jika nilai aslinya lebih rendah? misalnya angka antara 101-500 akan dibulatkan menjadi 500 dan angka 501-999 akan dibulatkan menjadi 1000? bukannya semua pembulatan ke 1000?
helen.h
Ubah saja vektor yang bagus:roundUpNice(501, nice=c(5, 10)) # 1000
Tommy
1
Hanya peringatan karena Anda bekerja dengan logaritma dalam fungsi Anda, saya akan memiliki 2 kasus, satu di mana x < 0dan menerapkannya - xdi log sebelum meletakkan -kembali. Saya juga akan menambahkan pengecualian untuk situasi di manax = 0
Yohan Obadia
Fungsi cantik bekerja dengan sangat baik untuk kebutuhan saya (untuk menetapkan batas yang cukup pada sumbu X grafik)
Joon
132

The plyrperpustakaan memiliki fungsi round_anyyang cukup umum untuk melakukan segala macam pembulatan. Sebagai contoh

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
Ramnath
sumber
Untuk dplyrpenggantinya, lihat: stackoverflow.com/a/46489816/435093
slhck
46

Fungsi bulat di R memberikan arti khusus ke parameter digit jika negatif.

bulat (x, digit = 0)

Membulatkan ke angka negatif angka berarti membulatkan ke pangkat sepuluh, jadi misalnya putaran (x, digit = -2) dibulatkan ke seratus terdekat.

Ini berarti fungsi seperti berikut ini mendekati apa yang Anda minta.

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

Outputnya terlihat seperti berikut

foo(4)
[1] 10
foo(6.1)
[1] 10
foo(30.1)
[1] 40
foo(100.1)
[1] 110
Nol
sumber
2
Hebat! Jawaban Anda harus ditandai sebagai jawaban yang benar!
Alessandro Jacopson
+1. Namun, fungsi @Alessandro dalam jawaban saya jauh lebih fleksibel: Anda dapat membulatkan angka APA PUN ke atas ATAU turun ke interval APA PUN .
ahli kehutanan
@theforestecologist terima kasih atas petunjuknya! Sebagai selera pribadi, saya biasanya lebih memilih solusi bawaan bahasa daripada solusi khusus.
Alessandro Jacopson
27

Bagaimana tentang:

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

Pemberian yang mana:

> roundUp(c(4,6.1,30.1,100.1))
[1]  10  10  40 110
> roundUp(4,5)
[1] 5
> roundUp(12,7)
[1] 14
James
sumber
1
@daroczig Pertanyaannya agak membingungkan, saya menulis ini dengan fokus pada persyaratan "sembarang X", tetapi jelas semua nilai yang diharapkan tidak dapat dihasilkan oleh solusi "satu putaran ke X terdekat". Tampaknya OP ingin menghasilkan nilai untuk sumbu, jadi prettymungkin pilihan terbaik.
James
1
Jelas terlambat ke pesta ini, tapi bukankah to * ceiling(x / to)lebih bersih?
jared
27

Jika Anda menambahkan angka negatif ke argumen digit round (), R akan membulatkannya ke kelipatan 10, 100 dll.

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

Bulatkan angka APA PUN Naik / Turun hingga interval APA PUN

Anda dapat dengan mudah membulatkan angka ke interval tertentu menggunakan operator modulo %% .

Fungsinya:

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

Contoh:

> 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

Bagaimana itu bekerja:

Operator modulo %%menentukan sisa pembagian bilangan pertama dengan bilangan kedua. Menambah atau mengurangi interval ini dengan jumlah minat Anda pada dasarnya dapat 'membulatkan' angka tersebut ke interval yang Anda pilih.

> 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

Memperbarui:

Versi 2-argumen yang nyaman:

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

yNilai positif roundUp, sedangkan ynilai negatif roundDown:

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

Atau....

Fungsi yang secara otomatis membulatkan KE ATAS atau KE BAWAH berdasarkan aturan pembulatan standar:

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

Secara otomatis dibulatkan jika xnilainya berada di >tengah antara contoh nilai pembulatan berikutnya 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
ahli kehutanan
sumber
Saya ditanya bagaimana mengubah Roundke VBA di 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
ahli kehutanan
@Abe Saya tidak yakin apakah Anda pernah mengatakan jawaban saya atau tidak sejak saya memposting 4 tahun setelah Anda bertanya, tetapi saya pikir Anda mungkin menemukan jawaban saya cukup elegan dan sangat VERSATILE. Semoga membantu!
theforestecologist
7

Mengenai pembulatan ke kelipatan angka acak , misalnya 10, berikut adalah alternatif sederhana untuk jawaban Yakobus.

Ini berfungsi untuk bilangan real apa pun yang dibulatkan ( from) dan bilangan positif nyata apa pun yang dibulatkan ke ( to):

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

Contoh:

> 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
hanna
sumber
2

Saya pikir kode Anda hanya berfungsi dengan baik dengan sedikit modifikasi:

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

Dan contoh Anda berjalan:

> 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

Saya mengubah fungsi Anda dengan dua cara:

  • menambahkan argumen kedua (untuk X yang Anda tentukan )
  • menambahkan nilai kecil ( =1e-09, silakan memodifikasi!) ke max(x)jika Anda menginginkan angka yang lebih besar
daroczig
sumber
1

Jika Anda selalu ingin membulatkan angka ke atas ke X terdekat, Anda dapat menggunakan ceilingfungsi:

#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

Demikian pula, jika Anda selalu ingin membulatkan ke bawah , gunakan floorfungsinya. Jika Anda ingin membulatkan ke atas atau ke bawah ke Z terdekat, gunakan roundsaja.

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

Anda akan menemukan versi yang ditingkatkan dari jawaban Tommy yang memperhitungkan beberapa kasus:

  • Memilih antara batas bawah atau batas atas
  • Memperhatikan nilai negatif dan nol
  • dua skala bagus yang berbeda jika Anda ingin fungsi membulatkan angka kecil dan besar yang berbeda. Contoh: 4 akan dibulatkan menjadi 0 sedangkan 400 akan dibulatkan menjadi 400.

Di bawah kode:

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)
    }
  }
}
Yohan Obadia
sumber
Keluaran default dari ini adalah pembulatan ke bawah:> round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
jessi
Saya pikir bagian dari masalahnya adalah itu nice_bigdan nice_smalldidefinisikan mundur, (jika kita membalik fungsi mereka, round.up.nice(4.5)menjadi 4) tetapi masih membulatkan ke bawah.
jessi
0

Saya mencoba ini tanpa menggunakan pustaka eksternal atau fitur samar dan berhasil!

Semoga bisa membantu seseorang.

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
Rahul Chawla
sumber