Случайное округление на 0,5 вверх или вниз с семенем в R
У меня есть набор данных, содержащий нецелые значения, которые я хочу округлить до ближайшего целого числа. Это довольно просто, но мне нужна функция, которая делает две вещи в дополнение к простому округлению:
Он должен округлять половинки (0,5,1,5,2,5 и т. Д.) Случайным образом вверх или вниз, при этом всегда оставляя пол, например 2,4, и потолок, например 2,6
Я хочу, чтобы функция каждый раз давала одни и те же результаты, при этом округляя примерно половину средних дел, а половину - вниз.
4 ответа
Поскольку, согласно OP, не только числа, которые строго равны пределу округления.5, но и те, которые очень близки к нему, должны рассматриваться как кандидаты для случайного округления вверх или вниз, точное условие x%%1==0.5
может (или должен) быть отброшен. В этом случае достаточно добавить очень маленькое возмущение ко всем числам перед округлением. jitter()
Функция добавляет такое случайное возмущение, которое влияет на результаты округления. Это делается воспроизводимым способом в зависимости от начального значения. Диапазон допуска, который определяет, какие числа являются "случайными кандидатами на округление", будет размером джиттера, который можно указать с помощью необязательного параметра. amount
,
Поэтому можно использовать такую функцию:
random_round <- function(x, seed = 123, tol = 1.e-6) {
set.seed(seed)
round(jitter(x, amount = tol))
}
Вы можете дополнительно векторизовать свое решение, используя ifelse
, делая sapply
ненужным:
FOO <- function(x, seed){
set.seed(seed)
ifelse(x %% 1 == .5, round(x + sample(c(-1, 1), 1) * .01), round(x))
}
test <- c(4.5, 3.4, 6.8, 3.5)
FOO(test, 1)
[1] 4 3 7 3
Это в несколько раз быстрее. Microbenchmark:
set.seed(10)
test <- sample(1:10, 10000, replace = T)
test <- test - sample(seq(0, 1, .1), 10000, replace = T)
microbenchmark(LAP = FOO(test, 1),
Samuel = round_r(test), unit = "ms", times = 1000L)
Unit: milliseconds
expr min lq mean median uq max neval cld
LAP 1.172478 1.197225 1.493402 1.20718 1.237616 158.8736 1000 a
Samuel 41.040701 46.280868 50.014392 49.02561 52.908411 215.4537 1000 b
Другое, примерно на 30% более быстрое издание, предложенное @AndreElrico:
FOO2 <- function(x, seed){
set.seed(seed)
ifelse(x %% 1 == .5, sample(c(ceiling,floor),1)[[1]](x), round(x))
}
Я не думаю, что ты должен sapply()
над каждым числом, когда вы можете просто векторизовать его.
round_rs <- function(x) {
set.seed(111)
x + sample(c(0.5, -0.5), length(x), replace = TRUE)
}
table(round_rs(v[v %% 1 == 0.5]))
Или завершите:
round_rs <- function(x) {
set.seed(111)
rn <- v %% 1 == 0.5
x[rn] <- x[rn] + sample(c(0.5, -0.5), sum(rn), replace = TRUE)
x[!rn] <- round(x[!rn])
x
}
microbenchmark(LAP1 = FOO(test, 1),
erocoar = round_rs(test),
LAP2 = FOO2(test), unit = "ms", times = 1000L)
Unit: milliseconds
expr min lq mean median uq max neval
LAP1 1.388751 1.402546 1.8448210 1.488841 1.631277 77.461753 1000
erocoar 0.464842 0.472542 0.7619839 0.483449 0.535098 75.046116 1000
LAP2 0.994486 1.009243 1.2846360 1.061694 1.165955 3.814334 1000
РЕДАКТИРОВАТЬ: Основываясь на RHertel, я отредактировал код, чтобы лучше соответствовать этому конкретному случаю.
На первом этапе создается функция, которая колеблется и округляет значения, очень близкие к n + 0,5. На этапе II функция применяется ко всем нецелым значениям при заданных переменных.
НАСТРОИТЬ
library(tidyverse)
var1 <- c(rep(10.5,10^4),rep(20.1,10^4),rep(30.9,10^4))
var2 <- c(rep(10.5,10^4),rep(20.1,10^4),rep(30.9,10^4))
data <- as.data.frame(cbind(var1,var2))
Фаза I
round_r <- function(x,seed=111, tol=1.e-6) {
set.seed(seed)
round(ifelse(near(x%%1,0.5), jitter(x, amount = tol), x))
}
Фаза II
data2 <- data %>% mutate_at(vars(var1,var2),
funs(ifelse(.==.%/%1,.,round_r(.))))
# results
table(data)
table(data2)
> table(data)
var2
var1 10.5 20.1 30.9
10.5 10000 0 0
20.1 0 10000 0
30.9 0 0 10000
> table(data2)
var2
var1 10 11 20 31
10 4994 0 0 0
11 0 5006 0 0
20 0 0 10000 0
31 0 0 0 10000
Не забудьте стереть строку с помощью set.seed, если вы хотите действительно случайные округления!
Если код еще можно улучшить, я бы хотел услышать!