it-swarm.com.ru

Как сделать случайную выборку из списка или массива Scala?

Я хочу сделать случайную выборку из списка или массива Scala (не RDD), размер выборки может быть намного длиннее длины списка или массива, как я могу сделать это эффективно ? Поскольку размер выборки может быть очень большим, и выборка (в разных списках/массивах) должна выполняться большое количество раз.

Я знаю, что для Spark RDD мы можем использовать takeSample (), есть ли эквивалент для списка/массива Scala?

Большое спасибо.

12
Carter

Простая для понимания версия будет выглядеть так:

import scala.util.Random

Random.shuffle(list).take(n)
Random.shuffle(array.toList).take(n)

// Seeded version
val r = new Random(seed)
r.shuffle(...)
24
Marius Soutier

Для массивов:

import scala.util.Random
import scala.reflect.ClassTag

def takeSample[T:ClassTag](a:Array[T],n:Int,seed:Long) = {
  val rnd = new Random(seed)
  Array.fill(n)(a(rnd.nextInt(a.size)))
}

Создайте генератор случайных чисел (rnd) на основе вашего начального числа. Затем заполните массив случайными числами от 0 до размера вашего массива.

Последний шаг - применение каждого случайного значения к оператору индексации вашего входного массива. Использование его в REPL может выглядеть следующим образом:

scala> val myArray = Array(1,3,5,7,8,9,10)
myArray: Array[Int] = Array(1, 3, 5, 7, 8, 9, 10)

scala> takeSample(myArray,20,System.currentTimeMillis)
res0: scala.collection.mutable.ArraySeq[Int] = ArraySeq(7, 8, 7, 3, 8, 3, 9, 1, 7, 10, 7, 10,
1, 1, 3, 1, 7, 1, 3, 7)

Для списков я просто преобразовал бы список в массив и использовал бы ту же функцию. Я сомневаюсь, что вы все равно можете получить гораздо более эффективную работу со списками.

Важно отметить, что та же функция, использующая списки, будет занимать O (n ^ 2) времени, тогда как сначала преобразование списка в массивы займет O(n) время

3
Felix

Если вы хотите сделать выборку без / замены - Zip с случайными числами, сортируйте O(n*log(n), отбросьте случайные числа, возьмите

import scala.util.Random
val l = Seq("a", "b", "c", "d", "e")
val ran = l.map(x => (Random.nextFloat(), x))
  .sortBy(_._1)
  .map(_._2)
  .take(3)
1
KevinKatz

Используя классическую рекурсию.

import scala.util.Random

def takeSample[T](a: List[T], n: Int): List[T] = {
    n match {
      case n: Int if n <= 0 => List.empty[T]
      case n: Int => a(Random.nextInt(a.size)) :: takeSample(a, n - 1)
    }
}
1
thomas pocreau

Используя для понимания, для данного массива xs следующим образом,

for (i <- 1 to sampleSize; r = (Math.random * xs.size).toInt) yield a(r)

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

Note Для чисто функционального генератора случайных чисел рассмотрим, например, подход State Monad из Функциональное программирование в Scala , обсуждался здесь .

Note Рассмотрим также NICTA , еще один чисто функциональный генератор случайных значений, его использование проиллюстрировано, например, здесь .

1
elm
package your.pkg

import your.pkg.SeqHelpers.SampleOps

import scala.collection.generic.CanBuildFrom
import scala.collection.mutable
import scala.language.{higherKinds, implicitConversions}
import scala.util.Random

trait SeqHelpers {

  implicit def withSampleOps[E, CC[_] <: Seq[_]](cc: CC[E]): SampleOps[E, CC] = SampleOps(cc)
}

object SeqHelpers extends SeqHelpers {

  case class SampleOps[E, CC[_] <: Seq[_]](cc: CC[_]) {

    private def recurse(n: Int, builder: mutable.Builder[E, CC[E]]): CC[E] = n match {
      case 0 => builder.result
      case _ =>
        val element = cc(Random.nextInt(cc.size)).asInstanceOf[E]
        recurse(n - 1, builder += element)
    }

    def sample(n: Int)(implicit cbf: CanBuildFrom[CC[_], E, CC[E]]): CC[E] = {
      require(n >= 0, "Cannot take less than 0 samples")
      recurse(n, cbf.apply)
    }
  }
}

Или: 

  • Mixin SeqHelpers, например, со спецификацией Scalatest
  • Включить import your.pkg.SeqHelpers._

Тогда должно работать следующее:

Seq(1 to 100: _*) sample 10 foreach { println }

Правки для удаления актерского состава приветствуются. 

Также, если есть способ создать пустой экземпляр коллекции для аккумулятора, не зная заранее конкретного типа, пожалуйста, прокомментируйте. Тем не менее, строитель, вероятно, более эффективен.

0
Darren Bishop

Не проверял на производительность, но следующий код - простой и элегантный способ сделать выборку, и я верю, что он может помочь многим, которые приходят сюда только для того, чтобы получить код выборки. Просто измените «диапазон» в соответствии с размером вашего конечного образца. Если псевдослучайности недостаточно для ваших нужд, вы можете использовать take (1) во внутреннем списке и увеличить диапазон.

Random.shuffle((1 to 100).toList.flatMap(x => (Random.shuffle(yourList))))

0
ruhsuzbaykus