Data format#

cpm works with pandas data frames in which each row is one trial of one participant. The columns hold everything the model needs to know about a trial: which stimuli were shown, what the rewards were, what the participant chose.

import numpy as np
import pandas as pd

from cpm.datasets import load_bandit_data

data = load_bandit_data()
data.head()
ppt trial arm_left arm_right reward_left reward_right response feedback
0 1 1 2 1 0.0 1.0 1 1.0
1 1 2 3 2 1.0 0.0 0 1.0
2 1 3 1 3 1.0 0.0 0 1.0
3 1 4 1 3 0.0 0.0 0 0.0
4 1 5 2 4 1.0 0.0 0 1.0

There are no required column names, apart from one: the column observed holds the behaviour that the model should predict, and that the optimisers compare the predictions of the model with. Your model function reads the other columns by name, so you choose them.

data["observed"] = data["response"]

One participant or many#

A Wrapper runs a model over the trials of one participant, so it takes the rows of a single participant:

one_participant = data[data.ppt == 1]
one_participant.shape
(71, 9)

A Simulator and the optimisers in cpm.optimisation work on many participants. They take the data grouped by participant, with pandas.DataFrame.groupby(), and run the model on each group:

grouped = data.groupby("ppt")
print(f"{grouped.ngroups} participants")
200 participants

The optimisers also need to know which column identifies participants, so that they can label their results: pass its name as ppt_identifier.

Values that change within a trial#

Each cell of a data frame holds one value. If a trial has several values of the same kind, such as the stimuli on screen, give each its own column (arm_left, arm_right), and combine them in the model function:

trial = one_participant.iloc[0]
stimuli = np.array([trial.arm_left, trial.arm_right])
stimuli
array([2., 1.])

Data for the ready-made models#

The models in cpm.applications expect specific column names, which are listed on their API pages. cpm.utils.data has converters that rename and reshape your columns for them, such as convert_to_RLRW() for RLRW:

from cpm.utils.data import convert_to_RLRW

my_data = pd.DataFrame(
    {
        "subject": [1, 1, 2],
        "stim_left": [1, 2, 1],
        "stim_right": [2, 1, 2],
        "choice": [1, 0, 1],
        "reward_left": [1, 0, 1],
        "reward_right": [0, 1, 0],
    }
)
convert_to_RLRW(
    data=my_data,
    human_response="choice",
    reward=["reward_left", "reward_right"],
    stimulus=["stim_left", "stim_right"],
    participant="subject",
)
arm_0 arm_1 response reward_0 reward_1 participant
0 1 2 1 1 0 1
1 2 1 0 0 1 1
2 1 2 1 1 0 2

Example datasets#

cpm.datasets ships with the datasets used in the tutorials and examples:

Loader

Task

load_bandit_data()

two-armed bandit with four stimuli

load_two_step_data()

deterministic two-step task (Smid et al., 2022)

load_risky_choices()

risky decision making with gains, losses and ambiguity

load_metacognition_data()

perceptual decisions with confidence ratings

Next: Parameters and priors.