it-swarm.com.de

Wie stichprobenartig aus einer Scala-Liste oder einem Array?

Ich möchte eine zufällige Stichprobe aus einer Scala-Liste oder einem Array (kein RDD) erstellen. Die Stichprobengröße kann viel länger sein als die Länge der Liste oder des Arrays. Wie kann ich dies tun effizient ? Da die Stichprobengröße sehr groß sein kann und die Stichproben (auf verschiedenen Listen/Arrays) häufig durchgeführt werden müssen.

Ich weiß, dass wir für eine Spark-RDD takeSample () verwenden können. Gibt es eine Entsprechung für die Scala-Liste/das Scala-Array?

Vielen Dank.

12
Carter

Eine leicht verständliche Version würde so aussehen:

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

Für Arrays:

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

Erstellen Sie einen Zufallszahlengenerator (rnd) basierend auf Ihrem Samen. Füllen Sie dann ein Array mit Zufallszahlen von 0 bis zur Größe Ihres Arrays.

Der letzte Schritt besteht darin, jeden Zufallswert auf den Indexierungsoperator Ihres Eingabearrays anzuwenden. Die Verwendung in REPL könnte folgendermaßen aussehen:

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)

Für Listen würde ich einfach die Liste in Array konvertieren und dieselbe Funktion verwenden. Ich bezweifle, dass Sie für Listen ohnehin viel effizienter werden können.

Es ist wichtig anzumerken, dass dieselbe Funktion, die Listen verwendet, O (n ^ 2) Zeit in Anspruch nehmen würde, während das Konvertieren der Liste in Arrays zuerst O(n) Zeit

3
Felix

Wenn Sie ohne Ersatz - Zip mit Random abtasten möchten, sortieren Sie O(n*log(n), Random verwerfen, mitnehmen

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

Verwendung der klassischen Rekursion.

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

Verwenden Sie für ein gegebenes Array xs ein für ein Verständnis wie folgt:

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

Beachten Sie, dass der Zufallsgenerator hier Werte innerhalb des Einheitenintervalls erzeugt, die über die Größe des Arrays skaliert und für die Indexierung über das Array in Int konvertiert werden.

Hinweis Betrachten Sie für den rein funktionalen Zufallsgenerator beispielsweise den State Monad-Ansatz aus Functional Programming in Scala , hier hier .

Hinweis Betrachten Sie auch NICTA , einen weiteren rein funktionalen Zufallswertgenerator, dessen Verwendung beispielsweise hier veranschaulicht wird.

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

Entweder: 

  • Mixin SeqHelpers beispielsweise mit einer Scalatest-Spezifikation
  • Include import your.pkg.SeqHelpers._

Dann sollte folgendes funktionieren:

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

Bearbeitungen zum Entfernen der Besetzung sind erwünscht. 

Wenn Sie eine Möglichkeit haben, eine leere Instanz der Sammlung für den Akku zu erstellen, ohne den konkreten Typ im Voraus zu kennen, kommentieren Sie dies bitte. Der Builder ist wahrscheinlich effizienter.

0
Darren Bishop

Ich habe nicht auf Leistung getestet, aber der folgende Code ist ein einfacher und eleganter Weg, um das Sampling durchzuführen. Ich glaube, viele können hier helfen, nur um einen Sampling-Code zu erhalten. Ändern Sie einfach den "Bereich" entsprechend der Größe Ihres Endmusters. Wenn Pseude-Zufälligkeit für Ihre Bedürfnisse nicht ausreicht, können Sie take (1) in der inneren Liste verwenden und die Reichweite erhöhen.

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

0
ruhsuzbaykus