4747 },
4848 {
4949 "cell_type" : " code" ,
50- "execution_count" : 2 ,
50+ "execution_count" : null ,
5151 "id" : " cell-2" ,
5252 "metadata" : {
5353 "execution" : {
5858 }
5959 },
6060 "outputs" : [],
61- "source" : [
62- " # Define tables for this tutorial\n " ,
63- " @schema\n " ,
64- " class Lab(dj.Manual):\n " ,
65- " definition = \"\"\"\n " ,
66- " lab_id : varchar(16)\n " ,
67- " ---\n " ,
68- " lab_name : varchar(100)\n " ,
69- " \"\"\"\n " ,
70- " \n " ,
71- " @schema\n " ,
72- " class Subject(dj.Manual):\n " ,
73- " definition = \"\"\"\n " ,
74- " subject_id : varchar(16)\n " ,
75- " ---\n " ,
76- " -> Lab\n " ,
77- " species : varchar(50)\n " ,
78- " date_of_birth : date\n " ,
79- " notes = '' : varchar(1000)\n " ,
80- " \"\"\"\n " ,
81- " \n " ,
82- " @schema\n " ,
83- " class Session(dj.Manual):\n " ,
84- " definition = \"\"\"\n " ,
85- " -> Subject\n " ,
86- " session_idx : int32\n " ,
87- " ---\n " ,
88- " session_date : date\n " ,
89- " duration : decimal(4,1) # minutes\n " ,
90- " \"\"\"\n " ,
91- " \n " ,
92- " class Trial(dj.Part):\n " ,
93- " definition = \"\"\"\n " ,
94- " -> master\n " ,
95- " trial_idx : int32\n " ,
96- " ---\n " ,
97- " outcome : enum('hit', 'miss', 'false_alarm', 'correct_reject')\n " ,
98- " reaction_time : decimal(3,2) # seconds\n " ,
99- " \"\"\"\n " ,
100- " \n " ,
101- " @schema\n " ,
102- " class ProcessedData(dj.Computed):\n " ,
103- " definition = \"\"\"\n " ,
104- " -> Session\n " ,
105- " ---\n " ,
106- " hit_rate : float32\n " ,
107- " \"\"\"\n " ,
108- " \n " ,
109- " def make(self, key):\n " ,
110- " outcomes = (Session.Trial & key).to_arrays('outcome')\n " ,
111- " n_trials = len(outcomes)\n " ,
112- " hit_rate = np.sum(outcomes == 'hit') / n_trials if n_trials else 0.0\n " ,
113- " self.insert1({**key, 'hit_rate': hit_rate})"
114- ]
61+ "source": "# Define tables for this tutorial\n@schema\nclass Lab(dj.Manual):\n definition = \"\"\"\n lab_id : varchar(16)\n ---\n lab_name : varchar(100)\n \"\"\"\n\n@schema\nclass Subject(dj.Manual):\n definition = \"\"\"\n subject_id : varchar(16)\n ---\n -> Lab\n species : varchar(50)\n date_of_birth : date\n notes = '' : varchar(1000)\n \"\"\"\n\n@schema\nclass Session(dj.Manual):\n definition = \"\"\"\n -> Subject\n session_idx : int32\n ---\n session_date : date\n duration : decimal(4,1) # minutes\n \"\"\"\n\n class Trial(dj.Part):\n definition = \"\"\"\n -> master\n trial_idx : int32\n ---\n outcome : enum('hit', 'miss', 'false_alarm', 'correct_reject')\n reaction_time : decimal(3,2) # seconds\n \"\"\"\n\n@schema\nclass SessionAnalysis(dj.Computed):\n definition = \"\"\"\n -> Session\n ---\n hit_rate : float32\n \"\"\"\n \n def make(self, key):\n outcomes = (Session.Trial & key).to_arrays('outcome')\n n_trials = len(outcomes)\n hit_rate = np.sum(outcomes == 'hit') / n_trials if n_trials else 0.0\n self.insert1({**key, 'hit_rate': hit_rate})"
11562 },
11663 {
11764 "cell_type" : " markdown" ,
982929 },
983930 {
984931 "cell_type" : " code" ,
985- "execution_count" : 16 ,
932+ "execution_count" : null ,
986933 "id" : " cell-29" ,
987934 "metadata" : {
988935 "execution" : {
992939 "shell.execute_reply" : " 2026-02-19T18:32:34.218561Z"
993940 }
994941 },
995- "outputs" : [
996- {
997- "name" : " stdout" ,
998- "output_type" : " stream" ,
999- "text" : [
1000- " Sessions: 1\n " ,
1001- " Trials: 5\n " ,
1002- " ProcessedData: 1\n "
1003- ]
1004- }
1005- ],
1006- "source" : [
1007- " # First, let's see what we have\n " ,
1008- " print(f\" Sessions: {len(Session())}\" )\n " ,
1009- " print(f\" Trials: {len(Session.Trial())}\" )\n " ,
1010- " \n " ,
1011- " # Populate computed table\n " ,
1012- " ProcessedData.populate()\n " ,
1013- " print(f\" ProcessedData: {len(ProcessedData())}\" )"
1014- ]
942+ "outputs" : [],
943+ "source" : " # First, let's see what we have\n print(f\" Sessions: {len(Session())}\" )\n print(f\" Trials: {len(Session.Trial())}\" )\n\n # Populate computed table\n SessionAnalysis.populate()\n print(f\" SessionAnalysis: {len(SessionAnalysis())}\" )"
1015944 },
1016945 {
1017946 "cell_type" : " code" ,
1018- "execution_count" : 17 ,
947+ "execution_count" : null ,
1019948 "id" : " cell-30" ,
1020949 "metadata" : {
1021950 "execution" : {
1025954 "shell.execute_reply" : " 2026-02-19T18:32:34.239434Z"
1026955 }
1027956 },
1028- "outputs" : [
1029- {
1030- "name" : " stdout" ,
1031- "output_type" : " stream" ,
1032- "text" : [
1033- " [2026-02-19 18:32:34] Deleting 5 rows from \" tutorial_data_entry\" .\" session__trial\"\n "
1034- ]
1035- },
1036- {
1037- "name" : " stdout" ,
1038- "output_type" : " stream" ,
1039- "text" : [
1040- " [2026-02-19 18:32:34] Deleting 1 rows from \" tutorial_data_entry\" .\" __processed_data\"\n "
1041- ]
1042- },
1043- {
1044- "name" : " stdout" ,
1045- "output_type" : " stream" ,
1046- "text" : [
1047- " [2026-02-19 18:32:34] Deleting 1 rows from \" tutorial_data_entry\" .\" session\"\n "
1048- ]
1049- },
1050- {
1051- "name" : " stdout" ,
1052- "output_type" : " stream" ,
1053- "text" : [
1054- " After delete:\n " ,
1055- " Sessions: 0\n " ,
1056- " Trials: 0\n " ,
1057- " ProcessedData: 0\n "
1058- ]
1059- }
1060- ],
1061- "source" : [
1062- " # Delete a session - cascades to Trial and ProcessedData\n " ,
1063- " (Session & {'subject_id': 'M001', 'session_idx': 1}).delete(prompt=False)\n " ,
1064- " \n " ,
1065- " print(f\" After delete:\" )\n " ,
1066- " print(f\" Sessions: {len(Session())}\" )\n " ,
1067- " print(f\" Trials: {len(Session.Trial())}\" )\n " ,
1068- " print(f\" ProcessedData: {len(ProcessedData())}\" )"
1069- ]
957+ "outputs" : [],
958+ "source" : " # Delete a session - cascades to Trial and SessionAnalysis\n (Session & {'subject_id': 'M001', 'session_idx': 1}).delete(prompt=False)\n\n print(f\" After delete:\" )\n print(f\" Sessions: {len(Session())}\" )\n print(f\" Trials: {len(Session.Trial())}\" )\n print(f\" SessionAnalysis: {len(SessionAnalysis())}\" )"
1070959 },
1071960 {
1072961 "cell_type" : " markdown" ,
11591048 },
11601049 {
11611050 "cell_type" : " code" ,
1162- "execution_count" : 19 ,
1051+ "execution_count" : null ,
11631052 "id" : " cell-34" ,
11641053 "metadata" : {
11651054 "execution" : {
11691058 "shell.execute_reply" : " 2026-02-19T18:32:34.262667Z"
11701059 }
11711060 },
1172- "outputs" : [
1173- {
1174- "name" : " stdout" ,
1175- "output_type" : " stream" ,
1176- "text" : [
1177- " Before correction: {'subject_id': 'M003', 'session_idx': 1, 'hit_rate': 0.5}\n "
1178- ]
1179- }
1180- ],
1181- "source" : [
1182- " # Add a session with trials (using transaction for compositional integrity)\n " ,
1183- " with dj.conn().transaction:\n " ,
1184- " Session.insert1({\n " ,
1185- " 'subject_id': 'M003',\n " ,
1186- " 'session_idx': 1,\n " ,
1187- " 'session_date': '2026-01-08',\n " ,
1188- " 'duration': 40.0\n " ,
1189- " })\n " ,
1190- " Session.Trial.insert([\n " ,
1191- " {'subject_id': 'M003', 'session_idx': 1, 'trial_idx': 1,\n " ,
1192- " 'outcome': 'hit', 'reaction_time': 0.35},\n " ,
1193- " {'subject_id': 'M003', 'session_idx': 1, 'trial_idx': 2,\n " ,
1194- " 'outcome': 'miss', 'reaction_time': 0.50},\n " ,
1195- " ])\n " ,
1196- " \n " ,
1197- " # Compute results\n " ,
1198- " ProcessedData.populate()\n " ,
1199- " print(\" Before correction:\" , ProcessedData.fetch1())"
1200- ]
1061+ "outputs" : [],
1062+ "source" : " # Add a session with trials (using transaction for compositional integrity)\n with dj.conn().transaction:\n Session.insert1({\n 'subject_id': 'M003',\n 'session_idx': 1,\n 'session_date': '2026-01-08',\n 'duration': 40.0\n })\n Session.Trial.insert([\n {'subject_id': 'M003', 'session_idx': 1, 'trial_idx': 1,\n 'outcome': 'hit', 'reaction_time': 0.35},\n {'subject_id': 'M003', 'session_idx': 1, 'trial_idx': 2,\n 'outcome': 'miss', 'reaction_time': 0.50},\n ])\n\n # Compute results\n SessionAnalysis.populate()\n print(\" Before correction:\" , SessionAnalysis.fetch1())"
12011063 },
12021064 {
12031065 "cell_type" : " code" ,
1204- "execution_count" : 20 ,
1066+ "execution_count" : null ,
12051067 "id" : " cell-35" ,
12061068 "metadata" : {
12071069 "execution" : {
12111073 "shell.execute_reply" : " 2026-02-19T18:32:34.287511Z"
12121074 }
12131075 },
1214- "outputs" : [
1215- {
1216- "name" : " stdout" ,
1217- "output_type" : " stream" ,
1218- "text" : [
1219- " [2026-02-19 18:32:34] Deleting 2 rows from \" tutorial_data_entry\" .\" session__trial\"\n "
1220- ]
1221- },
1222- {
1223- "name" : " stdout" ,
1224- "output_type" : " stream" ,
1225- "text" : [
1226- " [2026-02-19 18:32:34] Deleting 1 rows from \" tutorial_data_entry\" .\" __processed_data\"\n "
1227- ]
1228- },
1229- {
1230- "name" : " stdout" ,
1231- "output_type" : " stream" ,
1232- "text" : [
1233- " [2026-02-19 18:32:34] Deleting 1 rows from \" tutorial_data_entry\" .\" session\"\n "
1234- ]
1235- },
1236- {
1237- "name" : " stdout" ,
1238- "output_type" : " stream" ,
1239- "text" : [
1240- " After correction: {'subject_id': 'M003', 'session_idx': 1, 'hit_rate': 1.0}\n "
1241- ]
1242- }
1243- ],
1244- "source" : [
1245- " # Suppose we discovered trial 2 was actually a 'hit' not 'miss'\n " ,
1246- " # WRONG: Updating the trial would leave ProcessedData stale!\n " ,
1247- " # Session.Trial.update1({...}) # DON'T DO THIS\n " ,
1248- " \n " ,
1249- " # CORRECT: Delete, reinsert, recompute\n " ,
1250- " key = {'subject_id': 'M003', 'session_idx': 1}\n " ,
1251- " \n " ,
1252- " # 1. Delete cascades to ProcessedData\n " ,
1253- " (Session & key).delete(prompt=False)\n " ,
1254- " \n " ,
1255- " # 2. Reinsert with corrected data (using transaction)\n " ,
1256- " with dj.conn().transaction:\n " ,
1257- " Session.insert1({**key, 'session_date': '2026-01-08', 'duration': 40.0})\n " ,
1258- " Session.Trial.insert([\n " ,
1259- " {**key, 'trial_idx': 1, 'outcome': 'hit', 'reaction_time': 0.35},\n " ,
1260- " {**key, 'trial_idx': 2, 'outcome': 'hit', 'reaction_time': 0.50},\n " ,
1261- " ])\n " ,
1262- " \n " ,
1263- " # 3. Recompute\n " ,
1264- " ProcessedData.populate()\n " ,
1265- " print(\" After correction:\" , ProcessedData.fetch1())"
1266- ]
1076+ "outputs" : [],
1077+ "source" : " # Suppose we discovered trial 2 was actually a 'hit' not 'miss'\n # WRONG: Updating the trial would leave SessionAnalysis stale!\n # Session.Trial.update1({...}) # DON'T DO THIS\n\n # CORRECT: Delete, reinsert, recompute\n key = {'subject_id': 'M003', 'session_idx': 1}\n\n # 1. Delete cascades to SessionAnalysis\n (Session & key).delete(prompt=False)\n\n # 2. Reinsert with corrected data (using transaction)\n with dj.conn().transaction:\n Session.insert1({**key, 'session_date': '2026-01-08', 'duration': 40.0})\n Session.Trial.insert([\n {**key, 'trial_idx': 1, 'outcome': 'hit', 'reaction_time': 0.35},\n {**key, 'trial_idx': 2, 'outcome': 'hit', 'reaction_time': 0.50},\n ])\n\n # 3. Recompute\n SessionAnalysis.populate()\n print(\" After correction:\" , SessionAnalysis.fetch1())"
12671078 },
12681079 {
12691080 "cell_type" : " markdown" ,
15281339 },
15291340 "nbformat" : 4 ,
15301341 "nbformat_minor" : 5
1531- }
1342+ }
0 commit comments