# Example Experiments

Here you can find some code examples to see Expyriment in action. All examples are fully working experiments.

## Simon task

An experiment to asses a spatial stimulus-response compatibility effect (see wikipedia).

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    A simple behavioural task to asses a Simon effect.

    See also:
    http://en.wikipedia.org/wiki/Simon_effect

    """

    from expyriment import design, control, stimuli, io, misc

    # Create and initialize an Experiment
    exp = design.Experiment("Simon Task")
    control.initialize(exp)

    # Define and preload standard stimuli
    fixcross = stimuli.FixCross()
    fixcross.preload()

    # Create IO
    #response_device = io.EventButtonBox(io.SerialPort("/dev/ttyS1"))
    response_device = exp.keyboard

    # Create design
    for task in ["left key for green", "left key for red"]:
        b = design.Block()
        b.set_factor("Response", task)
        for where in [["left", -300], ["right", 300]]:
            for what in [["red", misc.constants.C_RED],
                         ["green", misc.constants.C_GREEN]]:
                t = design.Trial()
                t.set_factor("Position", where[0])
                t.set_factor("Colour", what[0])
                s = stimuli.Rectangle([50, 50], position=[where[1], 0],
                                      colour=what[1])
                t.add_stimulus(s)
                b.add_trial(t, copies=20)
        b.shuffle_trials()
        exp.add_block(b)
    exp.add_bws_factor("ResponseMapping", [1, 2])
    exp.data_variable_names = ["Position", "Button", "RT"]

    # Start Experiment
    control.start()
    exp.permute_blocks(misc.constants.P_BALANCED_LATIN_SQUARE)
    for block in exp.blocks:
        stimuli.TextScreen("Instructions", block.get_factor("Response")).present()
        response_device.wait()
        for trial in block.trials:
            fixcross.present()
            exp.clock.wait(1000 - trial.stimuli[0].preload())
            trial.stimuli[0].present()
            button, rt = response_device.wait()
            exp.data.add([trial.get_factor("Position"), button, rt])

    # End Experiment
    control.end()

## Word fragment completion task

Task as used for instance in Weldon, 1991. The script read in a stimulus list file (`demo stimulus list`).

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    Word fragment completion task as used in the study of Weldon (1991).

    Stimulus list: "word_fragment_completion_stimuluslist.csv"!

    Weldon, M. S. (1991). Mechanisms underlying priming on
    perceptual tests. Journal of Experimental Psychology: Learning, Memory, and
    Cognition, 17, 526-541.

    """

    import csv
    from expyriment import design, control, stimuli, io, misc

    control.set_develop_mode(True)

    #### read in wordlist file and make design
    exp = design.Experiment("word fragment completion test")
    block = design.Block()
    with open("word_fragment_completion_stimuluslist.csv", "rb") as f:
        reader = csv.reader(f)
        for row in reader:
            trial = design.Trial()
            trial.set_factor("word", row[0].strip())
            trial.set_factor("fragment", row[1].strip())
            block.add_trial(trial)
    block.shuffle_trials()
    exp.add_block(block)
    exp.add_data_variable_names(["word", "fragment", "RT", "RT2", "answer"])

    control.initialize(exp)

    #prepare some stimuli
    fixcross = stimuli.FixCross(line_width=1)
    fixcross.preload()
    blank = stimuli.BlankScreen()
    blank.preload()
    txt_input = io.TextInput("")
    control.start(exp)

    #run experiment
    for trial in exp.blocks[0].trials:
        #present blank inter-trial-screen and prepare stimulus
        blank.present()
        fragment = ""
        for c in trial.get_factor("fragment").upper():
            fragment += c + " "
        target = stimuli.TextLine(fragment.strip())
        target.preload()
        exp.clock.wait(1000)
        #present fixcross
        fixcross.present()
        exp.clock.wait(500)
        #present target
        target.present()
        key, rt = exp.keyboard.wait(misc.constants.K_SPACE)
        #ask response
        exp.clock.reset_stopwatch()
        answer = txt_input.get()
        rt2 = exp.clock.stopwatch_time
        #process answer and save data
        blank.present()
        answer = answer.strip()
        exp.data.add([trial.get_factor("word"), trial.get_factor("fragment"),
                      rt, rt2, answer])
        target.unload()

    control.end()

## Number classification task

