Что такое Scala-эквивалент Numpy np.random.choice в Python? (Случайный взвешенный выбор в Scala) - PullRequest
0 голосов
/ 07 ноября 2018

Я искал эквивалентный код Scala или базовую теорию для питонов np.random.choice (Numpy as np). У меня есть похожая реализация, которая использует метод Python np.random.choice для выбора случайных ходов из распределения вероятностей.

Код Питона

Список ввода: ['pooh', 'rabbit', 'piglet', 'Christopher'] и вероятности: [0.5, 0.1, 0.1, 0.3]

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

1 Ответ

0 голосов
/ 07 ноября 2018

Стандартная библиотека Scala не имеет эквивалента np.random.choice, но ее создание не должно быть слишком сложным, в зависимости от того, какие опции / функции вы хотите эмулировать.

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

def weightedSelect[T](input :(T,Int)*): Stream[T] = {
  val items  :Seq[T]    = input.flatMap{x => Seq.fill(x._2)(x._1)}
  def output :Stream[T] = util.Random.shuffle(items).toStream #::: output
  output
}

При этом каждому элементу ввода присваивается множитель. Таким образом, чтобы получить бесконечный псевдослучайный выбор символов c и v, при этом c поднимается на 3/5 доли времени, а v - на 2/5 доли времени:

val cvs = weightedSelect(('c',3),('v',2))

Таким образом, грубый эквивалент примера np.random.choice(aa_milne_arr,5,p=[0.5,0.1,0.1,0.3]) будет:

weightedSelect("pooh"-> 5
              ,"rabbit" -> 1
              ,"piglet" -> 1
              ,"Christopher" -> 3).take(5).toArray

Или, возможно, вам нужно лучшее (менее псевдо) случайное распределение, которое может быть сильно однобоким.

def weightedSelect[T](items :Seq[T], distribution :Seq[Double]) :Stream[T] = {
  assert(items.length == distribution.length)
  assert(math.abs(1.0 - distribution.sum) < 0.001) // must be at least close

  val dsums  :Seq[Double] = distribution.scanLeft(0.0)(_+_).tail
  val distro :Seq[Double] = dsums.init :+ 1.1 // close a possible gap
  Stream.continually(items(distro.indexWhere(_ > util.Random.nextDouble())))
}

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

val choices :Stream[String] = weightedSelect( List("this"     , "that")
                                           , Array(4998/5000.0, 2/5000.0))

// let's test the distribution
val (choiceA, choiceB) = choices.take(10000).partition(_ == "this")

choiceA.length  //res0: Int = 9995
choiceB.length  //res1: Int = 5  (not bad)
...