{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Analyze Polyn et al. (2009) CMR Data\n\nThis example demonstrates analyzing the Polyn et al. (2009) free recall dataset,\nwhich was used in the development of the Context Maintenance and Retrieval (CMR)\nmodel. The dataset contains behavioral data from 45 subjects who studied lists\nof 24 words using either SIZE or ANIMACY encoding tasks.\n\nThe experiment included three list types:\n- Control (Size): All items studied using the SIZE task\n- Control (Animacy): All items studied using the ANIMACY task\n- Shift: Items alternated between SIZE and ANIMACY encoding tasks\n\nWe'll analyze recall performance using:\n1. Probability of First Recall (PFR) - probability of recalling each position first\n2. Lag-CRP - conditional recall probability by temporal lag\n3. Serial Position Curve (SPC) - recall probability by encoding position\n4. Memory Fingerprint - clustering by task and temporal features\n\nReference:\nPolyn, S.M., Norman, K.A., & Kahana, M.J. (2009). A Context Maintenance and\nRetrieval Model of Organizational Processes in Free Recall. Psychological\nReview, Vol. 116 (1), 129-156. https://doi.org/10.1037/a0014420\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Code source: Contextual Dynamics Laboratory\n# License: MIT\n\nfrom collections import Counter\n\nimport quail\nimport matplotlib.pyplot as plt\nimport warnings\n\n# Suppress RuntimeWarnings about empty slices (from subjects with fewer lists)\nwarnings.filterwarnings('ignore', category=RuntimeWarning)\n\n# Load the CMR dataset\negg = quail.load_example_data('cmr')\n\nprint(f\"Loaded CMR data: {egg.n_subjects} subjects, {egg.n_lists} lists, \"\n f\"{egg.list_length} items per list\")\n\n# Build per-subject listgroups: map each list to its condition\n# In this dataset, lists are mixed within subjects (each list has its own condition)\n# Use None for lists without valid condition data (they will be excluded from grouped analyses)\nlistgroup = []\nfor subj_idx in range(egg.n_subjects):\n subj_listgroup = []\n for list_idx in range(egg.n_lists):\n try:\n sample = egg.pres.loc[(subj_idx, list_idx)][0]\n if sample and 'condition' in sample:\n subj_listgroup.append(sample['condition'])\n else:\n # Use the same group name as valid lists to avoid separate \"Unknown\" category\n # These are likely practice/buffer lists\n subj_listgroup.append(None)\n except (KeyError, IndexError, TypeError):\n subj_listgroup.append(None)\n listgroup.append(subj_listgroup)\n\n# Count lists per condition (excluding None)\nall_conditions = [c for subj in listgroup for c in subj if c is not None]\nprint(f\"Lists per condition: {dict(Counter(all_conditions))}\")\n\n# Create a figure with subplots for each analysis\nfig, axes = plt.subplots(2, 2, figsize=(12, 10))\n\n# 1. Probability of First Recall - use quail's built-in plot with error bars\npfr = egg.analyze('pfr', listgroup=listgroup)\npfr.plot(ax=axes[0, 0], plot_type='list', legend=True)\n\n# 2. Lag-CRP - use quail's built-in plot with error bars (legend=False)\nlagcrp = egg.analyze('lagcrp', listgroup=listgroup)\nlagcrp.plot(ax=axes[0, 1], plot_type='list', legend=False)\n\n# 3. Serial Position Curve - use quail's built-in plot with error bars (legend=False)\nspc = egg.analyze('spc', listgroup=listgroup)\nspc.plot(ax=axes[1, 0], plot_type='list', legend=False)\n\n# Configure PFR plot\naxes[0, 0].set_title('Probability of First Recall')\naxes[0, 0].set_xlabel('Serial Position')\naxes[0, 0].set_ylabel('Probability')\naxes[0, 0].set_ylim([0, 0.3])\n\n# Configure Lag-CRP plot\naxes[0, 1].set_title('Lag-CRP')\naxes[0, 1].set_xlabel('Lag')\naxes[0, 1].set_ylabel('Conditional Recall Probability')\naxes[0, 1].set_xlim([-10, 10])\naxes[0, 1].axvline(x=0, color='gray', linestyle='--', alpha=0.5)\n\n# Configure SPC plot\naxes[1, 0].set_title('Serial Position Curve')\naxes[1, 0].set_xlabel('Serial Position')\naxes[1, 0].set_ylabel('Recall Probability')\naxes[1, 0].set_ylim([0, 1])\n\n# 4. Memory Fingerprint - averaged across all lists\navg_listgroup = ['average'] * egg.n_lists\nfingerprint = egg.analyze('fingerprint', features=['task', 'temporal'],\n listgroup=avg_listgroup)\nfingerprint.plot(ax=axes[1, 1], title='Memory Fingerprint', ylim=[0, 1])\naxes[1, 1].set_xlabel('Feature')\naxes[1, 1].set_ylabel('Clustering Score')\n\nplt.tight_layout()\nplt.suptitle('Polyn et al. (2009) CMR Dataset Analysis', y=1.02, fontsize=14)\nplt.savefig('cmr_analysis.png', dpi=150, bbox_inches='tight')\nplt.show()\n\nprint(\"\\nAnalysis complete! Saved plot to cmr_analysis.png\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }