diff --git a/Final Project/overall_analysis.ipynb b/Final Project/overall_analysis.ipynb index 57b32c3..d15d70e 100755 --- a/Final Project/overall_analysis.ipynb +++ b/Final Project/overall_analysis.ipynb @@ -24,10 +24,10 @@ "id": "578c9128", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:19.274466Z", - "iopub.status.busy": "2026-04-22T03:09:19.273226Z", - "iopub.status.idle": "2026-04-22T03:09:19.280048Z", - "shell.execute_reply": "2026-04-22T03:09:19.279206Z" + "iopub.execute_input": "2026-04-22T03:20:38.290986Z", + "iopub.status.busy": "2026-04-22T03:20:38.290709Z", + "iopub.status.idle": "2026-04-22T03:20:38.297801Z", + "shell.execute_reply": "2026-04-22T03:20:38.296447Z" } }, "outputs": [], @@ -42,10 +42,10 @@ "id": "857b22c0", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:19.283002Z", - "iopub.status.busy": "2026-04-22T03:09:19.282756Z", - "iopub.status.idle": "2026-04-22T03:09:20.342432Z", - "shell.execute_reply": "2026-04-22T03:09:20.341961Z" + "iopub.execute_input": "2026-04-22T03:20:38.300669Z", + "iopub.status.busy": "2026-04-22T03:20:38.300420Z", + "iopub.status.idle": "2026-04-22T03:20:39.360556Z", + "shell.execute_reply": "2026-04-22T03:20:39.360152Z" } }, "outputs": [], @@ -77,10 +77,10 @@ "id": "dc4b2c55", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:20.343870Z", - "iopub.status.busy": "2026-04-22T03:09:20.343760Z", - "iopub.status.idle": "2026-04-22T03:09:20.346854Z", - "shell.execute_reply": "2026-04-22T03:09:20.346506Z" + "iopub.execute_input": "2026-04-22T03:20:39.362226Z", + "iopub.status.busy": "2026-04-22T03:20:39.362118Z", + "iopub.status.idle": "2026-04-22T03:20:39.364971Z", + "shell.execute_reply": "2026-04-22T03:20:39.364522Z" } }, "outputs": [], @@ -118,21 +118,11 @@ "MOTOR_CH = ['C3', 'Cz', 'C4']\n", "MU_BAND = (8, 13)\n", "\n", - "# Sessions are stored with compound key \"{ses_id}_{kind}_{stim}\" and looked up\n", - "# positionally: idx=0 is the first session of that (kind, stim) type sorted by\n", - "# key, idx=1 is the second. This handles subjects like 002 where OFFLINE and\n", - "# ONLINE sessions share the same ses-Sxxx number in the filename.\n", "PAIRS = [\n", " {'name': 'Pair1 (train=OFFLINE_FES)',\n", - " 'train': {'kind': 'OFFLINE', 'stim': 'FES', 'idx': 0},\n", - " 'online_fes': {'kind': 'ONLINE', 'stim': 'FES', 'idx': 0},\n", - " 'online_nofes': {'kind': 'ONLINE', 'stim': 'NOFES', 'idx': 0},\n", - " },\n", + " 'train': 'S001', 'online_fes': 'S002', 'online_nofes': 'S003'},\n", " {'name': 'Pair2 (train=OFFLINE_NOFES)',\n", - " 'train': {'kind': 'OFFLINE', 'stim': 'NOFES', 'idx': 0},\n", - " 'online_fes': {'kind': 'ONLINE', 'stim': 'FES', 'idx': 1},\n", - " 'online_nofes': {'kind': 'ONLINE', 'stim': 'NOFES', 'idx': 1},\n", - " },\n", + " 'train': 'S004', 'online_fes': 'S006', 'online_nofes': 'S005'},\n", "]" ] }, @@ -150,10 +140,10 @@ "id": "e798b039", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:20.348424Z", - "iopub.status.busy": "2026-04-22T03:09:20.348357Z", - "iopub.status.idle": "2026-04-22T03:09:20.361153Z", - "shell.execute_reply": "2026-04-22T03:09:20.360830Z" + "iopub.execute_input": "2026-04-22T03:20:39.366319Z", + "iopub.status.busy": "2026-04-22T03:20:39.366243Z", + "iopub.status.idle": "2026-04-22T03:20:39.379175Z", + "shell.execute_reply": "2026-04-22T03:20:39.378716Z" } }, "outputs": [], @@ -391,10 +381,10 @@ "id": "d266216b", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:20.362577Z", - "iopub.status.busy": "2026-04-22T03:09:20.362504Z", - "iopub.status.idle": "2026-04-22T03:09:59.345624Z", - "shell.execute_reply": "2026-04-22T03:09:59.271366Z" + "iopub.execute_input": "2026-04-22T03:20:39.380444Z", + "iopub.status.busy": "2026-04-22T03:20:39.380365Z", + "iopub.status.idle": "2026-04-22T03:21:10.042926Z", + "shell.execute_reply": "2026-04-22T03:21:10.041519Z" } }, "outputs": [ @@ -404,174 +394,30 @@ "text": [ "Found 24 XDF file(s).\n", "Preprocessing: notch 60 Hz → CAR → bandpass 8–30 Hz → baseline-correct → PTP-reject @ 100 µV\n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 002/S001 OFFLINE FES n= 85 (MI=43, REST=42) rej=5\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 002/S002 ONLINE FES n= 53 (MI=27, REST=26) rej=7 mk_acc=0.883\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 002/S003 ONLINE NOFES n= 52 (MI=26, REST=26) rej=8 mk_acc=0.833\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 002/S004 OFFLINE NOFES n= 90 (MI=45, REST=45) rej=0\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 002/S005 ONLINE NOFES n= 60 (MI=30, REST=30) rej=0 mk_acc=0.850\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 002/S006 ONLINE FES n= 56 (MI=27, REST=29) rej=4 mk_acc=0.917\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 003/S001 OFFLINE FES n= 89 (MI=44, REST=45) rej=1\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 003/S002 ONLINE FES n= 59 (MI=29, REST=30) rej=1 mk_acc=0.750\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 003/S003 ONLINE NOFES n= 38 (MI=17, REST=21) rej=0 mk_acc=0.763\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 003/S004 OFFLINE NOFES n= 86 (MI=42, REST=44) rej=4\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 003/S005 ONLINE NOFES n= 43 (MI=19, REST=24) rej=17 mk_acc=0.717\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 003/S006 ONLINE FES n= 52 (MI=23, REST=29) rej=8 mk_acc=0.767\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 005/S001 OFFLINE FES n= 90 (MI=45, REST=45) rej=0\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 005/S002 ONLINE FES n= 60 (MI=30, REST=30) rej=0 mk_acc=0.800\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 005/S003 ONLINE NOFES n= 59 (MI=29, REST=30) rej=1 mk_acc=0.933\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 005/S004 OFFLINE NOFES n= 89 (MI=44, REST=45) rej=1\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 005/S005 ONLINE NOFES n= 58 (MI=28, REST=30) rej=2 mk_acc=0.783\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 005/S006 ONLINE FES n= 59 (MI=30, REST=29) rej=1 mk_acc=0.917\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 009/S001 OFFLINE FES n= 57 (MI=33, REST=24) rej=33\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 009/S002 ONLINE FES n= 42 (MI=21, REST=21) rej=18 mk_acc=0.717\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 009/S003 ONLINE NOFES n= 1 (MI=1, REST=0) rej=59 mk_acc=0.717\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 009/S004 OFFLINE NOFES n= 86 (MI=42, REST=44) rej=4\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 009/S005 ONLINE NOFES n= 60 (MI=30, REST=30) rej=0 mk_acc=0.850\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", + " 002/S001 OFFLINE FES n= 85 (MI=43, REST=42) rej=5\n", + " 002/S002 ONLINE FES n= 53 (MI=27, REST=26) rej=7 mk_acc=0.883\n", + " 002/S003 ONLINE NOFES n= 52 (MI=26, REST=26) rej=8 mk_acc=0.833\n", + " 002/S004 OFFLINE NOFES n= 90 (MI=45, REST=45) rej=0\n", + " 002/S005 ONLINE NOFES n= 60 (MI=30, REST=30) rej=0 mk_acc=0.850\n", + " 002/S006 ONLINE FES n= 56 (MI=27, REST=29) rej=4 mk_acc=0.917\n", + " 003/S001 OFFLINE FES n= 89 (MI=44, REST=45) rej=1\n", + " 003/S002 ONLINE FES n= 59 (MI=29, REST=30) rej=1 mk_acc=0.750\n", + " 003/S003 ONLINE NOFES n= 38 (MI=17, REST=21) rej=0 mk_acc=0.763\n", + " 003/S004 OFFLINE NOFES n= 86 (MI=42, REST=44) rej=4\n", + " 003/S005 ONLINE NOFES n= 43 (MI=19, REST=24) rej=17 mk_acc=0.717\n", + " 003/S006 ONLINE FES n= 52 (MI=23, REST=29) rej=8 mk_acc=0.767\n", + " 005/S001 OFFLINE FES n= 90 (MI=45, REST=45) rej=0\n", + " 005/S002 ONLINE FES n= 60 (MI=30, REST=30) rej=0 mk_acc=0.800\n", + " 005/S003 ONLINE NOFES n= 59 (MI=29, REST=30) rej=1 mk_acc=0.933\n", + " 005/S004 OFFLINE NOFES n= 89 (MI=44, REST=45) rej=1\n", + " 005/S005 ONLINE NOFES n= 58 (MI=28, REST=30) rej=2 mk_acc=0.783\n", + " 005/S006 ONLINE FES n= 59 (MI=30, REST=29) rej=1 mk_acc=0.917\n", + " 009/S001 OFFLINE FES n= 57 (MI=33, REST=24) rej=33\n", + " 009/S002 ONLINE FES n= 42 (MI=21, REST=21) rej=18 mk_acc=0.717\n", + " 009/S003 ONLINE NOFES n= 1 (MI=1, REST=0) rej=59 mk_acc=0.717\n", + " 009/S004 OFFLINE NOFES n= 86 (MI=42, REST=44) rej=4\n", + " 009/S005 ONLINE NOFES n= 60 (MI=30, REST=30) rej=0 mk_acc=0.850\n", " 009/S006 ONLINE FES n= 50 (MI=26, REST=24) rej=10 mk_acc=0.817\n", "\n", "Loaded 4 subject(s): ['002', '003', '005', '009'] | total artifact-rejected epochs: 184\n" @@ -600,9 +446,7 @@ " print(f' ERROR {os.path.basename(fp)}: {e}')\n", " continue\n", "\n", - " # Compound key prevents collision when multiple session types share the same ses_id\n", - " key = f'{ses_id}_{kind}_{stim}'\n", - " sessions.setdefault(subj, {})[key] = dict(\n", + " sessions.setdefault(subj, {})[ses_id] = dict(\n", " X=X, y=y, kind=kind, stim=stim,\n", " ch_names=ch_names, sfreq=sfreq,\n", " mk_acc=mk_acc, file=os.path.basename(fp))\n", @@ -615,16 +459,7 @@ "\n", "subjects = sorted(sessions.keys())\n", "print(f'\\nLoaded {len(subjects)} subject(s): {subjects} | '\n", - " f'total artifact-rejected epochs: {total_rej}')\n", - "\n", - "\n", - "def pick_session(subj_ses, kind, stim, idx):\n", - " \"\"\"Return the idx-th session matching (kind, stim), sorted by key. None if not enough.\"\"\"\n", - " matches = sorted(\n", - " (v for v in subj_ses.values() if v['kind'] == kind and v['stim'] == stim),\n", - " key=lambda v: v['file'] # alphabetical file order = recording order\n", - " )\n", - " return matches[idx] if idx < len(matches) else None" + " f'total artifact-rejected epochs: {total_rej}')" ] }, { @@ -641,10 +476,10 @@ "id": "611baf23", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:59.485105Z", - "iopub.status.busy": "2026-04-22T03:09:59.481587Z", - "iopub.status.idle": "2026-04-22T03:09:59.616890Z", - "shell.execute_reply": "2026-04-22T03:09:59.612929Z" + "iopub.execute_input": "2026-04-22T03:21:10.051665Z", + "iopub.status.busy": "2026-04-22T03:21:10.051368Z", + "iopub.status.idle": "2026-04-22T03:21:10.057208Z", + "shell.execute_reply": "2026-04-22T03:21:10.056834Z" } }, "outputs": [ @@ -692,10 +527,10 @@ "id": "f5e80da3", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:09:59.637480Z", - "iopub.status.busy": "2026-04-22T03:09:59.637343Z", - "iopub.status.idle": "2026-04-22T03:10:03.257068Z", - "shell.execute_reply": "2026-04-22T03:10:03.255453Z" + "iopub.execute_input": "2026-04-22T03:21:10.059575Z", + "iopub.status.busy": "2026-04-22T03:21:10.059490Z", + "iopub.status.idle": "2026-04-22T03:21:11.605957Z", + "shell.execute_reply": "2026-04-22T03:21:11.605570Z" } }, "outputs": [ @@ -734,15 +569,13 @@ " subj_ses = sessions[subj]\n", "\n", " for pair in PAIRS:\n", - " train = pick_session(subj_ses, pair['train']['kind'], pair['train']['stim'], pair['train']['idx'])\n", - " te_f = pick_session(subj_ses, pair['online_fes']['kind'], pair['online_fes']['stim'], pair['online_fes']['idx'])\n", - " te_n = pick_session(subj_ses, pair['online_nofes']['kind'], pair['online_nofes']['stim'], pair['online_nofes']['idx'])\n", - "\n", - " missing = [role for role, ses in [('train', train), ('online_fes', te_f), ('online_nofes', te_n)] if ses is None]\n", + " needed = (pair['train'], pair['online_fes'], pair['online_nofes'])\n", + " missing = [k for k in needed if k not in subj_ses]\n", " if missing:\n", - " print(f'[{subj}] {pair[\"name\"]}: no session found for {missing} — skipping')\n", + " print(f'[{subj}] {pair[\"name\"]}: missing {missing} — skipping')\n", " continue\n", "\n", + " train = subj_ses[pair['train']]\n", " if set(np.unique(train['y'])) != {0, 1}:\n", " print(f'[{subj}] {pair[\"name\"]}: training set lacks both classes — skipping')\n", " continue\n", @@ -750,7 +583,8 @@ " clf = CSPLDA(n_csp=N_CSP).fit(train['X'], train['y'])\n", " train_acc = (clf.predict(train['X']) == train['y']).mean()\n", "\n", - " for te, cond_label in [(te_f, 'FES'), (te_n, 'NOFES')]:\n", + " for cond_key, cond_label in [('online_fes', 'FES'), ('online_nofes', 'NOFES')]:\n", + " te = subj_ses[pair[cond_key]]\n", " acc = te['mk_acc']\n", " if acc is None:\n", " print(f'[{subj}] {pair[\"name\"]} / {cond_label}: no EARLYSTOP markers — skipping')\n", @@ -798,10 +632,10 @@ "id": "d53e63b9", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:10:03.262351Z", - "iopub.status.busy": "2026-04-22T03:10:03.262228Z", - "iopub.status.idle": "2026-04-22T03:10:04.250444Z", - "shell.execute_reply": "2026-04-22T03:10:04.249983Z" + "iopub.execute_input": "2026-04-22T03:21:11.609669Z", + "iopub.status.busy": "2026-04-22T03:21:11.609563Z", + "iopub.status.idle": "2026-04-22T03:21:12.097953Z", + "shell.execute_reply": "2026-04-22T03:21:12.097507Z" } }, "outputs": [ @@ -885,10 +719,10 @@ "id": "393042a0", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:10:04.254162Z", - "iopub.status.busy": "2026-04-22T03:10:04.254059Z", - "iopub.status.idle": "2026-04-22T03:10:05.210590Z", - "shell.execute_reply": "2026-04-22T03:10:05.210168Z" + "iopub.execute_input": "2026-04-22T03:21:12.099369Z", + "iopub.status.busy": "2026-04-22T03:21:12.099281Z", + "iopub.status.idle": "2026-04-22T03:21:12.990027Z", + "shell.execute_reply": "2026-04-22T03:21:12.989595Z" } }, "outputs": [ @@ -963,10 +797,10 @@ "id": "75df404b", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:10:05.215990Z", - "iopub.status.busy": "2026-04-22T03:10:05.215881Z", - "iopub.status.idle": "2026-04-22T03:10:05.485686Z", - "shell.execute_reply": "2026-04-22T03:10:05.485120Z" + "iopub.execute_input": "2026-04-22T03:21:12.992553Z", + "iopub.status.busy": "2026-04-22T03:21:12.992447Z", + "iopub.status.idle": "2026-04-22T03:21:13.261572Z", + "shell.execute_reply": "2026-04-22T03:21:13.261249Z" } }, "outputs": [ @@ -1035,10 +869,10 @@ "id": "cf55268e", "metadata": { "execution": { - "iopub.execute_input": "2026-04-22T03:10:05.496520Z", - "iopub.status.busy": "2026-04-22T03:10:05.496412Z", - "iopub.status.idle": "2026-04-22T03:10:05.506439Z", - "shell.execute_reply": "2026-04-22T03:10:05.506014Z" + "iopub.execute_input": "2026-04-22T03:21:13.263110Z", + "iopub.status.busy": "2026-04-22T03:21:13.263012Z", + "iopub.status.idle": "2026-04-22T03:21:13.268334Z", + "shell.execute_reply": "2026-04-22T03:21:13.267954Z" } }, "outputs": [