Kapitel 10. Filter- und Plausibilitätsbedingungen

Inhaltsverzeichnis

Allgemeines
Bedingungen
Prüfung bei Arrays und Multivariablen
Funktionen in Bedingungen
Filterbedingungen (flt)
Filter allgemein
Filter an Fragen, Blöcken, Screens
Filter in Labellisten
Filter in Groups
Spezialitäten: Filtertexte, noexport und setfilter
Plausibilitätsbedingungen (assert)

Allgemeines

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.

Bedingungen

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:

  • eq gleich (equal)

  • ne ungleich (not equal)

  • gt größer als (greater than)

  • ge größer gleich (greater or equal)

  • lt kleiner als (less than)

  • le kleiner gleich (less or equal)

  • in prüft, ob ein Wert in einer Wertemenge (Array) enthalten ist

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)
    

Prüfung bei Arrays und Multivariablen

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.

Funktionen in Bedingungen

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 TextArray TARRAY 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 in ARRAY 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 untersucht regexp, ob die Zeichenkette mit 123 beginnt. Dagegen liefert regexp mit dem Muster com$ immer dann eine 1, wenn die Zeichenkette mit com 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);
	  

Filterbedingungen (flt)

Filter allgemein

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.

Filter an Fragen, Blöcken, Screens

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);
      

Filter in Labellisten

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.

Filter in Groups

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.

Spezialitäten: Filtertexte, noexport und setfilter

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 (assert)

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.