Skip to content
This repository was archived by the owner on Jan 14, 2022. It is now read-only.

Simulation definition

jthoenes edited this page May 31, 2011 · 4 revisions

Introduction

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

Root Structure of the Definition

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.

Parameter in der domänenspezifischen Sprache

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 $X_T \sim N(0.5, 1.0)$ und $X_P \sim N(0.0, 1.0)$ sowie einmal mit $X_T \sim N(1.0, 1.0)$ und $X_P \sim N(0.5, 1.0)$. Die Angabe der multiplen Parameter innerhalb des \lstinline|arms| Blocks, erfolgt innerhalb von Array-Klammern \lstinline|[]|.

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 $(\alpha=0.025, \beta=0.1)$, $(\alpha=0.025, \beta=0.2)$, $(\alpha=0.05, \beta=0.1)$ und $(\alpha=0.05, \beta=0.2)$. Die Angabe der multiplen Parameter innerhalb der Blöcke \lstinline|test|, \lstinline|sample_size| und \lstinline|internal_pilot| erfolgt durch Trennung mit Kommata oder ebenfalls durch Array-Klammern \lstinline|[]|.

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

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

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

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.

Der arms Block

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 $\mu=0.0$ und $\sigma^2=1.0$.

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 test und der sample_size Block

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.

Überschreiben der Test-Prozedur im test Block

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.

Überschreiben der Fallzahlberechnung im sample_size Block

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.

Default-Parameter und Parameter-Anpassung

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.

Der internal_pilot Block

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 $\delta$ natürlich nicht möglich.}, beziehungsweise, ob sie beschränkt oder unbeschränkt stattfinden soll.

\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.

Der collect Block

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.

The series

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 $10^7$ Simulationsläufe notwendig\footnote{Das Werkzeug braucht etwa 15MB ohne diese Größen}). Es sollte daher beim Durchführen der Simulation berücksichtigt werden, dass genügend Arbeitsspeicher zur Verfügung steht\footnote{Der verfügbare Arbeitsspeicher kann in JRuby durch die Option \texttt{-J-Xmx2048m} zum Beispiel auf 2GB gesetzt werden.}. Ansonsten bricht die Simulation mit einem Fehler ab. Falls auf dem System nicht genügend Arbeitsspeicher vorhanden ist, können die Größen der Simulationsläufe mithilfe des \lstinline|single| Block auf die Festplatte gespeichert werden und die Auswertungen im Nachhinein durchgeführt werden.

The single block

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}