Filter- und Plausibilitätsbedingungen sind zentrale
Mechanismen in Q., um den Befragungsablauf zu steuern
und Abbruchkriterien zu formulieren. Filter regeln, ob
den Teilnehmern einzelne Label, Fragen, Screens oder
auch ganze Blöcke vorgelegt werden sollen oder nicht.
Plausibilitätsbedingungen (asserts
)
können an einzelne Screens gebunden werden, um zu
überprüfen, ob bestimmte Bedingungen an dieser Stelle
erfüllt sind. Davon abhängig kann dem Befragten der
jeweilige Screen erneut vorgelegt oder das Interview
beendet werden. Da sowohl Filter- als auch
Plausibilitätsbedingungen auf der Grundlage von
Bedingungen arbeiten, geht es zunächst darum, wie sich
die Bedingungen formulieren lassen.
Logische Bedingungen in Q. richten sich nach Boolescher
Algebra. Ihre Auswertung ergibt am Ende immer
wahr oder
falsch. Für die beiden
Wahrheitswerte stehen die Ausdrücke
true
und false
zur Verfügung. Die einfachsten Bedingungen sind
demnach:
(true) // trifft immer zu (false) // trifft nie zu
In der Booleschen Algebra lassen sich zwei
(Teil-)Bedingungen mit dem logischen
UND (and
) und
dem logischen ODER
(or
) verknüpfen. Damit die gesamte
Bedingung am Ende wahr bzw.
erfüllt ist, müssen bei UND beide
Teilbedingungen wahr sein,
während bei ODER wenigstens eine
der Teilbedingungen erfüllt sein muss. Zusätzlich gibt
es noch ENTWEDER ODER
(xor
), bei dem genau eine der
Teilbedingungen erfüllt sein muss.
Im Gegensatz zu and
,
or
und xor
ist
auch eine Negation (not(X)
)
möglich, die den Wahrheitswert von
X
umkehrt. In der Regel wird eine
Verkettung von mehreren dieser Operatoren von links
nach rechts abgearbeitet. Durch Klammersetzung kann
der Vorrang, wie in der Mathematik, aber auch anders
gesetzt werden. Folgende Matrizen fassen zusammen, wie
sich die Operatoren verhalten:
AND | T | F OR | T | F XOR | T | F NOT | T | F ---- --- --- ---- --- --- ---- --- --- ---- --- --- T | T | F T | T | T T | F | T | F | T ---- --- --- ---- --- --- ---- --- --- F | F | F F | T | F F | T | F
Einzelne Bedingungen ergeben sich hauptsächlich durch Vergleiche von Variablenwerten mit Zahlen oder anderen Variablenwerten. Dafür existieren folgende Operatoren:
Beispiele:
(1 eq 1) // wahr (= true) (1 ne 1) // falsch (= false) (1 eq 2) // falsch (= false) (s eq 1) // wahr, wenn s = 1 (s gt 1) // wahr, wenn s > 1 (1 in m) // wahr, wenn 1 in m vorkommt ([1 2 3] in m) // wahr, wenn (1 in m) or (2 in m) or (3 in m) ([1:3] in m) // = ([1 2 3] in m) ([1:3 8] in m) // = ([1 2 3] in m) or (8 in m)
Es gibt einen grundlegenden Unterschied zwischen den Vergleichsoperatoren (größer / kleiner / gleich) einerseits und der Prüfung, ob ein Array einen bestimmten Wert enthält. Gemeint sind damit alle Fragetypen, die ihre Werte in Arrays ablegen (NumQ, MultiQ, SingleGridQ, MultiGridQ, s. „Fragetypen“).
Die Prüfung auf einen Wert mit in
ergibt in diesem Beispiel true
,
wenn 1 in der Wertemenge f2 enthalten ist, z.B.
enthält f2 die Werte "1 2 3":
flt=(1 in f2); // true da 1 in "1 2 3" enthalten ist
Dagegen bildet Q. bei anderen Vergleichsoperatoren (größer / kleiner / gleich) die Summe der Werte im Array bzw. der Frage (ohne die _missing-Werte).
flt=(f2 eq 6); // true da 1+2+3 gleich 6
Der Summenmechanismus kann sich als hilfreich erweisen, wenn etwa die Summe der Eingaben einer NumQ eine Rolle spielt.
Q. stellt einige Funktionen bereit, die speziell auf Bedingungen abzielen und sich mit logischen Operatoren und einfachen Vergleichen kombinieren lassen:
containsText
prüft ob
TEXT
im TextArrayTARRAY
enthalten ist. Liefert 1 zurück, wenn dies der Fall ist, sonst 0.containsText(TARRAY, TEXT);
if(containsText(arr, "ist drin?") eq 1){ // ... "ist drin?" ist drin }
count
zählt wie oft ein Wert
VAL
inARRAY
enthalten ist und liefert das Ergebnis zurück.count(ARRAY, VAL);
if(count(arr, 7) gt 2){ // ... die 7 ist mindestens 3 mal in arr enthalten }
exists
prüft, ob die angegebene Variable VAR im Skript existiert.
exists(VAR);
// Prüft, ob Frage f1 existiert und ein Label mit Code 7 definiert ist flt=(exists(f1) and hasLabel(f1, 7));
hasDuplicateValues
testet, ob ein Wert in
ARRAY
mehrfach enthalten ist. Ist dies der Fall, liefert der Aufruf eine 1 zurück, andernfalls eine 0.hasDuplicateValues(ARRAY);
if(hasDuplicateValues(arr) eq 1){ // ... es gibt Duplikate };
hasLabel
prüft, ob für Frage Q ein Label mit Code CODE definiert ist.
hasLabel(Q, CODE)
// Prüft, ob Frage f1 existiert und ein Label mit Code 7 definiert ist flt=(exists(f1) and hasLabel(f1, 7));
num
liefert die Anzahl der in ARRAY enthaltenen Werte, die ungleich
_missing
sind.num(ARRAY);
// Inhalt von zehnFelder: {_missing, 6, 3, _missing, _missing, _missing, 8, 1, 1, 2} num(zehnFelder) // liefert 6 if(num(arr) ge 1){ // ... enthält arr wenigstens einen Wert ungleich _missing? }
regexp
sucht ein Muster (einen regulären Ausdruck bzw. regular expression, daher auch »regex«) in einer Zeichenkette. Wenn das Muster in der Zeichenkette auftaucht, liefert regexp den Wert 1 zurück.
regexp( MUSTER, TEXT )
Das folgende Beispiel soll eine eingegebene E-Mail-Adresse überprüfen und gibt je nach Ergebnis einen anderen Text zurück:
group grp_check; labels= 1 "Adresse scheint korrekt" (regexp ("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", adr) eq 1) 2 "Bitte Adresse korrigieren" (regexp ("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", adr) ne 1) ;
E-Mail-Adressen sollte man nicht auf diese Weise prüfen, weil sie noch anders aussehen können als das Muster es beschreibt, aber der Ausdruck taugt zur Erläuterung:
Demnach besteht die E-Mail-Adresse aus drei Feldern. Das erste reicht vom Anfang der Zeichenkette (bezeichnet mit
^
) bis zum@
und würde durch die Kombination von[a-zA-Z0-9_.+-]
Kleinbuchstaben von a bis z und Großbuchstaben von A bis Z sowie Zahlen von 0 bis 9 und die Zeichen Unterstrich, Punkt, Plus, Minus berücksichtigen.Das zweite Feld hinter dem
@
berücksichtigt erneut die Buchstaben- und Zahlenkombination sieht aber als zusätzliches Zeichen nur ein Minus vor. Darauf folgt ein Punkt. Da der Punkt in regulären Ausdrücken selbst als Platzhalter für ein beliebiges Zeichen stehen kann, muss man eine Escape-Sequenz (\\
)[2] verwenden, um den Punkt als Punkt anzusprechen.Das letzte Feld bis zum Ende der Zeichenkette (bezeichnet durch
$
) kann nun wiederum die Buchstaben- und Zahlenkombination enthalten und berücksichtigt zusätzlich Minus und Punkt (innerhalb der eckigen Klammern braucht es keine Escape-Sequenz für den Punkt).Das Plus-Zeichen vor dem
@
, dem.
und dem$
, legt fest, dass der vorangehende Ausdruck mindestens einmal in der Zeichenkette vorkommen muss und mehrfach vorkommen kann.Das Beispiel legt es bereits nahe, aber es dürfte sinnvoll sein, auch die vergleichsweise einfachen Einsatzmöglichkeiten hervor zu heben: Mit dem regulären Ausdruck
^123
untersuchtregexp
, ob die Zeichenkette mit123
beginnt. Dagegen liefertregexp
mit dem Mustercom$
immer dann eine1
, wenn die Zeichenkette mitcom
endet.Reguläre Ausdrücke sind ein mächtiges Werkzeug und können hier nicht erschöpfend erklärt werden. Aber im Netz finden sich ausführliche Erläuterungen und Beispiele. Einen Ausgangspunkt bietet Wikipedia: https://de.wikipedia.org/wiki/Regulärer_Ausdruck.
strcmp
vergleicht TEXT1 und TEXT2 Zeichen für Zeichen. Sobald
strcmp
in TEXT1 und TEXT2 ein unterschiedliches Zeichen findet, liefert es die Differenz ihrer ASCII Codes zurück. Zwei identische Texte liefern demnach den Wert 0 zurück, unterschiedliche Texte einen Wert ungleich 0.strcmp(TEXT1, TEXT2);
if(strcmp("f12", _currentScreen) eq 0){ // ... ist f12 der aktuelle Screen? }
timesSent
gibt zurück, wie oft die Antwort auf eine Frage bereits mit »Weiter« an den Server übermittelt wurde. Mit der Funktion lässt sich z.B. das Verhalten abfangen, Fragen schlicht zu überspringen: Wenn eine Frage einmal unbeantwortet abgeschickt wurde, greift der Filter und im Screen wird eine zweite, präzisierende Frage gestellt. Auf diese Weise können auch Labels gefiltert werden, die dann erst erscheinen, wenn die Bedingung erfüllt ist.
timesSent(QUESTION);
flt(timessent(f7.1) ge 1 and f7 le 0);
Filter sorgen dafür, dass ein Element nur dann angezeigt wird, wenn eine Bedingung erfüllt ist. Die Syntax hierfür richtet sich nach dem Zusammenhang, in dem gefiltert wird: Frage / Screen / Block oder Label / Group.
Screens oder Blöcke, die leer sind, weil alle darin zusammengefassten Fragen gefiltert wurden, lässt Q. aus. Das gleiche gilt für leere Fragen, also solche, in denen alle Labels ausgefiltert wurden.
Wichtig für die Datenablage: Beim Überspringen von gefilterten Fragen werden sie explizit auf den _missing-Wert gesetzt.
Die allgemeine Formulierung für einen Filter, der sich auf eine Frage bezieht, lautet:
flt = BEDINGUNG;
Diese Anweisung muss direkt hinter der Definition der Frage, des Screens oder des Blocks stehen, zu dem sie gehören soll. Ein Element dieser Art kann höchstens einen Filter haben. Mehrere Bedingungen können mit den verschiedenen Operatoren verknüpft werden.
Beispiele:
flt=99 in marken; // "marken" muss Wert 99 enthalten flt=[1 3 5 7 9] in abc; // "abc" muss mindestens einen der Werte 1, 3, 5, 7 oder 9 enthalten flt=[1:7] in def; // "def" muss mindestens einen der Werte zwischen 1 und 7 enthalten flt=99 in marken or [1:7] in def or f1 eq 2; // "marken" muss den Wert 99 enthalten ODER // "def" muss mindestens einen der Werte zwischen 1 und 7 enthalten ODER // "f1" soll den Wert 2 haben
Um eine einzelne Frage zu filtern fügt man die Filterbedingung einfach der Fragedefinition hinzu:
SingleQ audi_bekannt; Text="Kennen Sie die Marke Audi?"; labels= 1 "ja" 2 "nein" ; SingleQ audi_einstufung; Text="Und wie gefällt Ihnen die Marke Audi?"; labels= 1 "gut" 2 "schlecht" ; flt = audi_bekannt eq 1;
Es ist auch möglich, einen Block zu filtern, beispielsweise eine Liste von Fragen zu einer Marke, die nur gestellt wird, wenn die Marke bekannt ist:
Block audi_items = (audi_item_1 audi_item_2 audi_item_3 audi_item_4) random; flt=audi_bekannt eq 1;
Filter nehmen Einfluss auf den Ablauf eines
Interviews, der häufig über einen Fortschrittsbalken
veranschaulicht wird. Um den Balken mit dem Ablauf
zu synchronisieren, sollten Filter an Fragen,
Blöcken oder Screens zusätzlich die
Funktion changepBar(NUM)
aufrufen
und die Anzahl der übersprungenen Fragen übergeben.
flt = BEDINGUNG changepBar(NUM);
Block bmw_items = (bmw_item_1 bmw_item_2 bmw_item_3 bmw_item_4) random; flt=bmw_bekannt eq 1 changepbar(4);
Auch Labels lassen sich filtern. Die Syntax bietet verschiedene Variationen, die alle gleichbedeutend sind.
1 "label" (bedingung) 2 "label" flt(bedingung) 3 "label" flt=(bedingung)
singleq s1; text="Welche Zeitung lesen sie am häufigsten?"; title=""; labels= 1 "SZ" 2 "taz" 3 "FAZ" 4 "WAZ" 5 "FR" ; singleq s2; text="Und welche Zeitung lesen sie am zweithäufigsten?"; title="Titel"; labels= 1 "SZ" (not (1 in s1)) 2 "taz" flt(not (2 in s1)) 3 "FAZ" flt=(not (3 in s1)) 4 "WAZ" flt(not (4 in s1)) 5 "FR" flt(not (5 in s1)) ;
In s2 wird die bereits in s1 genannte Zeitung ausgefiltert. Sollten bei einer anderen Konstruktion sämtliche Label einer Frage ausgefiltert sein, stellt Q. die Frage nicht.
Wie bei Labelfiltern stehen verschiedene Varianten zur Verfügung Filter zu definieren:
group grpname; labels= 1 "L1" (bedingung1) 2 "L2" flt(bedingung2) 2 "L2" flt=(bedingung2) ;
Gefilterte Gruppen sind ein bewährter Mechanismus für den Textersatz.
group zeitung1; labels= 1 "SZ" (1 in s1) 2 "taz" flt(2 in s1) 3 "FAZ" flt=(3 in s1) 4 "WAZ" (4 in s1) 5 "FR" (5 in s1) ; singleq s1; text="Welche Zeitung lesen sie am häufigsten?"; title=""; labels= 1 "SZ" 2 "taz" 3 "FAZ" 4 "WAZ" 5 "FR" ; singleq s2; text="Welche Zeitung lesen sie nach der @insert(zeitung1) am zweithäufigsten?"; title="Titel"; labels= 1 "SZ" (not (1 in s1)) 2 "taz" flt(not (2 in s1)) 3 "FAZ" flt=(not (3 in s1)) 4 "WAZ" flt(not (4 in s1)) 5 "FR" flt(not (5 in s1)) ;
In diesem Beispiel wird die Gruppe nur genutzt, um
die Antwort in s1 in Frage s2 zu verwenden. Das
Gleiche wäre auch mit dem Standard-Textersatz
@insert(s1)
möglich gewesen. Aber
der wesentliche Unterschied zum normalen Textersatz
besteht in der Möglichkeit, Bedingungen zu
formulieren, die durchaus komplex ausfallen können.
GESStabs kennt Filter, die mit Text versehen sind.
Wenn die Daten mit GESStabs weiter verarbeitet
werden, kann GESStabs Filter samt Text aus dem
Fragebogen übernehmen. Der Text wird für GESStabs in
die Datei var.inc
(s. „Variablendefinition (var.inc)“) exportiert.
flt = ( bedingung ) "Filtertext";
In verschiedenen Konstellationen kann es erwünscht
sein, Filter nicht in die
var.inc
zu übernehmen. Sei es,
dass die Syntax im Skript nicht mit GESStabs
vereinbar ist oder eine Frage in verschiedenen
Blöcken auftaucht und damit als zwei verschiedene
Variablen betrachtet werden. In solchen Fällen kann
der Export durch das Stichwort
noexport
verhindert werden.
flt = ( bedingung ) "Filtertext" noexport;
Als Alternative zu dem herkömmlichen flt
an Fragen,
Screens und Blöcken, kann auch ein setfilter-endfilter
-Konstrukt
verwendet werden.
Dieser Befehl ist identisch zu dem in GESStabs
.
Hierbei wird der Filter um den Skriptteil geschrieben. Mit setfilter = CONDITION
öffnet und mit endfilter
schließt sich der Befehl.
Eine Definition des Namens und eine Übergabe des Textes ist optional.
setfilter [NAME] [text "TEXT"] = CONDITION; // Fragen, Screens, Blöcke endfilter [NAME];
Plausibilitätsbedingungen (assertions) geben an, bei
welchen Bedingungen eine Frage (oder ein Screen/Block)
wieder verlassen werden darf. Wird die Bedingung nicht
erfüllt, bleibt das Interview unter Ausgabe einer
Fehlermeldung beim aktuellen Screen stehen. Ergänzend
dazu ist es auch möglich, Plausibilitätsbedingungen so
zu definieren, dass bei deren Nichterfüllung das
Interview beendet wird (z.B. wenn die Befragten nicht
in das Quotenschema passen). Im Gegensatz zu Filtern
können beliebig viele assert
Statements an Fragen/Screens/Blöcken notiert werden.
Die Syntax für Plausibilitätsbedingungen lautet:
assert BEDINGUNG "FEHLERMELDUNG" [exit FINISHCODE] [attempts TRIES];
Mit BEDINGUNG
wird eine These
aufgestellt, die erfüllt sein muss. Ist dies nicht der
Fall, wird FEHLERMELDUNG
auf dem
gleichen Screen ausgegeben, so dass der Befragte seine
Angaben korrigieren kann. Der optionale Parameter
exit
geht einen Schritt weiter und
beendet das Interview vollständig.
FINISHCODE
ist dabei eine
natürliche Zahl, die beim Interviewabbruch als Wert in
die Variable _finished
übernommen
wird. So ist es möglich Abbruchgründe zu
differenzieren (z.B. Abbruch wegen
Geschlecht-Screenout oder voller Quote).
Zusätzlich ist es mit dem Parameter
attempts
möglich die Konsequenzen
bei einem Verstoß gegen die Plausibilitätsprüfung
nicht unmittelbar herbeizuführen, sondern dem
Teilnehmer beschränkt viele Versuche einzuräumen, um
sich zu korrigieren. Wie viele Versuche genau gibt
TRIES
vor, das ebenfalls als
natürliche Zahl angegeben wird. Konkret heißt das bei
einem assert mit exit, dass der
Teilnehmer seine Angaben bis zu
TRIES
mal korrigieren kann, ehe das
Interview endgültig abgebrochen wird. Solange verhält
sich das assert
so als gäbe es kein
exit
. Ein assert ohne
exit würde einen Teilnehmer, der z.B.
Punkte verteilt, deren Summe nicht aufgeht, nach
TRIES
fehlgeschlagenen Versuchen trotzdem im Interview
weiterlassen. So kann bei komplexen Eingaben abgewogen
werden, ob man evtl. lieber etwas unstimmige Angaben
bekommt ehe der Teilnehmer möglicherweise frustriert
aufgibt und man das Interview ganz verliert.
Beispiele:
SingleQ geschlecht; text="Geschlecht des Befragten"; labels= 1 "Männlich" 2 "Weiblich" ; assert (geschlecht eq 1) "Frauen gehören nicht zur Zielgruppe" exit 2; SingleQ alter; text="Alter des Befragten"; labels= 1 "unter 18" 2 "18-29" 3 "30-49" 4 "50-64" 5 "65 und älter" ; assert ([2 3 4] in alter) "Befragter muss 18-64 Jahre alt sein." exit 3; NumQ punkte; Text="Bitte verteilen Sie 10 Punkte auf diese Marken"; labels= 1 "Marke A" format f1 2 "Marke B" format f1 3 "Marke C" format f1 4 "Marke D" format f1 ; assert (punkte.1 gt 0) "Screenout. Marke A hat keine Punkte bekommen." exit 108; assert (punkte eq 10) "Bitte verteilen Sie genau 10 Punkte." attempts 3;
Im Zusammenhang mit Plausibilitätsbedingungen ist auch der Skriptparamter
disableExitOnAssert
zu erwähnen, der den exit
Parameter
gänzlich unterdrückt und so beim Testen der Screenouts sehr hilfreich sein kann, siehe „Skriptparameter“.
[2] Der Standard für reguläre Ausdrücke sieht nur einen Backslash vor. Java benötigt an dieser Stelle jedoch zwei Rückstriche.