scipy.stats.sampling.

NumericalInversePolynomial#

class scipy.stats.sampling.NumericalInversePolynomial(dist, *, mode=None, center=None, domain=None, order=5, u_resolution=1e-10, random_state=None)#

Polynomial-Interpolation basierte INVerse der CDF (PINV).

PINV ist eine Variante der numerischen Invertierung, bei der die inverse CDF mittels Newtons Interpolationsformel angenähert wird. Das Intervall [0,1] wird in mehrere Teilintervalle unterteilt. In jedem dieser Teilintervalle wird die inverse CDF an den Knotenpunkten (CDF(x),x) für einige Punkte x in diesem Teilintervall konstruiert. Wenn die PDF gegeben ist, wird die CDF numerisch aus der gegebenen PDF mittels adaptiver Gauss-Lobatto-Integration mit 5 Punkten berechnet. Teilintervalle werden solange aufgeteilt, bis das geforderte Genauigkeitsziel erreicht ist.

Die Methode ist nicht exakt, da sie nur Zufallsvariaten der approximierten Verteilung erzeugt. Dennoch kann der maximal tolerierte Approximationsfehler auf die Auflösung (aber natürlich begrenzt durch die Maschinenpräzision) gesetzt werden. Wir verwenden den u-Fehler |U - CDF(X)| zur Fehlervermessung, wobei X das approximierte Perzentil ist, das dem Quantil U entspricht, d.h. X = approx_ppf(U). Wir nennen den maximal tolerierten u-Fehler die u-Auflösung des Algorithmus.

Sowohl die Ordnung des interpolierenden Polynoms als auch die u-Auflösung können ausgewählt werden. Beachten Sie, dass sehr kleine Werte der u-Auflösung möglich sind, aber die Kosten für den Einrichtungsschritt erhöhen.

Die interpolierenden Polynome müssen in einem Einrichtungsschritt berechnet werden. Sie funktioniert jedoch nur für Verteilungen mit endlichem Definitionsbereich; für Verteilungen mit unendlichem Definitionsbereich werden die Enden abgeschnitten, so dass die Wahrscheinlichkeit für die Endbereiche im Vergleich zur gegebenen u-Auflösung gering ist.

Die Konstruktion des Interpolationspolynoms funktioniert nur, wenn die PDF unimodal ist oder wenn die PDF zwischen zwei Modi nicht verschwindet.

Es gibt einige Einschränkungen für die gegebene Verteilung

  • Die Unterstützung der Verteilung (d.h. der Bereich, in dem die PDF strikt positiv ist) muss zusammenhängend sein. In der Praxis bedeutet dies, dass der Bereich, in dem die PDF „nicht zu klein“ ist, zusammenhängend sein muss. Unimodale Dichten erfüllen diese Bedingung. Wenn diese Bedingung verletzt wird, kann der Definitionsbereich der Verteilung abgeschnitten werden.

  • Wenn die PDF numerisch integriert wird, muss die gegebene PDF kontinuierlich und glatt sein.

  • Die PDF muss beschränkt sein.

  • Der Algorithmus hat Probleme, wenn die Verteilung schwere Enden hat (da die inverse CDF bei 0 oder 1 sehr steil wird) und die angeforderte u-Auflösung sehr klein ist. Z.B. zeigt die Cauchy-Verteilung wahrscheinlich dieses Problem, wenn die angeforderte u-Auflösung kleiner als 1.e-12 ist.

Parameter:
distObjekt

Eine Instanz einer Klasse mit einer pdf- oder logpdf-Methode, optional eine cdf-Methode.

  • pdf: PDF der Verteilung. Die Signatur der PDF wird erwartet als: def pdf(self, x: float) -> float, d.h. die PDF sollte einen Python-Float akzeptieren und einen Python-Float zurückgeben. Sie muss sich nicht zu 1 integrieren, d.h. die PDF muss nicht normalisiert sein. Diese Methode ist optional, aber entweder pdf oder logpdf muss angegeben werden. Wenn beide gegeben sind, wird logpdf verwendet.

  • logpdf: Der Logarithmus der PDF der Verteilung. Die Signatur ist dieselbe wie für pdf. Ebenso kann der Logarithmus der Normalisierungskonstante der PDF ignoriert werden. Diese Methode ist optional, aber entweder pdf oder logpdf muss angegeben werden. Wenn beide gegeben sind, wird logpdf verwendet.

  • cdf: CDF der Verteilung. Diese Methode ist optional. Wenn sie angegeben wird, ermöglicht sie die Berechnung des „u-Fehlers“. Siehe u_error. Muss dieselbe Signatur wie die PDF haben.

