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 |
|---|---|
two-armed bandit with four stimuli |
|
deterministic two-step task (Smid et al., 2022) |
|
risky decision making with gains, losses and ambiguity |
|
perceptual decisions with confidence ratings |
Next: Parameters and priors.