Skip to content

Flujo del experimento: condiciones, saltos, exclusiones y contrabalanceo

Asigna participantes a condiciones entre sujetos, salta páginas o excluye participantes mediante reglas, contrabalancea el orden de las páginas con un cuadrado latino y exporta cada asignación.

Nuevo en v2.12.0

Potato 2.12 añade cinco ajustes para la parte de diseño de un experimento. Cada uno ocupa unas pocas líneas de YAML, y todo lo que se asignó o mostró a un participante queda registrado, de modo que las exportaciones CSV y TSV pueden escribirlo en participants.csv.

AjusteQué hace
conditionsAsigna a cada participante una celda de un diseño entre sujetos
show_when en una página de faseSalta la página salvo que se cumpla una regla
exit_when en una página de faseTermina el estudio para el participante, como hace un cuestionario de selección
phases.counterbalanceOrdena un grupo de páginas para cada participante, al azar o por cuadrado latino
randomize_questions en una página de encuestaOrdena las preguntas de esa página para cada participante

Las páginas sobre las que actúan estos ajustes son las páginas de fase descritas en Flujos de trabajo multifase: consent, prestudy, instructions, training, annotation y poststudy, además de las páginas de ejecución que ejecutan una línea de tiempo de jsPsych, una comprobación de auriculares o una tarea externa.

Condiciones

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

Dos factores con dos niveles cada uno dan cuatro celdas. A un participante se le asigna una celda la primera vez que el servidor lo ve, y la conserva tras recargar la página o reiniciar el servidor.

ClaveValor predeterminadoSignificado
factorsobligatorioNombre del factor asociado a una lista de al menos dos niveles
assignmentbalancedbalanced elige la celda con menos participantes y deshace los empates al azar para cada participante. random sortea una celda, con una semilla por participante. round_robin recorre las celdas en orden de llegada
balance_onassignedcompleted cuenta solo a los participantes que llegaron al final sin ser excluidos, de modo que un abandono lo reemplaza la siguiente persona que llega
max_per_cellningunoUna celda llena se omite. Cuando todas las celdas están llenas, se usa la menos ocupada y se registra una advertencia
cellsfactorial completoUna lista explícita de celdas, un subconjunto del factorial, cada una con un weight opcional

Las celdas con peso dan a una condición una proporción mayor de participantes:

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

Pon entre comillas los nombres de nivel que YAML lee como booleanos ("yes", "no", "on", "off"). Tanto el validador como el servidor informan de uno sin comillas como error.

Comprobar una condición en las reglas

Las reglas se refieren a un factor como condition.<factor> en el show_when de una página, el exit_when de una página y el display_logic de una pregunta. El valor que se compara con un factor tiene que ser uno de sus niveles, porque de lo contrario una errata ocultaría la página o la pregunta a todo el mundo, y el validador informa de cualquier valor que no lo sea.

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}

Una pregunta oculta por su condición no es obligatoria, ni en la página ni en el servidor.

Ítems distintos para cada celda

Para dar a cada celda sus propios ítems, nombra los grupos de lotes según los niveles de un factor. Así es como un diseño de listas en cuadrado latino asigna una lista a cada participante:

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}

Los ID de los ítems deben ser únicos en todos los archivos de datos, incluidos los archivos de grupos distintos. El servidor no arranca cuando dos listas comparten un ID, así que un estímulo que aparece en ambas listas necesita un ID distinto en cada una, como h1_a y h1_b.

Una línea de tiempo de jsPsych recibe las condiciones del participante como potato.conditions, y sus datos de ensayo obtienen una columna condition_<factor> por cada factor. Consulta Fases de ejecución.

Redacción según la condición

{condition.<factor>} en cualquier lugar de una página, en el texto de un ítem, la redacción de una pregunta o las etiquetas de las opciones, se sustituye por el nivel de ese factor que tiene el participante. Un experimento de respaldo o una viñeta factorial varía su texto de esta manera:

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

La sustitución se escapa como HTML. Un marcador de posición para un factor en el que el participante no tiene nivel se queda en la página tal como está escrito, de modo que una errata aparece durante las pruebas. Un elemento con data-condition-only="factor=level" se elimina para todos los que no están en ese nivel, que es como un experimento de lista muestra la afirmación sensible solo al grupo de tratamiento.

Las exportaciones escriben el texto del ítem tal como está en el archivo de datos, con el marcador de posición. Los desplazamientos de los fragmentos cuentan los caracteres del texto ya rellenado, tal como lo vio el participante, así que un fragmento situado después de un marcador de posición no coincide con el texto exportado. Desplázalo según la diferencia de longitud entre el marcador de posición y el nivel del participante (sus condiciones están en la exportación), o bien mantén los marcadores de posición fuera del texto que se anota con fragmentos.

