"In diesem Abschnitt wollen wir uns mit einem komplexeren Beispiel beschäftigen, um weitere Methoden von `iminuit` kennzulernen.\n",
"Hierzu betrachten wir ein Zählexperiment, z.B. ein Teilchendetektor, bei dem ein Energiespektrum aufgenommen wird. Für jedes Energieintervall (Bin) wird die Anzahl der registrierten Ereignisse bestimmt. Hierbei können wir annehmen, dass die Verteilung der gemessenen Anzahl durch eine Poisson-Verteilung beschrieben wird. Dann entspricht der Fehler in jedem Bin gerade $\\sqrt n$. \n",
"Dieses Spektrum soll aus zwei gauß-förmigen Peaks über einem exponentiellen Untergrund bestehen und wird mit Hilfe eines Zufallszahlengenerator \"erzeugt\"."
]
},
{
"cell_type": "markdown",
"id": "100a4fe4-a5c4-4be3-a7f7-13337b97a194",
"metadata": {},
"source": [
"Nun wollen wir die Messdaten mit Hilfe von `iminuit` fitten. Hierzu müssen wir zunächste zwei Module des packages importieren und eine Funktion für die Entladekurve des Kondensators definieren:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "520f4973",
"metadata": {},
"outputs": [],
"source": [
"# Diese Zelle nur auf JupyterHub des ZDV ausführen um `iminuit` zu installieren!\n",
"definieren. Hier lohnt es sich, erst Funktionen für die einzelnen Komponenten zu definieren und dann das Gesamtmodel. Hierdurch lassen sich später die einzelnen Komponenten besser darstellen."
"Nun wollen wir wieder die Kostenfunktion und die Minimierungsfunktion definieren. Startwerte können wir anhand unseres Plots ablesen, lediglich $\\tau$ lässt sich auf diese Weise nicht gut bestimmen."
"Unsere Startparameter sind bereits nicht schlecht, aber weichen noch stark von den Daten ab. Bei komplexeren Daten und Fitmodellen lohnt es sich, den Fit schrittweise durchzuführen. Bevor wir uns den beiden Peaks widmen, welche uns eigentlich interessieren, sollten wir versuchen, den Untergrund etwas besser zu beschreiben. Um den Untergrund besser fitten zu können, sollten wir erst den Fitbereich auf einen Energiebereich limitieren, in welchem der Untergrund dominiert. Dem Plot können wir entnehmen, dass dies für alle Werte unterhalb von 45 keV und oberhalb von 70 keV der Fall ist. Im Allgemeinen können wir Wertebereiche in Python mit Hilfe von „Masken“ selektieren. Eine Maske lässt sich wie folgt erstellen:"
"Das Resultat sieht bereits sehr gut aus. Nun können wir uns den eigentlichen Peaks widmen und starten im Folgenden mit dem kleineren der beiden. Zunächst sollten wir den maskierten Bereich entweder neu definieren oder komplett entfernen."
]
},
{
"cell_type": "code",
"execution_count": 513,
"id": "ebd77c40-6fcd-4881-bc1d-e3ca8ae0bf3b",
"metadata": {},
"outputs": [],
"source": [
"ls.mask = None"
]
},
{
"cell_type": "markdown",
"id": "7850ae53-ae2d-49aa-ac7b-dcef60a2dab7",
"metadata": {},
"source": [
"Außerdem können wir dem Plot entnehmen, dass durch den höheren Untergrund unsere Anfangsstartwerte nicht mehr ganz so gut passen. Diese können wir wie folgt aktualisieren:"
]
},
{
"cell_type": "code",
"execution_count": 514,
"id": "823e05a0-516c-4d30-8dc7-5381e0e2e617",
"metadata": {},
"outputs": [],
"source": [
"mi.values['A_p1'] = 700\n",
"mi.values['sigma_p1'] = 3"
]
},
{
"cell_type": "markdown",
"id": "8648bf00-901e-40dc-ada2-9a6b684e8f31",
"metadata": {},
"source": [
"Nun sollten wir alle Parameter wieder festhalten und nur die Parameter des ersten Peaks freigeben."
"Das Ergebnis sieht sehr gut aus. Alle Kacheln sind grün und die Daten scheinen durch die Funktion gut beschrieben zu werden. Natürlich können wir das gesamte Fitverfahren auch etwas kompakter in einer Zelle darstellen:"
"Nach dem wir nun unser Model an unsere Daten angepasst haben, stellt sich die Frage: „Spiegelt unser Model unsere Daten gut wider?“. Um diese Frage beantworten zu können, gibt es verschiedene Möglichkeiten, welche wir im Folgenden etwas näher betrachten wollen. \n",
"## Fit Residual: \n",
"Schauen wir uns zunächst noch einmal an, wie das Chi-Quadrat definiert ist:\n",
"Wir minimieren den Abstand zwischen einem Messwert und unserem Model und gewichten diesen mit den Unsicherheiten unserer Messwerte. Fitresiduen spiegeln genau dies wider. Sie sind definiert als \n",
"Als einzelner Plot sind sie noch nicht sehr informativ. Hilfreicher ist es bereits, wenn wir die Residuen zusammen mit unseren Daten und Fitmodel darstellen. "
"Sofern unser Fitmodel unsere Daten gut beschreibt, erwarten wir, dass die Residuen sich Gaußförmig zufällig um den Wert 0 herum verteilen. Dies folgt direkt aus der Annahme, dass sich die Unsicherheiten unserer Messwerte durch eine Gaußverteilung darstellen lassen. Dies können wir direkt überprüfen, sofern wir unsere Residuen in ein Histogramm eintragen. "
"Zeigen unsere Residuen eine Struktur oder ein systematisches Verhalten, deutet dies auf einen ungenauen Fit oder ein falsches Fitmodel hin. Dies ist im Folgenden gezeigt. "
"Zusätzlich zu den Fit-Residuen bietet das $\\chi^2$ selbst einen Weg, um die „goodness-of-fit“ unseres Model bestimmen zu können ...\n",
"\n",
"### $\\chi^2$:"
]
},
{
"cell_type": "markdown",
"id": "fe1789cf-7ed3-4db3-a0ae-9e563a9dc85e",
"metadata": {},
"source": [
"Wie gut fittet unsere obige Funktion unsere Messdaten? Sehr gut? Gut? Befriedigend? Oder doch eher schlecht? Wäre es nicht gut, ein Maß für die Güte des Fits zu haben? Wie könnte ein solches Maß aussehen?\n",
"\n",
"Sie haben das entscheidende Kriterium bereits kennengelernt: bei der Methode der kleinsten Quadrate geht es darum, das $\\chi^2$ zu minimieren. Gucken wir uns hierzu erst noch einmal an, wie sich das $\\chi^2$ berechnet:\n",
"Bei der Minimierung werden dabei Werte mit geringerer Unsicherheit bevorzugt, d.h. stärker gewichtet (s. Bild unten).\n",
"\n",
"<figure class=\"image\">\n",
"<img src=\"images/MaterialPythonkurs092018/LeastSquare.png\" alt=\"{{ Least Square Beispiel }}\" width=80%>\n",
"</figure>\n",
"\n",
"Damit man für einen gegebenen Datensatz nicht hunderte von verschiedenen Funktionen durchprobieren muss, gibt es für das $\\chi^2$ eine allgemeine Faustregel, welche den berechneten $\\chi^2$-Wert mit der Anzahl unserer Freiheitsgrade vergleicht. Die Anzahl an Freiheitsgrade ist gemeinhin gegeben als *Anzahl der Messwerte - Anzahl der Funktionsparameter* ($m - n$).\n",
"\n",
"1. Sofern $\\chi^2/\\text{ndof} >> 1$: sollte die Hypothese bzw. die Fitfunktion angezweifelt werden. Sie beschreibt in diesem Fall die Messdaten nur unzureichend. (Bzw. sollte $\\chi^2/\\text{ndof} > 1$ kann dies auch bedeuten, dass die Unsicherheiten unterschätzt sind)\n",
"2. Sofern $\\chi^2/\\text{ndof} \\approx 1$: beschreibt die Hypothese bzw. die Fitfunktion die Daten wie erwartet und wird nicht abgelehnt. \n",
"3. Falls $\\chi^2/\\text{ndof} << 1$ beschreibt die Hypothese bzw. die Fitfunktion die Daten wesentlich besser als erwartet. In diesem Fall heißt das nicht automatisch, dass unsere Hypothese falsch ist, aber man sollte überprüfen, ob die gemessenen Fehler nicht überschätzt worden sind (oder eine Korrelation zwischen den Messfehlern vorliegt). \n",
"\n",
"Sofern Sie eine Arbeit schreiben und Ihre **Goodness-of-the-Fit** ($\\chi^2/\\text{ndof}$) angeben wollen, so geben Sie immer beides an, das $\\chi^2$ und die Anzahl an Freiheitsgraden *ndof*. Beide Werte getrennt haben einen größeren Informationsgehalt als der resultierende Quotient (Genaueres lernen Sie z.B. in der Vorlesung *Statistik, Datenanalyse und Simulationen* im Master).\n",
"\n",
"Sehen wir uns hierzu nochmal unseren Doppelpeakfit etwas genauer an. `iminuit` berechnet hier für uns bereits das reduzierete $\\chi^2$."
"Wie schon im vorherigen Abschnitt erwähnt, kann man das $\\chi^2$ auch dazu verwenden, die Gültigkeit des gewählten Models zu prüfen.\n",
"Hierzu schauen wir uns die $\\chi^2$-Verteilung an. Der einzige freie Parameter ist die Anzahl der Freiheitsgrade. Die Anzahl der Freiheitsgrade ist auch gleichzeitig der Erwartungswert der $\\chi^2$-Verteilung. In unserem Beispiel oben ist die Anzahl der Freiheitsgrade 112 und die entsprechende Verteilung sieht wie folgt aus..."
"Der erste Schritt für den Hypothesen-Test ist die Berechnung des $P$-Werts\n",
"$$ P = \\int_{\\chi^2}^{\\infty} f(z,n_d)dz $$\n",
"wobei $f(z,n_d)$ die $\\chi^2$-Verteilung und $n_d$ die Anzahl der Freiheitsgrade ist.\n",
"Im Bild oben entspricht dies der ausgefüllten Fläche.\n",
"\n",
"Die praktische Berechnung erfolgt mittels der kumulativen Verteilungsfunktion via\n",
"$$ P = 1 - \\chi^2_{CDF}(x, n_d) $$\n",
"wobei für $x$ das im Fit bestimmte $\\chi^2$ eingesetzt wird. Die praktische Bedeutung des $P$-Werts ist die Wahrscheinlichkeit bei einer Wiederholung des Experiments in größeres $\\chi^2$ zu erhalten, wenn unser Model die Daten richtig beschreibt und die ermittelten Fitparameter den wahren Werten entsprechen."
"Kehren wir zu unserem Doppelpeak-Spektrum zurück und änderen das Fitmodell, indem wir statt eines exponentiellen einen konstanten Untergrund annehmen."
"Diese Änderung ist gering und der Fit scheint die Daten weiterhin zu beschreiben. Allerdings gibt bei kleinen Energien eine deutlich sichtbare Diskrepanz. Dies zeigt sich auch in einem größeren $\\chi^2$-Wert. Wie wirkt sich dies auf den $P$-Wert aus?"
"Der Fit ist offensichtlich viel schlechter und der $P$-Wert liegt nahe bei null, so dass man dieses Model ausschließen sollte.\n",
"\n",
"Was aber, wenn die Änderung nicht so dramatisch ist? Ist ein $P$-Wert von 0,4 besser als 0,2? Nein, das kann man so nicht beantworten. Aber für einen Hypothesen-Test sollten man vorher eine Schwelle festlegen für die Akzeptanz oder Ablehnung des Models.\n",
"\n",
"Wie ein solcher Hypothesen-Test aussehen kann, wollen wir im Folgenden betrachten. Hierbei benutzen wir\n",
"1. ein korrektes Model (Normalverteilung),\n",
"2. ein korrektes Model mit überschätztem Fehler (10% größer),\n",
"3. und ein falsches Model (Lorentzverteilung)"
]
},
{
"cell_type": "code",
"execution_count": 264,
"id": "c3f1f1d4-4b84-45a1-9d23-4cbb8ba32c8c",
"metadata": {},
"outputs": [],
"source": [
"def lorentzian( x, x0, a, gam ):\n",
" return a * gam**2 / ( gam**2 + ( x - x0 )**2)"
]
},
{
"cell_type": "markdown",
"id": "0e3fcfd5",
"metadata": {},
"source": [
"Den Fit der drei Modelle und die Bestimmung des entsprechenden $P$-Werts wiederholen wir 5000-mal um eine ausreichende Statistik zu erhalten."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "9667c766",
"metadata": {},
"outputs": [],
"source": [
"# Diese Zelle nur auf JupyterHub des ZDV ausführen um `tqdm` zu installieren falls es nicht vorhanden sein sollte!\n",
"Die Schwelle des $P$-Werts für den Hypothesen-Test setzen wir auf 0,1, d.h. Ergebnisse mit eine, $P$-Wert $<$ 0,1 werden verworfen, alle anderen akzeptiert."
"Wie man sieht, wird das falsche Modell nahezu immer verworfen während das richtige Modell meistens nicht verworfen wird. Das Modell mit dem überschätzten Fehler wird sogar häufiger akzeptiert, so dass man hier keine Unterscheidung vornehmen kann."