A full experiment to access SNARC and SNARC-like effects in a number and a letter classification task (e.g., Gevers, Reynvoer, & Fias (2003)) with two response mappings, error feedback and between-subject factors.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    A parity judgment task to assess the SNARC effect.

    See e.g.:
    Gevers, W., Reynvoet, B., & Fias, W. (2003). The mental representation of
    ordinal sequences is spatially organized. Cognition, 87(3), B87-95.

    """

    from expyriment import design, control, stimuli
    from expyriment.misc import constants

    control.set_develop_mode(False)

    ########### DESIGN ####################
    exp = design.Experiment(name="SNARC")

    # Design: 2 response mappings x 8 stimuli x 10 repetitions
    for response_mapping in ["left_odd", "right_odd"]:
        block = design.Block()
        block.set_factor("mapping", response_mapping)
        #add trials to block
        for digit in [1, 2, 3, 4, 6, 7, 8, 9]:
            trial = design.Trial()
            trial.set_factor("digit", digit)
            block.add_trial(trial, copies=10)
        block.shuffle_trials()
        exp.add_block(block)

    exp.add_experiment_info("This a just a SNARC experiment.")
    #add between subject factors
    exp.add_bws_factor('mapping_order', ['left_odd_first', 'right_odd_first'])
    #prepare data output
    exp.data_variable_names = ["block", "mapping", "trial", "digit", "ISI",
                               "btn", "RT", "error"]

    #set further variables
    t_fixcross = 500
    min_max_ISI = [200, 750] # [min, max] inter_stimulus interval
    ITI = 1000
    t_error_screen = 1000
    no_training_trials = 10

    ######### INITIALIZE ##############
    control.initialize(exp)

    # Prepare and preload some stimuli
    blankscreen = stimuli.BlankScreen()
    blankscreen.preload()
    fixcross = stimuli.FixCross()
    fixcross.preload()
    error_beep = stimuli.Tone(duration=200, frequency=2000)
    error_beep.preload()

    #define a trial
    def run_trial(cnt, trial):
        # present Fixation cross and prepare trial in the meantime
        fixcross.present()
        exp.clock.reset_stopwatch()
        ISI = design.randomize.rand_int(min_max_ISI[0], min_max_ISI[1])
        digit = trial.get_factor("digit")
        target = stimuli.TextLine(text=str(digit), text_size=60)
        target.preload()
        exp.clock.wait(t_fixcross - exp.clock.stopwatch_time)
        #present blankscreen for a random interval
        blankscreen.present()
        exp.clock.wait(ISI)
        # Present target & record button response
        target.present()
        btn, rt = exp.keyboard.wait([constants.K_LEFT, constants.K_RIGHT])
        #Error feedback if required
        if block.get_factor("mapping") == "left_odd":
            error = (digit % 2 == 0 and btn == constants.K_LEFT) or \
                    (digit % 2 == 1 and btn == constants.K_RIGHT)
        else:
            error = (digit % 2 == 1 and btn == constants.K_LEFT) or \
                    (digit % 2 == 0 and btn == constants.K_RIGHT)
        if error:
            error_beep.present()
        #write data and clean up while inter-trial-interval
        blankscreen.present()
        exp.clock.reset_stopwatch()
        exp.data.add([block.id, block.get_factor("mapping"),
                      cnt, target.text, ISI,
                      btn, rt, int(error)])
        exp.data.save()
        target.unload()
        exp.clock.wait(ITI - exp.clock.stopwatch_time)

    ######### START ##############
    control.start(exp)

    # permute block order across subjects
    if exp.get_permuted_bws_factor_condition('mapping_order') == "right_odd_first":
        exp.swap_blocks(0, 1)

    # Run the actual experiment
    for block in exp.blocks:
        # Show instruction screen
        if block.get_factor("mapping") == "left_odd":
            instruction = "Press LEFT arrow key for ODD\n" + \
                                "and RIGHT arrow key for EVEN numbers."
        else:
            instruction = "Press RIGHT arrow key for ODD\n" + \
                                "and LEFT arrow key for EVEN numbers."
        stimuli.TextScreen("Indicate the parity of the numbers", instruction +
                           "\n\nPress space bar to start training.").present()
        exp.keyboard.wait(constants.K_SPACE)
        #training trials
        for cnt in range(0, no_training_trials):
            trial = block.get_random_trial()
            run_trial(-1 * (1 + cnt), trial) #training trails has negative trial numbers
        # Show instruction screen
        stimuli.TextScreen("Attention!", instruction +
                           "\n\nThe experimental block starts now.").present()
        exp.keyboard.wait(constants.K_SPACE)
        # experimental trials
        for cnt, trial in enumerate(block.trials):
            run_trial(cnt, trial)

    ####### END EXPERIMENT ########
    control.end(goodbye_text="Thank you very much for participating in our experiment",
                 goodbye_delay=5000)

## Line bisection task

Example of a line bisection task that is optimized for the use of touchscreens and the Expyriment Android Runtime.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    A line bisection task.

    This example is appropriate to illustrates the use of the Android runtime environment for Exypriment on tablet PC.

    """

    from expyriment import control, stimuli, io, design, misc

    # settings
    design.defaults.experiment_background_colour = misc.constants.C_GREY
    design.defaults.experiment_foreground_colour = misc.constants.C_BLACK
    line_length = 200

    def line_bisection_task(line_length, position):
        # make button
        button = stimuli.Rectangle(size=(40,20),
                    position=(exp.screen.size[0]/2-25, 15-exp.screen.size[1]/2))
        button_text = stimuli.TextLine(text="ok", position=button.position,
                                    text_colour=misc.constants.C_WHITE)
        mark_position = None
        while True:
            canvas = stimuli.BlankScreen()
            line = stimuli.Rectangle(size=(line_length,3), position=position,
                        colour=misc.constants.C_BLACK)
            line.plot(canvas)
            if mark_position is not None:
                # plot button and mark line on canvas
                button.plot(canvas)
                button_text.plot(canvas)
                markline = stimuli.Rectangle(size=(1,25),
                            position=(mark_position, line.position[1]),
                            colour=misc.constants.C_RED)
                markline.plot(canvas)
            # present stimulus
            canvas.present()
            # wait for mouse or touch screen response
            _id, pos, _rt = exp.mouse.wait_press()
            # process clicked position position
            if abs(pos[1]-line.position[1])<=50 and\
                        abs(pos[0]-line.position[0])<=line_length/2:
                mark_position = pos[0]
            else:
                if button.overlapping_with_position(pos): # is button pressed
                    return mark_position - line.position[0]

    ### init ###
    exp = control.initialize()

    # create touch button box
    buttonA = stimuli.Rectangle(size=(80, 40), position=(-60, 0))
    buttonB = stimuli.Rectangle(size=(80, 40), position=(60, 0))
    textA = stimuli.TextLine(text="quit", position=buttonA.position,
                text_colour=misc.constants.C_WHITE)
    textB = stimuli.TextLine(text="next", position=buttonB.position,
                text_colour=misc.constants.C_WHITE)
    touchButtonBox = io.TouchScreenButtonBox(button_fields=[buttonA, buttonB],
                    stimuli=[textA, textB])
    ### start ###
    control.start(exp)
    exp.mouse.show_cursor()

    # trial loop
    while True:
        # find random position
        rx, ry = ((exp.screen.size[0]-line_length)/2, (exp.screen.size[1]-50)/2)
        pos = [design.randomize.rand_int(-rx, rx), design.randomize.rand_int(-ry, ry)]
        # present task
        judgment = line_bisection_task(line_length, position=pos)
        # save data
        exp.data.add(pos +[judgment])
        # ask for new trail
        touchButtonBox.show()
        btn, _rt = touchButtonBox.wait()
        if btn==buttonA:
            break

    ## end##
    control.end()

