Исправление set.seed для всего сеанса

Я использую R для построения агентной модели с процессом Монте-Карло. Это означает, что у меня есть много функций, которые используют какой-то случайный движок. Чтобы получить воспроизводимые результаты, я должен исправить посевной материал. Но, насколько я понимаю, я должен устанавливать начальное число перед каждой случайной выборкой или выборкой. Это настоящая боль в шее. Есть ли способ исправить семя?

set.seed(123)
print(sample(1:10,3))
# [1] 3 8 4
print(sample(1:10,3))
# [1]  9 10  1
set.seed(123)
print(sample(1:10,3))
# [1] 3 8 4

person Elad663    schedule 17.12.2013    source источник
comment
Почему вы хотите, чтобы их все исправили? Разве недостаточно установить начальное значение один раз в начале, а затем выполнить 3 операции или их количество?   -  person Gavin Simpson    schedule 17.12.2013
comment
Вы получите воспроизводимые результаты от компьютерной программы, если вы установите семя один раз в начале и никогда не прикасаетесь к ней. Вы можете установить начальное значение в программе, если, например, вы хотите, чтобы агенты, использующие случайные числа, вели себя одинаково при каждом действии. В этом случае заставьте агент установить свое семя.   -  person Spacedman    schedule 17.12.2013


Ответы (6)


Есть несколько вариантов, в зависимости от ваших конкретных потребностей. Я подозреваю, что первого варианта недостаточно, но мои второй и третий варианты могут быть более подходящими, а третий вариант наиболее автоматизирован.

Опция 1

Если вы заранее знаете, что функция, использующая / создающая случайные числа, всегда будет отображать одно и то же число, и вы не переупорядочиваете вызовы функций и не вставляете новый вызов между существующими, тогда все, что вам нужно сделать, это установить начальное число один раз. В самом деле, вы, вероятно, не захотите постоянно сбрасывать начальное значение, поскольку вы просто продолжаете получать один и тот же набор случайных чисел для каждого вызова функции.

Например:

> set.seed(1)
> sample(10)
 [1]  3  4  5  7  2  8  9  6 10  1
> sample(10)
 [1]  3  2  6 10  5  7  8  4  1  9
> 
> ## second time round
> set.seed(1)
> sample(10)
 [1]  3  4  5  7  2  8  9  6 10  1
> sample(10)
 [1]  3  2  6 10  5  7  8  4  1  9

Вариант 2

Если вы действительно хотите убедиться, что функция использует одно и то же семя, и вы хотите установить его только один раз, передайте семя в качестве аргумента:

foo <- function(...., seed) {
  ## set the seed
  if (!missing(seed)) 
    set.seed(seed) 
  ## do other stuff
  ....
}

my.seed <- 42
bar <- foo(...., seed = my.seed)
fbar <- foo(...., seed = my.seed)

(где .... означает другие аргументы вашей функции; это псевдокод).

Вариант 3

Если вы хотите автоматизировать это еще больше, вы можете злоупотребить механизмом options, что нормально, если вы просто делаете это в сценарии (для пакета вы должны использовать свой собственный объект параметров). Тогда ваша функция может искать эту опцию. Например.

foo <- function() {
  if (!is.null(seed <- getOption("myseed")))
    set.seed(seed)
  sample(10)
}

Тогда в употреблении имеем:

> getOption("myseed")
NULL
> foo()
 [1]  1  2  9  4  8  7 10  6  3  5
> foo()
 [1]  6  2  3  5  7  8  1  4 10  9
> options(myseed = 42)
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
person Gavin Simpson    schedule 17.12.2013
comment
Вариант 3 полезен для моего варианта использования, но, к сожалению, для меня он работает не так, как ожидалось. Когда я запускаю options(myseed = 42), все работает нормально, и я вижу произошедшее изменение, глядя на getOption("myseed"). Однако при запуске функции-образца результаты кажутся непоследовательными, и я вижу, как они меняют каждую строку по сравнению с ожидаемым результатом, показанным для варианта 3. - person Ricky; 28.08.2020
comment
Вариант 3 у меня работает; вы не можете просто позвонить sample() после установки опции. Как показано в foo(), вам нужна оболочка, которая проверяет, установлена ​​ли опция, и если она устанавливает начальное значение в значение, хранящееся в опции, а затем вызывает sample(). - person Gavin Simpson; 28.08.2020
comment
Я понимаю, что вы имеете в виду, извините, я неправильно это понял. Код пользователя @stevec ниже - это то, что я искал, где вам не нужно оборачивать функцию внутри другой функции, чтобы семя было постоянным: addTaskCallback(function(...) {set.seed(123);TRUE}) - person Ricky; 28.08.2020

Я думаю, что в этом вопросе возникает путаница. В этом примере начальное значение было установлено для всего сеанса. Однако это не означает, что он будет выдавать один и тот же набор чисел каждый раз, когда вы используете команду print(sample)) во время выполнения; это не походило бы на случайный процесс, поскольку было бы полностью определено, что одни и те же три числа будут появляться каждый раз. Вместо этого на самом деле происходит то, что после того, как вы установили начальное число, каждый раз, когда вы запускаете скрипт, одно и то же начальное число используется для создания псевдослучайного выбора чисел, то есть чисел, которые выглядят как случайные, но на самом деле произведено воспроизводимым процессом с использованием засеянных вами семян.