Saltar páginas con 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 gramática es la misma que usan las preguntas condicionales a nivel de pregunta: una lista de condiciones con schema, operator y value, combinadas con logic: all (el valor predeterminado) o logic: any, con los mismos 19 operadores y grupos anidados all, any y not. Una regla puede comprobar cualquier pregunta formulada en una página anterior, en cualquier fase, y cualquier condition.<factor>.

La regla se comprueba cuando el participante sale de la página anterior. Una página oculta se omite en ambas direcciones: Next la salta y Previous no se detiene en ella. Cada salto queda registrado.

El servidor se niega a arrancar con una regla que nombra una pregunta que ninguna página formula, o una que se formula en la misma página o en una posterior, ya que esa pregunta solo podría estar sin responder. La fase de anotación en sí no se puede saltar.

Excluir participantes con 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 regla se comprueba cuando el participante envía la página, así que puede comprobar preguntas de esa misma página. Cuando se cumple, el participante pasa al final del estudio:

  • Sin un proveedor de crowdsourcing, ve message.
  • Con uno (Prolific, SONA, CloudResearch Connect y los demás), recibe el resultado de exclusión de la plataforma y nunca el código de finalización.

exit_when funciona en las páginas consent, prestudy, instructions y poststudy. Un participante excluido se queda al final del estudio. Previous no lo lleva de vuelta, ni siquiera con allow_phase_back_navigation, y el servidor no guarda nada más de lo que envíe. Las respuestas que dio antes de la exclusión permanecen en user_state.json, y la exclusión se escribe en participants.csv, no en annotations.csv.

Orden de páginas contrabalanceado

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

Las páginas conservan las posiciones que ocupan en order, y cada participante recibe su propia disposición de ellas.

  • random sortea una permutación con una semilla basada en el participante, así que es la misma tras recargar o reiniciar.
  • latin_square toma filas de un cuadrado latino balanceado (de Williams) en orden de llegada. Con conditions configurado, el orden de llegada se cuenta dentro de cada celda, de modo que cada condición recorre el cuadrado completo y el orden de las páginas queda balanceado dentro de cada condición en lugar de confundirse con ella. Con un número impar de páginas, el cuadrado tiene el doble de filas. Los contadores se reconstruyen a partir de los estados guardados cuando el servidor se reinicia.

Todas las páginas de un grupo deben tener el mismo tipo de fase. Las páginas de tipos distintos nunca están una junto a otra en un estudio, así que no podrían intercambiar sus posiciones.

Orden de las preguntas en una página de encuesta

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

Cada participante ve las preguntas de la página en su propio orden, con una semilla basada en el participante y la página, y registrado la primera vez que se muestra la página. pin_first y pin_last mantienen las preguntas indicadas en uno u otro extremo. Las páginas organizadas con layout.groups conservan su orden. El orden de las opciones dentro de una pregunta es un ajuste aparte, randomize_order en el esquema.

El registro del flujo

user_state.json guarda un flow_record para cada participante:

ClaveContenido
conditions, condition_cell, condition_ordinalLa celda asignada y el número de llegada en todo el estudio
cell_ordinalNúmero de llegada dentro de la celda, que elige la fila del cuadrado latino cuando hay condiciones configuradas
latin_ordinalNúmero de llegada para las filas del cuadrado latino cuando no hay condiciones configuradas
page_ordersEl orden de cada grupo contrabalanceado
question_ordersEl orden de las preguntas de cada página aleatorizada
skipped_pagesPáginas omitidas por show_when
screened_outLa página y la hora de una exclusión
runtimesTipo, estado, intentos, cargas de página, número de ensayos e ID de ejecución de cada página de ejecución
runtime_answersLo que las páginas de ejecución derivaron de sus ejecuciones, como el <page>_passed de una comprobación de auriculares

Las exportaciones CSV y TSV escriben una fila por participante en participants.csv (o .tsv). Con un factor feedback, su cabecera es:

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 se separa con |, y las últimas cuatro columnas contienen JSON. El backend de almacenamiento MySQL no guarda el registro del flujo, y un servidor que lo usa registra una advertencia cuando estas funciones están configuradas.

Ejemplo

examples/advanced/jspsych-stroop/ en el repositorio de Potato usa conditions, exit_when, una página de jsPsych y randomize_questions. Arráncalo desde la raíz del repositorio:

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

Lecturas adicionales

Para detalles de implementación, consulta la documentación fuente.