## Really short example

Expyriment is efficient!. See here a very short example of an functioning experiment in less than 20 lines of pure code.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    A very short example experiment in 16 lines of pure code.

    Participants have to indicate the parity of digits by pressing
    the left arrow key for odd and the right arrow key for even numbers.

    """

    from expyriment import control, stimuli, design, misc

    digit_list = [1, 2, 3, 4, 6, 7, 8, 9]*12
    design.randomize.shuffle_list(digit_list)

    exp = control.initialize()
    exp.data_variable_names = ["digit", "btn", "rt", "error"]

    control.start(exp)

    for digit in digit_list:
        target = stimuli.TextLine(text=str(digit), text_size=80)
        exp.clock.wait(500 - stimuli.FixCross().present() - target.preload())
        target.present()
        button, rt = exp.keyboard.wait([misc.constants.K_LEFT, misc.constants.K_RIGHT])
        error = (button == misc.constants.K_LEFT) == digit%2
        if error: stimuli.Tone(duration=200, frequency=2000).play()
        exp.data.add([digit, button, rt, int(error)])
        exp.clock.wait(1000 - stimuli.BlankScreen().present() - target.unload())

    control.end(goodbye_text="Thank you very much...", goodbye_delay=2000)

## Data preprocessing

Preprocessing the data of the SNARC experiment for further statistical analysis.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    Example analysis script for snarc_experiment.py

    The current script produces two files for different analysis of the SNARC
    effect (ANOVA vs. slopes analysis) using mean and median RTs

    """

    from expyriment.misc import data_preprocessing, constants

    agg = data_preprocessing.Aggregator(data_folder="./data/",
                                        file_name="snarc_experiment")

    agg.set_subject_variables(["mapping_order"])
    agg.set_computed_variables(["parity = digit % 2", #0:odd, 1:even
                          "size = digit > 5", #0:small, 1:large
                          "space = btn == {0}".format(constants.K_RIGHT) #0:left, 1:right
                          ])
    # RTs: space x size
    agg.set_exclusions(["RT > 1000", "RT < 200", "error == 1", "trial<0"])
    agg.set_dependent_variables(["mean(RT)"])
    agg.set_independent_variables(["size", "space"])
    print agg
    agg.aggregate(output_file="rt_size_space.csv")
    # RTs: slopes analysis
    agg.set_independent_variables(["digit"])
    agg.aggregate(output_file="rt_digits.csv")