-
Notifications
You must be signed in to change notification settings - Fork 0
Simulation definition
A simulation to be executed with the Internal Pilot Simulator (IPS) is written in a little custom domain-specific language. This language has been build on top of the general purpose language Ruby. Therefore some concepts of Ruby are used, when you define your simulation with this language.
A specifically important Ruby concept in this sense are blocks. Blocks define a certain area, where you can define a specific part of the simulation. You write a code by writing the name and enclose some statements between do and end.
This looks linke this
block_name do
statement_1
statement_2
end
The domain-specific language is seperated in the part arms, test, sample_size, internal_pilot and collect.
Every part represents its equivalent inside the simulated trials.
All blocks are in side an eclosing simulation.
In consequence as simulation block encloses all the definition relevant to one simulation study.
The root structure of the definition for a simulation study looks like this:
simulate do
arms do
# the trial arms with their distribution
end
test do
# the statistical test and its parameters
end
sample_size do
# how to calculate the sample sizes
end
internal_pilot do
# which parameters to adapt in an internal pilot
# and when
end
collect do
# Collect the the data for the study evaluation
end
end
The order of the blocks is not relevant. It might however be sensible to keep this order, as it will be used in all the examples of the IPS.
Das Verhalten der Simulation wird im wesentlichen durch die Parameter, die in den Blöcken eingegeben werden, bestimmt. So sind beispielsweise die Irrtumswahrscheinlichkeiten des Tests und die gewünschte statistische Power Fallzahlberechnung Parameter, die in den entsprechenden Blöcken angegeben werden. Die Angabe der Parameter erfolgt dabei über den Namen und den Wert.
\lstinputlisting[caption={Angabe der Irrtumswahrscheinlichkeiten}] {code/ergebnisse/beschreibung/parameter_alpha_beta.rb}
Als Namen von Parametern müssen Bezeichner gewählt werden, die aus den 26 Buchstaben des Alphabets bestehen. Das erste Zeichen darf ein großer oder kleiner Buchstabe sein, die folgenden Zeichen müssen kleine Buchstaben sein.
Neben der einfachen Angabe der Parameter bietet die domänenspezifische Sprache die Möglichkeit, mehrere Werte für einen Parameter anzugeben.
\lstinputlisting[caption={Multiple Parameter}] {code/ergebnisse/beschreibung/multiple_parameter.rb}
Diese Angabe -- im folgenden als multiple Parameter bezeichnet -- führt dazu, dass die Simulationsstudie mehrere Situationen enthält und zwar jeweils mit dem entsprechend veränderten Parameter.
Es gibt einen fundamentalen Unterschied zwischen dem Verhalten der multiplen Parameter innerhalb des \lstinline|arms| Block und den \lstinline|test|, \lstinline|sample_size| und \lstinline|internal_pilot| Blöcken.
Im \lstinline|arms| Block werden die einzelnen multiplen Parameter einander zugeordnet. Beispielsweise werden bei Angabe von \lstinline|treatment N([0.5,1.0], 1)| und \lstinline|placebo N([0.0, 0.5], 1)| zwei Simulationsserien durchgeführt, einmal mit
In den Blöcken \lstinline|test|, \lstinline|sample_size| und \lstinline|internal_pilot| wird das kartesische Produkt der multiplen Parameter gebildet. So werden bei Angabe von \lstinline|alpha 0.025, 0.05| und \lstinline|beta 0.1, 0.2| vier Simulationsserien durchgeführt, jeweils mit
Zwischen den einzelnen Blöcken wird ebenfalls das kartesische Produkt gebildet.
Die bisher dargestellten Parameter sind numerische Parameter, das heißt sie haben einen numerischen Wert. Neben den numerischen Parametern gibt es boolesche Parameter und Bereichs-Parameter.
Numerische Parameter definieren einen numerischen Wert. Sie können -- wie bereits dargestellt -- einfach durch den Namen und die Angabe eines Wertes, bzw. im multiplen Fall, durch Angabe der Werteliste definiert werden. Zusätzlich gibt es noch die folgenden Möglichkeit welche die Lesbarkeit der domänenspezifischen Sprache erhöhen soll:
\lstinputlisting[caption={Sprechende numerische Parameter}] {code/ergebnisse/beschreibung/numerische_parameter_sprechend.rb}
Boolesche Parameter definieren einen Wahrheitswert (\lstinline|wahr = true|, \lstinline|falsch = false|). Sie können ebenso wie numerische Parameter direkt angegeben werden. Es gibt jedoch auch die Möglichkeit, keinen Parameter anzugeben, was \lstinline|true| bedeutet, oder den Namen mit \lstinline|not_|, \lstinline|in| oder \lstinline|un| voranzustellen, was \lstinline|false| bedeutet.
\lstinputlisting[caption={Boolesche Parameter}] {code/ergebnisse/beschreibung/boolesche_parameter.rb}
Die Angabe von multiplen Parameter geschieht, wie in der letzten Zeile dargestellt, durch den Variablennamen und die Angabe von \lstinline|true, false|.
Bereichs-Parameter definieren einen Wertebereich, in dem eine Eigenschaft gilt. Typischer Anwendungsfall ist der Äquivalenzbereich bei einer Äquivalenzfragestellung. Ein Bereichs-Parameter kann durch eine Ruby Range angegeben werden.
\lstinputlisting[caption={Bereichs-Parameter}] {code/ergebnisse/beschreibung/range_parameter.rb}
Die Angabe von multiplen Parametern geschieht, wie in der letzten Zeile dargestellt, durch mehrere Range-Angaben, die durch Kommata von einander getrennt sind.
Innerhalb des \lstinline|arms| Blocks werden die zu simulierenden Arme der Simulationsstudie definiert. Die Definition eines Arms sieht wie folgt aus:
\lstinputlisting[caption={Definition eines Studienarms}] {code/ergebnisse/beschreibung/arms_definition.rb}
Hier wird ein Arm mit dem Namen \lstinline|treatment| angelegt und als Vorgabe für das Erzeugen der Werte dieses Arms ein Zufallszahlgenerator mit Normalverteilung bei
Die verschiedenen Arme können einfach untereinander geschrieben werden. Sollen Arme mit unterschiedlicher Größe verwendet werden, kann als Parameter ein relatives Gewicht angegeben werden:
\lstinputlisting[caption={Drei Studienarme mit unterschiedlichen Gewichten}] {code/ergebnisse/beschreibung/arms_three_and_weight.rb}
In diesem Fall wäre der \lstinline|treatment|-Arm fünfmal so groß und der \lstinline|control|-Arm viermal so groß wie der \lstinline|placebo|-Arm. Wie viele Werte tatsächlich pro Arm simuliert werden hängt von der definierten bzw. berechneten Fallzahl ab.
Der Zugriff auf die Arme erfolgt intern, jedoch nicht auf Grund der Namen der Arme, sondern auf Grund folgender Reihenfolge:
\begin{enumerate} \item \lstinline|treatment| \item \lstinline|control| \item \lstinline|placebo| \item Arme mit einem anderen Namen. \end{enumerate}
Die Sprache verhält sich dabei in soweit flexibel, als auch Arme wie \lstinline|treatment1| in diese Reihenfolge mit einbezogen werden -- Arme, die in die gleiche Kategorie gehören werden lexikalisch sortiert. So hätte beispielsweise folgender Quellcode die Reihenfolge \lstinline|treatment1, treatment2, control|\footnote{In dem angegebenen Beispiel ist die Reihenfolge vertauscht eingegeben. Dies ist im Allgemeinen keine gute Praxis und sollte daher vermieden werden.}:
\lstinputlisting[caption={Reihenfolge der Arme}] {code/ergebnisse/beschreibung/arms_order.rb}
Soll eine andere Reihenfolge verwendet werden, so kann dies durch folgende Anweisungen angepasst werden:
\lstinputlisting[caption={Reihenfolge der Arme (angepasst)}] {code/ergebnisse/beschreibung/arms_order_defined.rb}
Wenn eigene Test- und Fallzahl Methoden verwendet werden, ist eine Anpassung der Reihenfolge nicht zwingend notwendig, da auf die Arme auch direkt über den Namen zugegriffen werden kann.
Der \lstinline|test|- und der \lstinline|sample_size| Block sind aus verschiedenen Gründen sehr ähnlich. Zum einen verwenden beide dieselbe Strategie für die Durchführung des Tests bzw. der Fallzahlberechnung, zum anderen werden die angegebenen Parameter in beiden Blöcken gleich verwendet.
Die Auswahl der Strategie erfolgt im Block \lstinline|test| -- entweder durch die Angabe von Eigenschaften oder durch die explizite Angabe der Strategie:
\lstinputlisting[caption={Wahl der Test- und Fallzahlstrategie anhand von Eigenschaften}] {code/ergebnisse/beschreibung/test_strategy_durch_constraints.rb}
\lstinputlisting[caption={Wahl der Test- und Fallzahlstrategie durch direkte Angabe}] {code/ergebnisse/beschreibung/test_strategy_direkt.rb}
\lstset{language=Bash} Die verfügbaren Strategien, wie sie ausgewählt werden können und welche Parameter sie erwarten, kann über den Befehl \lstinline|script/list_strategies| oder über die API-Dokumentation auf der beiliegenden CD ermittelt werden. Abschnitt \ref{SEC:WEITERERE_TEST_UND_FALLZAHLSTRATEGIEN} beschreibt, wie weitere Strategien zum Werkzeug hinzugefügt werden können. \lstset{language=Ruby}
Soll allerdings eine neue Test- oder Fallzahlstrategie ausprobiert werden, kann dies auch im Rahmen der domänenspezifische Sprache vorgenommen werden. Sowohl der \lstinline|test| als auch der \lstinline|sample_size| Block bietet diese Möglichkeit.
Diese Möglichkeit sollte nur dazu verwendet werden, das Verhalten im Rahmen der Untersuchung dieser Methoden auszutesten. Die in das Werkzeug integrierten Strategien können durch Memoization (vgl. Abschnitt \ref{SEC:MEMOIZATION}) und Thread-Sicherheit (vgl. Abschnitt \ref{SEC:PARALELLISIERUNG}) in der Regel schneller durchgeführt werden.
Wenn die Test-Prozedur einer Strategie überschrieben werden soll bzw. die Test-Strategie keine Test-Prozedur definiert\footnote{Dies kann durch Angabe von \lstinline|strategy :none| im \lstinline|test|-Block erreicht werden.}, kann mithilfe der \lstinline|method| Anweisung eine Test-Prozedur angegeben werden.
\lstinputlisting[caption={Überschreiben der Test-Prozedur (zweiseitiger superiority t-Test)}] {code/ergebnisse/beschreibung/test_prozedur_ttest.rb}
Die Test-Prozedur wird in einem Ruby-Block implementiert. Als erstes Argument wird der gesamte Datensatz als Objekt der Klasse \lstinline|Sim::DataArray| übergeben. Die weiteren Argumente sind die Samples der Arme als Objekte der Klasse \lstinline|Sim::Data|. Die im Block \lstinline|test| angegebenen Parameter der Test-Prozedur sind als Instanzvariablen zugänglich. Die statistischen Verteilungen aus dem Modul \lstinline|Distribution| stehen direkt zur Verfügung und können verwendet werden.
Als Rückgabewert oder als letzter berechneter Ausdruck muss angegeben werden, ob der Test die Nullhypothese abgelehnt hat oder nicht.
Die Details zu den einzelnen Aufrufen können in der API-Dokumentation nachgelesen werden.
Soll die Fallzahlberechnung der Strategie angepasst werden bzw. ist keine Strategie definiert, so kann durch Angabe von \lstinline|method| innerhalb des \lstinline|sample_size| Blocks die Berechnung der Fallzahl definiert werden.
\lstinputlisting[caption={Überschreiben der Fallzahlberechnung (Normalapproximation)}] {code/ergebnisse/beschreibung/sample_size_prozedur_gauss.rb}
Der definierende Block hat keine Argumente; alle im \lstinline|test| und \lstinline|sample_size| Block definierten Parameter stehen als Instanzvariablen zu Verfügung, wobei diese gegebenenfalls durch das interne Profiling überschrieben worden sind. Die statistischen Verteilungen aus dem Modul \lstinline|Distribution| können, ebenso wie im Block \lstinline|test|, direkt verwendet werden.
Als Rückgabewert oder als letzter berechneter Ausdruck wird die Gesamtfallzahl erwartet. Die Verteilung auf die einzelnen Arme wird anhand der Gewichtungen vorgenommen.
Die Details zu den einzelnen Aufrufen können in der API-Dokumentation nachgelesen werden. Wenn eine iterative Fallzahl berechnet werden soll, können die Hilfsmethoden des \lstinline|TestStrategy::IterationHelper| Moduls verwendet werden.
Die Parameter der Test-Prozedur bzw. der Fallzahlberechnung, etwa die Irrtumswahrscheinlichkeit oder die Stichprobenvarianz, können durch einfache Definition der Parameter in den Blöcken definiert werden.
\lstinputlisting[caption={Angabe von Parametern im \lstinline|test| und \lstinline|sample_size| Block}] {code/ergebnisse/beschreibung/test_parameter.rb}
Die definierten Parameter stehen sowohl in der Test-Prozedur als auch in der Fallzahlberechnung zur Verfügung. Werden Parameter der Arme, also zum Beispiel die Varianz, nicht angegeben, so werden diese aus den angegebenen Armen berechnet. Die Irrtumswahrscheinlichkeiten und andere konfigurierbaren Eigenschaften der Tests haben Standardwerte.
Diese berechneten Werte können gezielt durch \lstinline|add_to_| oder \lstinline|remove_from_| Anweisungen verändert werden, sodass das Verhalten der Prozeduren untersucht werden kann, wenn falsche Parameter angenommen werden.
\lstinputlisting[caption={Verwenden der \lstinline|add_to_| und \lstinline|remove_from_| Anweisungen}] {code/ergebnisse/beschreibung/ss_parameter_add_remove.rb}
Im \lstinline|sample_size| Block kann mit dem Parameter \lstinline|initial| eine initiale Gesamtfallzahl angegeben werden. Im ersten Schritt wird dann keine Fallzahl berechnet, sondern die angegebene Fallzahl verwendet. Mit dem Parameter \lstinline|fixed| kann eine feste Fallzahl gewählt werden, die auch durch eine interne Pilot-Studie nicht angepasst werden darf.
Mithilfe des Block \lstinline|internal_pilot| kann die Fallzahladaption während einer Simulationsstudie definiert werden. Zunächst muss mit dem Parameter \lstinline|at| angegeben werden, nach wie vielen Samples die Adaption stattfinden soll. Dabei kann sowohl eine feste Größe angegeben werden oder eine Anteil zwischen 0.0 und 2.0, der sich auf die initial geplante Fallzahl bezieht. Mit \lstinline|adjust| wird angegeben, welche Parameter der Fallzahlberechnung angepasst werden sollen --- zur Zeit stehen \lstinline|:variance| und \lstinline|:delta| zur Verfügung.
Mit den boole'schen Parametern \lstinline|blinded| und \lstinline|restricted| kann angegeben werden, ob die Fallzahladaption verblindet oder unverblindet\footnote{Bei verblindeter Adaption ist eine Anpassung von
\lstinputlisting[caption={Interne Pilot-Studie bei 50% bzw 100% der geplanten Fälle}] {code/ergebnisse/beschreibung/internal_pilot.rb}
Der \lstinline|internal_pilot| Block kann weggelassen werden. In diesem Fall findet keine Fallzahladaption statt. Soll eine Fallzahladaption durchgeführt werden und eine Kontrolle ohne Fallzahladaption simuliert werden, kann der booleschen Parameter \lstinline|control| angegeben werden.
Innerhalb des \lstinline|collect| Block kann angegeben werden, welche Informationen aus den simulierten Werten ermittelt und gespeichert werden sollen. Anhand dieser Informationen kann dann die Auswertung der Simulation vorgenommen werden.
Der \lstinline|collect| Block hat zwei Unterblöcke. Den \lstinline|series| Block, in dem die zu erhebenden Größen einer Simulationsserie angegeben werden können und den \lstinline|single| Block, in dem die zu erhebenden Größen eines Simulationslaufs angegeben werden können.
Im \lstinline|collect| Block kann mit \lstinline|folder| angegeben werden, in welchen Ordner die Dateien geschrieben werden sollen, die bei der Simulation erstellt werden. Wird der Ordner nicht angegeben, so wird das temporäre Verzeichnis des Betriebssystems verwendet.
Mit \lstinline|name| kann angegeben werden, welcher Name den Dateien gegeben werden soll, die bei der Auswertung anfallen. Wenn dies nicht angegeben wird, wird der Name der Datei verwendet, in dem die Simulationsstudie definiert wurde.
\lstinputlisting[caption={Struktur des \lstinline|collect| Blocks}] {code/ergebnisse/beschreibung/struktur_des_collect_block.rb}
Die erhobenen Größen werden in \glslink{glossar:comma_seperated_values}{CSV} Dateien abgelegt. Die Datei, in der die Größen der Serie stehen, erhalten bei dem obigen Quellcode den Namen alpha_two_sided.csv. Die Dateien in denen die einzelnen Serien abgelegt werden, haben zusätzlich einen laufende Nummer, also zum Beispiel alpha_two_sided.5.csv.
Im \lstinline|series| Block können die zu erhebenden Größen einer Simulationsserie definiert werden. In Tabelle \ref{TBL:DIREKTE_GROESSEN_SERIES} sind die möglichen direkten Größen aufgelistet.
\begin{table}[htb]\begin{tabularx}{420pt}{lX} \toprule Größen & Beschreibung\ \midrule \lstinline|runs|, \lstinline|count| & Anzahl der Simulationsläufe die zu einer Serie gehören.\ \lstinline|reject_count| & Anzahl der abgelehnten Hypothesen. \ \lstinline|non_reject_count| & Anzahl der nicht abgelehnten Hypothesen.\ \lstinline|alpha|, \lstinline|power| & Fehler erster Art bzw. die Power des Tests. \ \lstinline|beta| & Fehler zweiter Art.\ \bottomrule \end{tabularx} \caption{Direkte Größen des \lstinline|series| Block} \label{TBL:DIREKTE_GROESSEN_SERIES} \end{table}
Zusätzlich zu den direkten Größen können noch verschiedene zusammenfassende Größen über die Größen der Simulationsläufe ermittelt werden. Die Angabe erfolgt über den Namen der zusammenfassenden Methode und den Namen der auszuwertenden Größe als Ruby Symbol. Die möglichen Auswertungen sind in Tabelle \ref{TBL:STATISTISCHE_GROESSEN_SERIES} angegeben. Über alle Größen, die im \lstinline|single| Block angegeben werden können(siehe Tabelle \ref{TBL:GROESSEN_SINGLE}), sind zusammenfassende Auswertungen möglich.
\begin{table}[htb]\begin{tabularx}{420pt}{lX} \toprule Größen & Beschreibung\ \midrule \lstinline|mean| & Das arithmetische Mittel.\ \lstinline|variance| & Die Variance.\ \lstinline|median| & Der Median.\ \lstinline|quartiles| & Die Quartile.\ \lstinline|min| & Das Minimum.\ \lstinline|max| & Das Maximum.\ \lstinline|quantile| & Die im zweiten Parameter als \lstinline|Array| angegebenen Quantile.\ \bottomrule \end{tabularx} \caption{Zusammenfassende Auswertungen des \lstinline|series| Blocks} \label{TBL:STATISTISCHE_GROESSEN_SERIES} \end{table}
Im folgendem ist ein Beispiel für den \lstinline|series| Block angegeben.
\lstinputlisting[caption={Definition der zu erhebenden Größen im \lstinline|series| Block}] {code/ergebnisse/beschreibung/series_block.rb}
Um die zusammenfassenden Auswertungen für eine Simulationsserie durchführen zu können, müssen die Werte jedes Simulationslaufs im Arbeitsspeicher vorgehalten werden(für eine Größe sind etwa 200MB Arbeitsspeicher für
Im \lstinline|single| Block können die zu erhebenden Größen eine Simulationsserie angegeben werden. In Tabelle \ref{TBL:GROESSEN_SINGLE} werden diese angegeben -- die Angabe im Quellcode der domänenspezifischen Sprache erfolgt analog zum \lstinline|series| Block.
\begin{table}[htb]\begin{tabularx}{420pt}{lX} \toprule Größen & Beschreibung\ \midrule \lstinline|mean| & Das arithmetische Mittel aller Samples. \ \lstinline|variance| & Die one-sample-Varianz über alle Samples. \ \lstinline|delta| & Die Differenz der ersten beiden Studienarme \ \lstinline|pooled_variance| & Die gepoolte Varianz der Stichproben. \ \lstinline|median| & Der Median aller Samples. \ \lstinline|statistics| & Die Test-Statistik.\ \lstinline|rejected| & Gibt an, ob die Nullhypothese abgelehnt wurde. \ \lstinline|sample_sizes| & Die Fallzahl jedes Abschnitts des Simulationslaufs. \ \lstinline|sample_size| & Die Gesamtfallzahl des Simulationslaufs. \ \lstinline|means| & Die arithmetischen Mittelwerte jedes Studienarms. \ \lstinline|variances| & Die Varianz jedes Studienarms. \ \lstinline|medians| & Die Mediane jedes Studienarms. \ \bottomrule \end{tabularx} \caption{Größen des \lstinline|single| Blocks} \label{TBL:GROESSEN_SINGLE} \end{table}