Làm thế nào để làm tròn đến 10 (hoặc 100 hoặc X) gần nhất?


93

Tôi đang viết một hàm để vẽ dữ liệu. Tôi muốn chỉ định một số tròn đẹp cho trục y maxlớn hơn số tối đa của tập dữ liệu.

Cụ thể, tôi muốn một hàm foothực hiện như sau:

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

Tôi đã đi xa như

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

để làm tròn đến 10 gần nhất, nhưng điều này không hoạt động đối với các khoảng làm tròn tùy ý.

Có cách nào tốt hơn để làm điều này trong R không?


Hành vi mặc định của R khi vẽ biểu đồ là đặt giới hạn biểu đồ ~ 4% ngoài phạm vi của dữ liệu theo mỗi hướng. Nếu điều này không làm bạn hài lòng, có thể chỉ cần viết một cái gì đó có tỷ lệ% cao hơn hoặc thấp hơn?
joran

@joran cảm ơn bạn đã cung cấp thông tin, nhưng tôi muốn nhiều mảnh đất đều có giới hạn trục và dấu tích giống nhau và tôi không chắc điều này sẽ hữu ích như thế nào.
Abe

1
Chà, tôi đang mò mẫm trong bóng tối ở đây, vì tôi không biết tất cả lý lịch. Foo của bạn sẽ làm tròn đến X gần nhất nếu bạn chỉ cần thêm một tham số khác X và thay thế cả 10 bằng X. Hoặc bạn có thể sử dụng phương pháp ghép mặt.
joran

15
Bạn đang tìm kiếm ?pretty?
hadley

Tại sao có foo(4)==5và không 10?
James

Câu trả lời:


64

Nếu bạn chỉ muốn làm tròn đến lũy thừa gần nhất của 10, thì chỉ cần xác định:

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

Điều này thực sự cũng hoạt động khi x là một vectơ:

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

..nhưng muốn làm tròn thành số “đẹp” thì trước hết bạn cần xác định thế nào là số “đẹp”. Phần sau cho phép chúng tôi định nghĩa "nice" là một vectơ có các giá trị cơ sở đẹp từ 1 đến 10. Giá trị mặc định được đặt thành các số chẵn cộng với 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]]]]
}

Ở trên không hoạt động khi x là một vector - quá muộn vào buổi tối ngay bây giờ :)

> 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

[[BIÊN TẬP]]

Nếu câu hỏi là làm thế nào để làm tròn đến một giá trị gần nhất được chỉ định (như 10 hoặc 100), thì câu trả lời của James có vẻ thích hợp nhất. Phiên bản của tôi cho phép bạn lấy bất kỳ giá trị nào và tự động làm tròn nó thành một giá trị hợp lý "tốt đẹp". Một số lựa chọn tốt khác của vectơ "đẹp" ở trên là:1:10, c(1,5,10), seq(1, 10, 0.1)

Ví dụ: nếu bạn có một phạm vi giá trị trong biểu đồ của mình [3996.225, 40001.893]thì cách tự động phải tính đến cả kích thước của phạm vi và độ lớn của các con số. Và như Hadley đã lưu ý , pretty()chức năng có thể là những gì bạn muốn.


1
Vectorize(roundUpNice)là khá nhanh =) +1 Dù sao.
mbq

Tôi đã tự hỏi liệu có cách nào để chỉnh sửa hàm roundUp thành một nửa kết quả cuối cùng nếu giá trị ban đầu thấp hơn không? ví dụ một số từ 101-500 sẽ làm tròn thành 500 và số 501-999 sẽ làm tròn thành 1000? thay vì làm tròn tất cả thành 1000?
helen.h

Chỉ cần thay đổi các vector đẹp:roundUpNice(501, nice=c(5, 10)) # 1000
Tommy

1
Chỉ là một cảnh báo vì bạn làm việc với logarit trong hàm của mình, tôi sẽ có 2 trường hợp, một là ở đâu x < 0và áp dụng - xtrong nhật ký trước khi đặt -lại. Tôi cũng sẽ thêm một ngoại lệ cho tình huốngx = 0
Yohan Obadia,

Hàm khá hoạt động thực sự tốt cho nhu cầu của tôi (để đặt một giới hạn khá trên trục X của biểu đồ)
Joon

132

Các plyrthư viện có chức năng round_anyđó là khá tổng quát để làm tất cả các loại tròn. Ví dụ

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

Đối với một dplyrsự thay thế, xem: stackoverflow.com/a/46489816/435093
slhck

46

Hàm vòng trong R gán ý nghĩa đặc biệt cho tham số chữ số nếu nó là số âm.

tròn (x, chữ số = 0)

Làm tròn đến một số âm có nghĩa là làm tròn đến lũy thừa của mười, vì vậy, ví dụ làm tròn (x, chữ số = -2) làm tròn đến hàng trăm gần nhất.

Điều này có nghĩa là một chức năng như sau sẽ khá gần với những gì bạn đang yêu cầu.

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

Đầu ra trông giống như sau

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

2
Tuyệt vời! Câu trả lời của bạn nên được đánh dấu là đúng!
Alessandro Jacopson

