Skip to content

Déroulement de l'expérience : conditions, sauts, exclusions et contrebalancement

Affectez les participants à des conditions inter-sujets, sautez des pages ou excluez des participants par des règles, contrebalancez l'ordre des pages par carré latin et exportez chaque affectation.

Nouveau dans la v2.12.0

Potato 2.12 ajoute cinq réglages pour la partie conception d'une expérience. Chacun tient en quelques lignes de YAML, et tout ce qui a été attribué ou montré à un participant est enregistré, de sorte que les exports CSV et TSV peuvent l'écrire dans participants.csv.

RéglageRôle
conditionsAffecte chaque participant à une cellule d'un plan inter-sujets
show_when sur une page de phaseSaute la page sauf si une règle est vérifiée
exit_when sur une page de phaseMet fin à l'étude pour le participant, comme le fait un questionnaire de sélection
phases.counterbalanceOrdonne un groupe de pages pour chaque participant, au hasard ou par carré latin
randomize_questions sur une page de questionnaireOrdonne les questions de cette page pour chaque participant

Les pages sur lesquelles agissent ces réglages sont les pages de phase décrites dans Flux de travail multi-phases : consent, prestudy, instructions, training, annotation et poststudy, ainsi que les pages d'exécution qui lancent une timeline jsPsych, un test de casque ou une tâche externe.

Conditions

yaml
conditions:
  factors:
    feedback: [none, immediate]
    font: [serif, sans]
  assignment: balanced        # balanced | random | round_robin
  balance_on: assigned        # assigned | completed
  max_per_cell: 40            # optional

Deux facteurs à deux niveaux chacun donnent quatre cellules. Un participant reçoit une cellule la première fois que le serveur le voit, et la conserve après un rechargement de la page ou un redémarrage du serveur.

CléValeur par défautSignification
factorsobligatoireNom du facteur associé à une liste d'au moins deux niveaux
assignmentbalancedbalanced choisit la cellule qui compte le moins de participants, en départageant les ex aequo au hasard pour chaque participant. random tire une cellule, avec une graine propre au participant. round_robin parcourt les cellules à tour de rôle dans l'ordre d'arrivée
balance_onassignedcompleted ne compte que les participants arrivés au bout sans avoir été exclus, si bien qu'un abandon est remplacé par l'arrivant suivant
max_per_cellaucuneUne cellule pleine est ignorée. Quand toutes les cellules sont pleines, la moins remplie est utilisée et un avertissement est journalisé
cellsplan factoriel completUne liste explicite de cellules, sous-ensemble du plan factoriel, chacune avec un weight facultatif

Des cellules pondérées donnent à une condition une part plus grande des participants :

yaml
conditions:
  factors:
    prime: [none, positive, negative]
  cells:
    - {prime: none, weight: 2}
    - {prime: positive}
    - {prime: negative}

Mettez entre guillemets les noms de niveaux que YAML lit comme des booléens ("yes", "no", "on", "off"). Le validateur et le serveur signalent tous deux un nom sans guillemets comme une erreur.

Tester une condition dans les règles

Les règles désignent un facteur par condition.<factor> dans le show_when d'une page, l'exit_when d'une page et le display_logic d'une question. La valeur comparée à un facteur doit être l'un de ses niveaux, car une faute de frappe masquerait sinon la page ou la question à tout le monde ; le validateur signale toute valeur qui n'en est pas un.

yaml
- name: feedback_rating
  annotation_type: likert
  description: How useful was the feedback?
  size: 5
  display_logic:
    show_when:
      - {schema: condition.feedback, operator: equals, value: immediate}

Une question masquée par sa condition n'est pas obligatoire, ni sur la page ni côté serveur.

Des items différents pour chaque cellule

Pour donner à chaque cellule ses propres items, nommez les groupes de lots d'après les niveaux d'un facteur. C'est ainsi qu'un plan à listes en carré latin attribue une liste par participant :

yaml
conditions:
  factors:
    list: [list_a, list_b]