Если вы перезапустите весь сценарий с самого начала, вы воспроизведете те числа, которые выглядят случайными, но не таковыми. Итак, в этом примере второй раз, когда начальное значение установлено на 123, на выходе снова будет 9, 10 и 1, что именно то, что вы ожидаете увидеть, потому что процесс начинается снова с самого начала. Если бы вы продолжили воспроизведение своего первого прогона, написав print(sample(1:10,3)), то второй набор выходных данных снова был бы 3, 8 и 4.

Итак, краткий ответ на вопрос: если вы хотите установить начальное число для создания воспроизводимого процесса, сделайте то, что вы сделали, и установите начальное значение один раз; однако вам не следует устанавливать начальное число перед каждой случайной выборкой, потому что это приведет к повторному запуску псевдослучайного процесса с самого начала.

Это старый вопрос, но он по-прежнему занимает одно из первых мест в результатах поиска, и казалось, что он стоит расширять ответ Spacedman.

person TilmanHartley    schedule 05.01.2017
comment
Людям нужны не только воспроизводимые сценарии. Воспроизводимые функции также важны, особенно в рабочих процессах современных ноутбуков. Вы ответили на заголовок, но не на основной вопрос. - person jiggunjer; 22.07.2020

Если вы хотите всегда возвращать одни и те же результаты из случайных процессов, просто все время сохраняйте начальное значение, установленное с помощью:

addTaskCallback(function(...) {set.seed(123);TRUE})

Теперь результат всегда один и тот же:

print(sample(1:10,3))
# [1] 3 8 4
print(sample(1:10,3))
# [1] 3 8 4
person stevec    schedule 18.09.2019
comment
+1 это самое простое решение для монте-карло и т. Д. Можно ли это отключить (без перезагрузки)? - person user63230; 11.09.2020
comment
@ user63230 спасибо! Да, попробуй removeTaskCallback(1). Это удаляет первый обратный вызов (так что, если у вас есть ›1, вы должны изменить аргумент, чтобы он совпадал с аргументом, установленным с addTaskCallback()). - person stevec; 11.09.2020

Нет нужды. Хотя результаты различаются от образца к образцу (чего вы почти наверняка хотите, иначе случайность очень сомнительна), результаты от цикла к запуску будут одинаковыми. Смотрите, вот результат моей машины.

> set.seed(123)
> sample(1:10,3)
[1] 3 8 4
> sample(1:10,3)
[1]  9 10  1
person Aaron left Stack Overflow    schedule 17.12.2013
comment
Я не думаю, что то, что вы сказали, улавливает смысл OP. Думаю, упор делается на воспроизводимый результат. Вы можете получить разные образцы, но вы не можете их контролировать в своем подходе. - person alittleboy; 17.12.2013
comment
@alittleboy Что ж, совершенно непонятно, что нужно ОП. Например, если у меня есть сценарий, который вызывает функцию foo() 4 раза, и каждый из них будет использовать 10 случайных чисел, то все, что мне нужно сделать, это установить начальное число один раз в начале сценария. Результаты можно воспроизвести, если вы запустите сценарий. Если вам нужно сделать отдельные пробежки и сделать их независимо, то вам нужен другой подход. Ваш ответ один, и он очень конкретен и предполагает многое, о чем не сказал ОП. Например, я много занимаюсь Монте-Карло и не стал бы делать все по-вашему. Итак, контекст здесь все. - person Gavin Simpson; 17.12.2013
comment
@GavinSimpson Проблема в том, что длина динамическая, поскольку модель агента динамична. Затем, поскольку структура не зафиксирована, я могу получить разные результаты для одного и того же семени. Я думаю. - person Elad663; 17.12.2013
comment
@ Elad663 Тогда это полезная информация, которую вы можете задать в своем вопросе. В этом случае вы можете воспользоваться вариантами 2 или 3 из моего ответа. Но я не уверен, что вы действительно этого хотите; имеет ли значение, что каждый вызов функции будет использовать точно такой же начальный набор случайных чисел, пока не сработает динамический бит? - person Gavin Simpson; 17.12.2013
comment
@GavinSimpson Вы правы, это должно было быть частью вопроса. Из обсуждения здесь я становлюсь более сомнительным в правильной реализации. Посмотрим, что скажет мой советник .. спасибо за ваше время и усилия. - person Elad663; 17.12.2013

Вы можете выполнить функцию-оболочку, например:

> wrap.3.digit.sample <- function(x) {
+    set.seed(123)
+    return(sample(x, 3))
+ }
> wrap.3.digit.sample(c(1:10))
[1] 3 8 4
> wrap.3.digit.sample(c(1:10))
[1] 3 8 4

Вероятно, есть более элегантный способ, и я уверен, что кто-то ему подойдет. Но если они этого не сделают, это должно облегчить вам жизнь.

person hd1    schedule 17.12.2013

Я предлагаю вам set.seed перед вызовом каждого генератора случайных чисел в R. Я думаю, что вам нужна воспроизводимость для моделирования Монте-Карло. Если в for цикле, вы можете set.seed(i) перед вызовом sample, что гарантирует полную воспроизводимость. Во внешней функции вы можете указать аргумент seed=1, чтобы в цикле for вы использовали set.seed(i+seed).

person alittleboy    schedule 17.12.2013