+1. Tuy nhiên, @Alessandro các hàm trong câu trả lời của tôi linh hoạt hơn nhiều: bạn có thể làm tròn BẤT KỲ số nào lên HOẶC xuống BẤT KỲ khoảng nào.
theforestecologist

@theforestecologist cảm ơn bạn đã gợi ý! Theo sở thích cá nhân, tôi thường thích giải pháp tích hợp sẵn ngôn ngữ hơn là giải pháp tùy chỉnh.
Alessandro Jacopson

27

Làm thế nào về:

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

Cái nào mang lại:

> 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 Câu hỏi hơi khó hiểu, tôi đã viết bài này tập trung vào yêu cầu "X tùy ý", nhưng rõ ràng không thể tạo ra tất cả các giá trị mong đợi bằng giải pháp "một vòng lên đến X gần nhất". Có vẻ như OP muốn tạo ra các giá trị cho một trục, vì vậy prettycó lẽ là lựa chọn tốt nhất.
James

1
Rõ ràng là đến muộn bên này, nhưng sẽ không to * ceiling(x / to)sạch sẽ?
jared

25

Nếu bạn thêm một số âm vào đối số chữ số của round (), R sẽ làm tròn số đó thành bội số của 10, 100, v.v.

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

17

Làm tròn BẤT CỨ số Lên / Xuống đến BẤT KỲ khoảng thời gian nào

Bạn có thể dễ dàng làm tròn số thành một khoảng cụ thể bằng cách sử dụng toán tử modulo %% .

Chức năng:

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

Ví dụ:

> 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

Làm thế nào nó hoạt động:

Toán tử modulo %%xác định phần còn lại của phép chia số đầu tiên cho số thứ 2. Việc cộng hoặc trừ khoảng thời gian này vào số sở thích của bạn về cơ bản có thể 'làm tròn' con số thành một khoảng bạn chọn.

> 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

Cập nhật:

Phiên bản 2 đối số thuận tiện :

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

yGiá trị dương roundUp, trong khi ygiá trị âm roundDown:

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

Hoặc là....

Chức năng tự động làm tròn LÊN hoặc XUỐNG dựa trên các quy tắc làm tròn tiêu chuẩn:

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

Tự động làm tròn nếu xgiá trị >nằm giữa các lần xuất hiện tiếp theo của giá trị làm tròn 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

Tôi đã được hỏi làm thế nào để chuyển đổi Roundsang VBA trong 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

@Có lẽ tôi không chắc liệu bạn có bao giờ nói câu trả lời của tôi hay không vì tôi đã đăng 4 năm sau khi bạn hỏi, nhưng tôi nghĩ bạn có thể thấy câu trả lời của tôi khá thanh lịch và rất VERSATILE. Hy vọng nó giúp!
theforestecologist

7

Về việc làm tròn đến bội số của một số tùy ý , ví dụ 10, đây là một phương án thay thế đơn giản cho câu trả lời của James.

Nó hoạt động cho bất kỳ số thực nào được làm tròn lên ( from) và bất kỳ số thực dương nào được làm tròn thành ( to):

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

Thí dụ:

> 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

Tôi nghĩ rằng mã của bạn chỉ hoạt động tốt với một sửa đổi nhỏ:

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

Và các ví dụ của bạn chạy:

> 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

Tôi đã thay đổi chức năng của bạn theo hai cách:

  • đã thêm đối số thứ hai (cho X đã chỉ định của bạn )
  • đã thêm một giá trị nhỏ ( =1e-09, vui lòng sửa đổi!) vào max(x)nếu bạn muốn một số lớn hơn

1

Nếu bạn luôn muốn làm tròn một số lên đến X gần nhất, bạn có thể sử dụng các ceilingchức năng:

#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

Tương tự, nếu bạn luôn muốn làm tròn xuống , hãy sử dụng floorhàm. Nếu bạn chỉ muốn làm tròn lên hoặc xuống đến Z gần nhất, hãy sử dụng roundthay thế.

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

0

Bạn sẽ tìm thấy phiên bản nâng cấp của câu trả lời của Tommy có tính đến một số trường hợp:

  • Chọn giữa giới hạn thấp hơn hoặc cao hơn
  • Có tính đến các giá trị âm và 0
  • hai tỷ lệ đẹp khác nhau trong trường hợp bạn muốn hàm làm tròn các số lớn nhỏ khác nhau. Ví dụ: 4 sẽ được làm tròn ở 0 trong khi 400 sẽ được làm tròn ở 400.

Dưới mã:

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

Đầu ra mặc định của điều này là một vòng giảm:> round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
jessi

Tôi nghĩ một phần của vấn đề là điều đó nice_bignice_smallđược định nghĩa ngược lại, (nếu chúng ta lật chúng trong hàm round.up.nice(4.5)sẽ trở thành 4) nhưng nó vẫn làm tròn.
jessi

0

Tôi đã thử điều này mà không sử dụng bất kỳ thư viện bên ngoài hoặc các tính năng khó hiểu nào và nó hoạt động!

Hy vọng nó sẽ giúp một ai đó.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.