assignment_strategy: batch
batch_assignment:
  auto_assign_annotators: true
  group_by_condition: list
  groups:
    - {name: list_a, data_file: data/list_a.jsonl}
    - {name: list_b, data_file: data/list_b.jsonl}

Les identifiants d'items doivent être uniques dans l'ensemble des fichiers de données, y compris entre les fichiers de groupes différents. Le serveur refuse de démarrer quand deux listes partagent un identifiant ; un stimulus présent dans les deux listes a donc besoin d'un identifiant distinct dans chacune, par exemple h1_a et h1_b.

Une timeline jsPsych reçoit les conditions du participant sous la forme potato.conditions, et ses données d'essai reçoivent une colonne condition_<factor> par facteur. Voir Phases d'exécution.

Formulation selon la condition

{condition.<factor>}, placé n'importe où sur une page, dans le texte d'un item, la formulation d'une question ou les libellés d'options, est remplacé par le niveau du participant pour ce facteur. Une expérience d'endossement ou une vignette factorielle fait varier son texte de cette façon :

yaml
conditions:
  factors:
    endorser: ["The city council", "A neighbourhood association"]
json
{"id": "p1", "text": "{condition.endorser} proposes turning the car park into a park."}

Le texte de remplacement est échappé en HTML. Un espace réservé pour un facteur dont le participant n'a aucun niveau reste sur la page tel quel, ce qui fait apparaître une faute de frappe pendant les tests. Un élément portant data-condition-only="factor=level" est retiré pour tous ceux qui ne sont pas à ce niveau ; c'est ainsi qu'une expérience de liste ne montre l'énoncé sensible qu'au groupe de traitement.

Les exports écrivent le texte de l'item tel qu'il figure dans le fichier de données, avec l'espace réservé. Les positions des segments comptent les caractères du texte rempli, tel que le participant l'a vu ; un segment situé après un espace réservé ne correspond donc pas au texte exporté. Décalez-le de la différence de longueur entre l'espace réservé et le niveau du participant (ses conditions figurent dans l'export), ou bien n'utilisez pas d'espaces réservés dans un texte annoté par segments.

Sauter des pages avec show_when

yaml
phases:
  order: [screener, smoking_followup, general, annotation]
  screener:
    type: prestudy
    file: surveys/screener.json
  smoking_followup:
    type: prestudy
    file: surveys/smoking.json
    show_when:
      - {schema: smoker, operator: equals, value: "Yes"}
  general:
    type: prestudy
    file: surveys/general.json

La grammaire est celle des questions conditionnelles au niveau des questions : une liste de conditions avec schema, operator et value, combinées par logic: all (par défaut) ou logic: any, avec les mêmes 19 opérateurs et des groupes imbriqués all, any et not. Une règle peut tester toute question posée sur une page antérieure, dans n'importe quelle phase, ainsi que toute condition.<factor>.

La règle est évaluée quand le participant quitte la page qui précède. Une page masquée est passée dans les deux sens : Next la saute, et Previous ne s'y arrête pas. Chaque saut est enregistré.

Le serveur refuse de démarrer avec une règle qui cite une question qu'aucune page ne pose, ou une question posée sur la même page ou sur une page ultérieure, puisque cette question resterait forcément sans réponse. La phase d'annotation elle-même ne peut pas être sautée.

Exclure des participants avec exit_when

yaml
phases:
  screener:
    type: prestudy
    file: surveys/screener.json
    exit_when:
      show_when:
        - {schema: age, operator: lt, value: 18}
      message: This study is for adults only. Thank you for your time.

La règle est évaluée quand le participant envoie la page ; elle peut donc tester les questions de cette page. Quand elle est vérifiée, le participant est envoyé à la fin de l'étude :

  • Sans plateforme de crowdsourcing, il voit message.
  • Avec une plateforme (Prolific, SONA, CloudResearch Connect et les autres), il reçoit le résultat d'exclusion de la plateforme et jamais le code de complétion.

