Fitting single subject data using MLE#

Author: Nicolas Legrand nicolas.legrand@cfin.au.dk

Hide code cell content
%%capture
import sys
if 'google.colab' in sys.modules:
    ! pip install metadpy
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from metadpy import load_dataset
from metadpy.mle import metad
from metadpy.plotting import plot_confidence, plot_roc

sns.set_context("talk")

In this notebook, we are going to estimate meta-d’ using Maximum Likelihood Estimation (MLE) [Fleming and Lau, 2014] using the function implemented in metadpy. This function is directly adapted from the transcription of the Matlab fit_meta_d_MLE.m by Alan Lee that can be retrieved here.

We are going to see, however, that metadpy greatly simplifies the preprocessing of raw data, letting the user fit the model for many participants/groups/conditions from the results data frame in a single command call. Another advantage here is that the python code supporting the model fitting is optimized using Numba, which greatly improves its performance.

From response-signal arrays#

# Create responses data
nR_S1 = np.array([52, 32, 35, 37, 26, 12, 4, 2])
nR_S2 = np.array([2, 5, 15, 22, 33, 38, 40, 45])
fig, axs = plt.subplots(1, 2, figsize=(13, 5))
plot_confidence(nR_S1, nR_S2, ax=axs[0])
plot_roc(nR_S1, nR_S2, ax=axs[1])
sns.despine()
../_images/7ee02b46d6ed9337d477d7dd833f5bef695c106d3e2b499680b36495bffefb41.png

The model is fitted using the metadpy.mle.metad() function. This function accepts response-signal arrays as input if the data comes from a single subject.

output = metad(nR_S1=nR_S1, nR_S2=nR_S2)
output
dprime meta_d m_ratio m_diff
0 1.534994 1.63376 1.064343 0.098766

The function will return a data frame containng the dprime, metad, m_ratio and m_diff scores for this participant.

From a data frame#

To simplify the preprocessing steps, the model can also be fitted directly from the raw result data frame. The data frame should contain the following columns:

  • Stimuli: Which of the two stimuli was presented [0 or 1].

  • Response or Accuracy: The response provided by the participant or the accuracy [0 or 1].

  • Confidence: The confidence level [can be continuous or discrete].

In addition, it can also integrate:

  • Subject: The subject ID.

  • within or between: The condition or the group ID (if many groups or conditions were used).

Note that the MLE method will always fit the participant separately (i.e. in a non-hierarchical way), which means that the results will be the same by fitting each participant and condition separately (e.g. in a for loop).

df = load_dataset("rm")
df.head()
Stimuli Responses Accuracy Confidence nTrial Subject Condition
0 0 0 1 4 0 0 0
1 1 1 1 4 1 0 0
2 0 1 0 4 2 0 0
3 1 0 0 1 3 0 0
4 1 1 1 2 4 0 0
subject_fit = metad(
    data=df[df.Subject == 0].copy(),
    nRatings=4,
    stimuli="Stimuli",
    accuracy="Accuracy",
    confidence="Confidence",
    padding=True,
)
subject_fit.head()
dprime meta_d m_ratio m_diff
0 0.999247 0.781983 0.782573 -0.217264

Fitting at the group level#

Using a dataframe#

group_fit = metad(
    data=df,
    nRatings=4,
    stimuli="Stimuli",
    accuracy="Accuracy",
    confidence="Confidence",
    subject="Subject",
    padding=True,
    within="Condition",
)
group_fit.head()
dprime meta_d m_ratio m_diff Subject Condition
0 0.959244 0.739642 0.771068 -0.219601 0 0
0 1.006310 0.790893 0.785934 -0.215417 0 1
0 1.117543 1.084441 0.970379 -0.033103 1 0
0 0.967871 0.869539 0.898404 -0.098332 1 1
0 0.792528 0.670130 0.845560 -0.122398 2 0
_, axs = plt.subplots(1, 4, figsize=(12, 5), sharex=True)

for i, metric in enumerate(["dprime", "meta_d", "m_ratio", "m_diff"]):

    sns.boxplot(data=group_fit, x="Condition", y=metric, ax=axs[i])
    sns.stripplot(data=group_fit, x="Condition", y=metric, color="k", ax=axs[i])

plt.tight_layout()
sns.despine()
../_images/ec5001201ed2713341431136ab1c08e25334854f0f4672327d682dd8ca2749fa.png

Watermark#

%load_ext watermark
%watermark -n -u -v -iv -w -p metadpy,pytensor,pymc
Last updated: Wed Nov 08 2023

Python implementation: CPython
Python version       : 3.9.18
IPython version      : 8.17.2

metadpy : 0.1.1
pytensor: 2.17.3
pymc    : 5.9.1

sys       : 3.9.18 (main, Aug 28 2023, 08:38:32) 
[GCC 11.4.0]
seaborn   : 0.13.0
matplotlib: 3.8.1
numpy     : 1.25.2

Watermark: 2.4.3