modefloat, optional

(Exakter) Modus der Verteilung. Standard ist None.

centerfloat, optional

Ungefähre Lage des Modus oder des Mittelwerts der Verteilung. Diese Lage liefert Informationen über den Hauptteil der PDF und wird verwendet, um numerische Probleme zu vermeiden. Standard ist None.

domainListe oder Tupel der Länge 2, optional

Die Unterstützung der Verteilung. Standard ist None. Wenn None

  • Wenn eine support-Methode vom Verteilungsobjekt dist bereitgestellt wird, wird sie verwendet, um den Definitionsbereich der Verteilung festzulegen.

  • Andernfalls wird die Unterstützung als \((-\infty, \infty)\) angenommen.

orderint, optional

Ordnung des interpolierenden Polynoms. Gültige Ordnungen liegen zwischen 3 und 17. Höhere Ordnungen führen zu weniger Intervallen für die Annäherungen. Standard ist 5.

u_resolutionfloat, optional

Setzt den maximal tolerierten u-Fehler. Werte von u_resolution müssen mindestens 1.e-15 und höchstens 1.e-5 betragen. Beachten Sie, dass die Auflösung der meisten gleichmäßigen Zufallszahlengeneratoren 2-32 = 2.3e-10 beträgt. Ein Wert von 1.e-10 führt also zu einem Invertierungsalgorithmus, der als exakt bezeichnet werden könnte. Für die meisten Simulationen sind auch etwas größere Werte für den maximalen Fehler ausreichend. Standard ist 1e-10.