exit_when fonctionne sur les pages consent, prestudy, instructions et poststudy. Un participant exclu reste à la fin de l'étude. Previous ne le ramène pas en arrière, même avec allow_phase_back_navigation, et le serveur n'enregistre plus rien de ce qu'il envoie. Les réponses données avant l'exclusion restent dans user_state.json, et l'exclusion est écrite dans participants.csv, pas dans annotations.csv.

Ordre des pages contrebalancé

yaml
phases:
  order: [intro, block_a, block_b, block_c, annotation]
  # ...block definitions...
  counterbalance:
    - pages: [block_a, block_b, block_c]
      method: latin_square        # or random

Les pages gardent les emplacements qu'elles occupent dans order, et chaque participant reçoit son propre agencement de ces pages.

  • random tire une permutation avec une graine propre au participant ; elle est donc identique après un rechargement ou un redémarrage.
  • latin_square prend les lignes d'un carré latin équilibré (de Williams) dans l'ordre d'arrivée. Quand conditions est configuré, l'ordre d'arrivée est compté à l'intérieur de chaque cellule : chaque condition parcourt ainsi le carré entier, et l'ordre des pages est équilibré au sein de chaque condition au lieu d'être confondu avec elle. Pour un nombre impair de pages, le carré a deux fois plus de lignes. Les compteurs sont reconstruits à partir des états enregistrés au redémarrage du serveur.

Toutes les pages d'un groupe doivent avoir le même type de phase. Des pages de types différents ne sont jamais voisines dans une étude ; elles ne pourraient donc pas échanger leurs places.

Ordre des questions sur une page de questionnaire

yaml
phases:
  big_five:
    type: poststudy
    instrument: tipi
    randomize_questions:
      pin_last: [comments]     # or: randomize_questions: true

Chaque participant voit les questions de la page dans son propre ordre, tiré avec une graine propre au participant et à la page, et enregistré la première fois que la page est affichée. pin_first et pin_last maintiennent les questions nommées à l'une ou l'autre extrémité. Les pages mises en forme avec layout.groups gardent leur ordre. L'ordre des options au sein d'une question relève d'un réglage distinct, randomize_order sur le schéma.

L'enregistrement du déroulement

user_state.json contient un flow_record pour chaque participant :

CléContenu
conditions, condition_cell, condition_ordinalLa cellule attribuée et le numéro d'arrivée à l'échelle de l'étude
cell_ordinalNuméro d'arrivée au sein de la cellule, qui choisit la ligne du carré latin quand des conditions sont configurées
latin_ordinalNuméro d'arrivée pour les lignes du carré latin quand aucune condition n'est configurée
page_ordersL'ordre de chaque groupe contrebalancé
question_ordersL'ordre des questions de chaque page randomisée
skipped_pagesLes pages passées par show_when
screened_outLa page et l'heure d'une exclusion
runtimesType, statut, tentatives, chargements de page, nombre d'essais et identifiant d'exécution de chaque page d'exécution
runtime_answersCe que les pages d'exécution ont tiré de leurs exécutions, par exemple le <page>_passed d'un test de casque

Les exports CSV et TSV écrivent une ligne par participant dans participants.csv (ou .tsv). Avec un facteur feedback, l'en-tête est :

text
user_id,condition_feedback,condition_ordinal,cell_ordinal,latin_ordinal,final_phase,screened_out,screened_out_page,skipped_pages,page_orders,question_orders,runtime_runs,runtime_answers

skipped_pages est séparé par |, et les quatre dernières colonnes contiennent du JSON. Le backend de stockage MySQL n'enregistre pas l'enregistrement du déroulement, et un serveur qui l'utilise journalise un avertissement quand ces fonctions sont configurées.

Exemple

examples/advanced/jspsych-stroop/ dans le dépôt Potato utilise conditions, exit_when, une page jsPsych et randomize_questions. Lancez-le depuis la racine du dépôt :

bash
python potato/flask_server.py start examples/advanced/jspsych-stroop/config.yaml -p 8000

Pour aller plus loin

Pour les détails d'implémentation, consultez la documentation source.