Jag måste generera slumptal efter Normalfördelning inom intervallet $ (a, b) $. (Jag jobbar i R.)
Jag vet att funktionen rnorm(n,mean,sd)
genererar slumptal efter normalfördelning, men hur ställer man in intervallgränserna inom det? Finns det några speciella R-funktioner tillgängliga för det?
Kommentarer
Svar
Det låter som om du vill simulera från en trunkerad distribution , och i ditt specifika exempel , en trunkerad normal .
Det finns en mängd olika metoder för att göra det, några enkla, andra relativt effektiv.
Jag illustrerar några tillvägagångssätt i ditt normala exempel.
-
Här är en mycket enkel metod för att skapa en i taget (i någon form av pseudokod ):
$ \ tt {repeat} $ generera $ x_i $ från N (medelvärde, sd) $ \ tt {tills} $ lägre $ \ leq x_i \ leq $ övre
Om större delen av distributionen ligger inom gränserna är detta ganska rimligt men det kan bli ganska långsamt om genererar du nästan alltid utanför gränserna.
I R kan du undvika slingan åt gången genom att beräkna området inom gränserna och generera tillräckligt med värden för att du kan vara nästan säker på att efter att ha kastat ut värdena utanför gränserna hade du fortfarande så många värden som behövs.
-
Du kan använda accept-reject med någon lämplig majoritetsfunktion över intervallet (i vissa fall enhetlig vara tillräckligt bra). Om gränserna var ganska smala i förhållande till s.d. men du var inte långt in i svansen, en enhetlig majorering skulle fungera bra med det normala, till exempel.
-
Om du har en rimligt effektiv cdf och invers cdf (som
pnorm
ochqnorm
för normalfördelning i R) kan du använda den inversa-cdf-metoden som beskrivs i första stycket i simuleringsavsnittet i Wikipedia-sidan på den trunkerade normalen . detta är detsamma som att ta en trunkerad uniform (trunkerad vid de erforderliga kvantilerna, vilket egentligen inte kräver några avslag alls, eftersom det bara är en annan uniform) och applicera den inversa normala cdf på det. Observera att detta kan misslyckas om du ”är långt in i svansen] -
Det finns andra tillvägagångssätt; samma Wikipedia-sida nämner att anpassa metoden ziggurat , som ska fungera för en mängd distributioner.
samma Wikipedia-länk nämner två specifika paket (båda på CRAN) med funktioner för att generera trunkerade normaler:
MSM
-paketet i R har en funktion,rtnorm
, som beräknar drag från en trunkerad normal. Paketettruncnorm
i R har också funktioner att dra från en trunkerad normal.
När man tittar runt täcks mycket av detta i svar på andra frågor (men inte exakt dubbletter eftersom denna fråga är mer allmän än bara den trunkerade normalen) … se ytterligare diskussion i
a. Detta svar
b. Xi ”an” s svar här , som har en länk till hans arXiv papper (tillsammans med några andra givande svar).
Svar
Den snabba och smutsiga metoden är att använda 68-95-99.7 regel .
I en normalfördelning faller 99,7% av värdena inom 3 standardavvikelser från medelvärdet. Så om du ställer in ditt medelvärde till mitten av önskat minimivärde och maximivärde och ställer in din standardavvikelse till 1/3 av ditt medelvärde, får du (mestadels) värden som faller inom det önskade intervallet. Då kan du bara rensa resten.
minVal <- 0 maxVal <- 100 mn <- (maxVal - minVal)/2 # Generate numbers (mostly) from min to max x <- rnorm(count, mean = mn, sd = mn/3) # Do something about the out-of-bounds generated values x <- pmax(minVal, x) x <- pmin(maxVal, x)
Jag har nyligen mött samma problem och försöker skapa slumpmässiga studentbetyg för testdata. I koden ovan har jag använt pmax
och pmin
för att ersätta värden utanför gränserna med min eller max in-bounds värde.Detta fungerar för mitt syfte, eftersom jag genererar ganska små mängder data, men för större mängder kommer det att ge dig märkbara stötar vid min- och maxvärdena. Så beroende på dina syften kan det vara bättre att kasta bort dessa värden, byt ut dem med NA
s, eller ”rulla om” dem tills de ”åter finns inom gränserna.
Kommentarer
- Varför bry sig om det här? Det är så enkelt att generera vanliga slumpmässiga nummer och släppa de som behöver trunkeras att det inte är ’ t nödvändigt för att vara komplicerat om det inte är önskad trunkering som är nära 100% av området av densiteten.
- Kanske ’ tolkar jag den ursprungliga frågan fel. Jag stötte på den här frågan när jag försökte ta reda på hur man uppnår en inte-direkt-statsrelaterad programmeringsuppgift i R, och jag ’ har först märkt att den här sidan är en statistikstackexchange , inte ett programmeringsstackexchange. 🙂 I mitt fall ville jag generera en viss mängd slumpmässiga heltal, med värden från 0 till 100, och jag ville att de genererade värdena skulle falla på en fin klockkurva över det intervallet. Sedan jag skrev detta har jag ’ insett att
sample(x=min:max, prob=dnorm(...))
är kanske ett enklare sätt att göra det. - @Glen_b Aaron Wells nämner
sample(x=min:max, prob=dnorm(...))
vilket verkar lite kortare än ditt svar. - Men observera att
sample()
tricket bara är användbart om du ’ försöker välja slumpmässiga heltal eller någon annan uppsättning diskreta, fördefinierade värden.
Svar
Inget av svaren här ger en effektiv metod för att generera trunkerade normala variabler som inte innebär avvisning av godtyckligt stora antal genererade värden. Om du vill generera värden från en trunkerad normalfördelning med specificerade nedre och övre gränser $ a < b $ , detta kan göras — utan avstötning — genom att generera enhetliga kvantiler över det kvantilintervall som tillåts av trunkeringen och använda omvänd transformationssampling för att få motsvarande normala värden .
Låt $ \ Phi $ beteckna CDF för normal normalfördelning. Vi vill generera $ X_1, …, X_N $ från en trunkerad normalfördelning (med medelparametern $ \ mu $ och variansparameter $ \ sigma ^ 2 $ ) $ ^ \ dolk $ med lägre och övre trunkeringsgränser $ a < b $ . Detta kan göras enligt följande:
$$ X_i = \ mu + \ sigma \ cdot \ Phi ^ {- 1} (U_i) \ quad \ quad \ quad U_1, …, U_N \ sim \ text {IID U} \ Big [\ Phi \ Big (\ frac {a- \ mu} {\ sigma} \ Big), \ Phi \ Big (\ frac {b- \ mu} {\ sigma} \ Big) \ Big]. $$
Det finns ingen inbyggd funktion för genererade värden från den trunkerade distributionen, men det är trivialt att programmera den här metoden med vanliga funktioner för att generera slumpmässiga variabler. Här är en enkel R
-funktion rtruncnorm
som implementerar denna metod i några rader kod.
rtruncnorm <- function(N, mean = 0, sd = 1, a = -Inf, b = Inf) { if (a > b) stop("Error: Truncation range is empty"); U <- runif(N, pnorm(a, mean, sd), pnorm(b, mean, sd)); qnorm(U, mean, sd); }
Detta är en vektoriserad funktion som genererar N
IID slumpmässiga variabler från den trunkerade normalfördelningen. Det skulle vara enkelt att programmera funktioner för andra trunkerade distributioner via samma metod. Det skulle inte heller vara för svårt att programmera tillhörande densitets- och kvantilfunktioner för den trunkerade fördelningen.
$ ^ \ dolk $ Observera att trunkeringen ändrar medelvärdet och variansen för fördelningen, så $ \ mu $ och $ \ sigma ^ 2 $ är inte medelvärdet och variansen för den trunkerade fördelningen.
Svar
Tre sätt har fungerat för mig:
-
med hjälp av sample () med rnorm ():
sample(x=min:max, replace= TRUE, rnorm(n, mean))
-
med msm-paketet och rtnorm-funktionen:
rtnorm(n, mean, lower=min, upper=max)
-
med hjälp av rnorm () och specificerar de nedre och övre gränserna, som Hugh har skrivit ovan:
sample <- rnorm(n, mean=mean); sample <- sample[x > min & x < max]
x <- rnorm(n, mean, sd); x <- x[x > lower.limit & x < upper.limit]