random_state{None, int, numpy.random.Generator,

Ein NumPy-Zufallszahlengenerator oder ein Seed für den zugrunde liegenden NumPy-Zufallszahlengenerator, der zum Erzeugen des Stroms von gleichmäßigen Zufallszahlen verwendet wird. Wenn random_state None ist (oder np.random), wird die Singleton-Instanz numpy.random.RandomState verwendet. Wenn random_state ein int ist, wird eine neue RandomState-Instanz mit random_state als Seed verwendet. Wenn random_state bereits eine Generator- oder RandomState-Instanz ist, wird diese Instanz verwendet.

Attribute:
intervals

Gibt die Anzahl der im Berechnungsprozess verwendeten Intervalle zurück.

Methoden

cdf(x)

Approximierte kumulative Verteilungsfunktion der gegebenen Verteilung.

ppf(u)

Approximierte PPF (Prozentrangfunktion) der gegebenen Verteilung.

qrvs([size, d, qmc_engine])

Quasi-Zufallsvariaten der gegebenen RV.

rvs([size, random_state])

Stichprobe aus der Verteilung.

set_random_state([random_state])

Setzt den zugrunde liegenden gleichmäßigen Zufallszahlengenerator.

u_error([sample_size])

Schätzt den u-Fehler der Approximation mittels Monte-Carlo-Simulation.

Referenzen

[1]

Derflinger, Gerhard, Wolfgang Hörmann, und Josef Leydold. „Random variate generation by numerical inversion when only the density is known.“ ACM Transactions on Modeling and Computer Simulation (TOMACS) 20.4 (2010): 1-25.

[2]

UNU.RAN Referenzhandbuch, Abschnitt 5.3.12, „PINV - Polynomial interpolation based INVersion of CDF“, https://statmath.wu.ac.at/software/unuran/doc/unuran.html#PINV

Beispiele

>>> from scipy.stats.sampling import NumericalInversePolynomial
>>> from scipy.stats import norm
>>> import numpy as np

Um einen Generator zur Stichprobenentnahme aus der Standardnormalverteilung zu erstellen, tun Sie Folgendes:

>>> class StandardNormal:
...    def pdf(self, x):
...        return np.exp(-0.5 * x*x)
...
>>> dist = StandardNormal()
>>> urng = np.random.default_rng()
>>> rng = NumericalInversePolynomial(dist, random_state=urng)

Nachdem ein Generator erstellt wurde, können Stichproben aus der Verteilung gezogen werden, indem die Methode rvs aufgerufen wird.

>>> rng.rvs()
-1.5244996276336318

Um zu überprüfen, ob die Zufallsvariaten der gegebenen Verteilung genau folgen, können wir uns ihr Histogramm ansehen.

>>> import matplotlib.pyplot as plt
>>> rvs = rng.rvs(10000)
>>> x = np.linspace(rvs.min()-0.1, rvs.max()+0.1, 1000)
>>> fx = norm.pdf(x)
>>> plt.plot(x, fx, 'r-', lw=2, label='true distribution')
>>> plt.hist(rvs, bins=20, density=True, alpha=0.8, label='random variates')
>>> plt.xlabel('x')
>>> plt.ylabel('PDF(x)')
>>> plt.title('Numerical Inverse Polynomial Samples')
>>> plt.legend()
>>> plt.show()
../../_images/scipy-stats-sampling-NumericalInversePolynomial-1_00_00.png

Es ist möglich, den u-Fehler der approximierten PPF zu schätzen, wenn die exakte CDF während der Einrichtung verfügbar ist. Um dies zu tun, übergeben Sie ein dist-Objekt mit der exakten CDF der Verteilung während der Initialisierung.

>>> from scipy.special import ndtr
>>> class StandardNormal:
...    def pdf(self, x):
...        return np.exp(-0.5 * x*x)
...    def cdf(self, x):
...        return ndtr(x)
...
>>> dist = StandardNormal()
>>> urng = np.random.default_rng()
>>> rng = NumericalInversePolynomial(dist, random_state=urng)

Nun kann der u-Fehler durch Aufrufen der Methode u_error geschätzt werden. Sie führt eine Monte-Carlo-Simulation durch, um den u-Fehler zu schätzen. Standardmäßig werden 100000 Stichproben verwendet. Um dies zu ändern, können Sie die Anzahl der Stichproben als Argument übergeben.

>>> rng.u_error(sample_size=1000000)  # uses one million samples
UError(max_error=8.785994154436594e-11, mean_absolute_error=2.930890027826552e-11)

Dies gibt ein benanntes Tupel zurück, das den maximalen u-Fehler und den mittleren absoluten u-Fehler enthält.

Der u-Fehler kann durch Verringern der u-Auflösung (maximal zulässiger u-Fehler) reduziert werden.

>>> urng = np.random.default_rng()
>>> rng = NumericalInversePolynomial(dist, u_resolution=1.e-12, random_state=urng)
>>> rng.u_error(sample_size=1000000)
UError(max_error=9.07496300328603e-13, mean_absolute_error=3.5255644517257716e-13)

Beachten Sie, dass dies auf Kosten einer erhöhten Einrichtungszeit geht.

Die approximierte PPF kann durch Aufrufen der Methode ppf ausgewertet werden.

>>> rng.ppf(0.975)
1.9599639857012559
>>> norm.ppf(0.975)
1.959963984540054

Da die PPF der Normalverteilung als spezielle Funktion verfügbar ist, können wir auch den x-Fehler überprüfen, d.h. die Differenz zwischen der approximierten PPF und der exakten PPF.

>>> import matplotlib.pyplot as plt
>>> u = np.linspace(0.01, 0.99, 1000)
>>> approxppf = rng.ppf(u)
>>> exactppf = norm.ppf(u)
>>> error = np.abs(exactppf - approxppf)
>>> plt.plot(u, error)
>>> plt.xlabel('u')
>>> plt.ylabel('error')
>>> plt.title('Error between exact and approximated PPF (x-error)')
>>> plt.show()
../../_images/scipy-stats-sampling-NumericalInversePolynomial-1_01_00.png