Ihre Frage über eine Folge von looping inspirierte mich zu versuchen, eine Implementierung mehrerer Sampling-ohne-Ersatz Rekursion auf Probenahme- mit -Ersatz mit zu schreiben.
NS
Letting repräsentieren die Anzahl der gewünschten Proben und NE
die Anzahl der Elemente aus dem Eingangs für jede Probe eingestellt auszuwählen, meine Idee war, dass es von Vorteil sein könnte, um zu versuchen über NS
sample()
Anrufe zu vermeiden, Looping, welche Zeit sein würde, -Verbrauch für große . Stattdessen können wir einen einzelnen Beispielaufruf starten, der Werte mit Ersetzung nimmt, und betrachten, dass die "erste Auswahl" jedes Beispiels darstellen. Dann können wir für jede eindeutige Auswahl den Eingabesatz (und den Wahrscheinlichkeitsgewichtungsvektor) um das ausgewählte Element reduzieren und rekursiv ausführen, bis wir NE
erreicht haben. Durch Kombinieren jedes (Teil-) Samples können wir eine Matrix erzeugen, deren Zeilen jeweils aus einem Sample ohne Ersetzung von NE
Werten aus dem Eingangssatz bestehen.
samplesNoReplace <- function(NS,set,NE=length(set),prob=NULL) {
if (NE>1L) {
inds <- sample(seq_along(set),NS,T,prob);
uris <- split(seq_len(NS),inds);
us <- as.integer(names(uris));
res <- base::matrix(set[inds],NS,NE);
for (ui in seq_along(uris)) {
u <- us[ui];
ris <- uris[[ui]];
res[ris,-1L] <- samplesNoReplace(length(ris),set[-u],NE-1L,prob[-u]);
}; ## end for
} else {
res <- base::matrix(sample(set,NS,T,if (length(set)==1L) NULL else prob),ncol=1L);
}; ## end if
res;
}; ## end samplesNoReplace()
Demo:
set.seed(10L); samplesNoReplace(10L,1:5,3L,c(10,2,2,2,1));
## [,1] [,2] [,3]
## [1,] 1 3 2
## [2,] 1 4 3
## [3,] 1 2 4
## [4,] 3 2 1
## [5,] 1 3 2
## [6,] 1 4 2
## [7,] 1 4 2
## [8,] 1 2 5
## [9,] 3 1 2
## [10,] 1 2 5
Benchmarking
library(microbenchmark);
bgoldst <- function() samplesNoReplace(NS,set,NE,prob);
akrun <- function() { N1 <- seq_len(NS); N <- length(set); lapply(N1, function(i) sample(set, size =NE, replace=FALSE,prob)); };
khashaa <- function() { replicate(NS, sample(set, NE,prob=prob), simplify = FALSE); };
## OP's case (100k samples, smallish set, smaller subset)
set.seed(1L);
NS <- 1e5L; set <- 1:20; NE <- 3L; prob <- runif(length(set));
microbenchmark(times=5L,bgoldst(),akrun(),khashaa());
## Unit: milliseconds
## expr min lq mean median uq max neval
## bgoldst() 40.9888 42.69257 46.33044 46.68856 47.40488 53.8774 5
## akrun() 547.3142 564.94249 599.96134 625.07602 631.19658 631.2774 5
## khashaa() 501.1226 521.14871 531.50227 524.65247 549.47600 561.1116 5
## 1k samples, large set, large subset
set.seed(1L);
NS <- 1e3L; set <- 1:1000; NE <- 500L; prob <- runif(length(set));
microbenchmark(times=1L,bgoldst(),akrun(),khashaa());
## Unit: milliseconds
## expr min lq mean median uq max neval
## bgoldst() 74478.4313 74478.4313 74478.4313 74478.4313 74478.4313 74478.4313 1
## akrun() 350.7270 350.7270 350.7270 350.7270 350.7270 350.7270 1
## khashaa() 353.2574 353.2574 353.2574 353.2574 353.2574 353.2574 1
## 1M samples, small set, necessarily small subset
set.seed(1L);
NS <- 1e6L; set <- 1:4; NE <- 4L; prob <- runif(length(set));
microbenchmark(times=5L,bgoldst(),akrun(),khashaa());
## Unit: milliseconds
## expr min lq mean median uq max neval
## bgoldst() 502.0865 519.1875 602.5631 627.6124 648.3831 715.5459 5
## akrun() 5450.3987 5653.0774 5817.0921 5799.4497 5987.0575 6195.4771 5
## khashaa() 5301.3673 5667.8592 5683.3805 5744.1461 5824.8801 5878.6497 5
## 10M samples, small set, necessarily small subset
set.seed(1L);
NS <- 1e7L; set <- 1:4; NE <- 4L; prob <- runif(length(set));
microbenchmark(times=1L,bgoldst(),akrun(),khashaa());
## Unit: seconds
## expr min lq mean median uq max neval
## bgoldst() 5.023389 5.023389 5.023389 5.023389 5.023389 5.023389 1
## akrun() 75.891354 75.891354 75.891354 75.891354 75.891354 75.891354 1
## khashaa() 69.422056 69.422056 69.422056 69.422056 69.422056 69.422056 1
Das Muster ist sehr interessant und, glaube ich, leicht erklärlich. Meine Funktion übertrifft für viele Samples, kleine Sets und kleine Subsets, da nur sehr wenige Rekursionen erforderlich sind, um alle möglichen (Sub-) Sample-Zweige abzudecken, während die Looping-Lösungen iterieren und einen sample()
Aufruf für jedes Sample machen müssen.Aber meine Funktion unterschreitet erheblich weniger Stichproben, große Mengen und große Untermengen, weil die Schleifenlösungen nicht sehr viele Iterationen zu vervollständigen haben, und der Baum der (Teil-) Stichprobenverzweigungen mit jeder neuen Auswahl etwas exponentiell wächst. Daher eignet sich meine Funktion nur für den Fall vieler Samples, kleiner Sets und kleiner Subsets, die übrigens ziemlich genau Ihren Beispiel-Anwendungsfall beschreiben.
Natürlich funktionieren die Looping-Lösungen selbst für ihre ungünstigsten Zeiten immer noch in einer Größenordnung meiner Funktion. Darüber hinaus ist es unwahrscheinlich, dass viele Millionen Proben einer kleinen Teilmenge einer kleinen Menge unter keinen Umständen benötigt werden. Aus Gründen der Einfachheit würde ich es daher nicht für unangemessen halten, diese Lösung vollständig zu ignorieren und immer den Schleifenansatz zu verwenden.
'replizieren (100000, Beispiel (1:20, 3), vereinfachen = FALSE)' – Khashaa