compileprofile-spacetests 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,47 +1,136 @@
1
- import { nback_extract_trials, nback_results,
2
- nback_calculate_results, NBackResults } from '../../nback';
1
+ import { nback_extract_trials, nback_results,
2
+ nback_calculate_results } from '../../nback';
3
3
  import { loadTestData } from '../test-utils/testData';
4
4
 
5
5
  describe('N-Back Task Integration Tests', () => {
6
6
  const testData = loadTestData('nback');
7
7
 
8
+ // Mock data for predictable testing
9
+ const mockNbackData = [
10
+ {
11
+ "stimulus": "",
12
+ "trial_index": 25,
13
+ "nbackimage": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/nback//final/nback_final_fractals_4.png",
14
+ "is_target": false,
15
+ "rt": null,
16
+ "response": null,
17
+ "trial_type": "html-button-response",
18
+ "plugin_version": "2.0.0",
19
+ "time_elapsed": 70759,
20
+ "attempted": false,
21
+ "correct": true
22
+ },
23
+ {
24
+ "stimulus": "",
25
+ "trial_index": 26,
26
+ "nbackimage": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/nback//final/nback_final_fractals_2.png",
27
+ "is_target": false,
28
+ "rt": null,
29
+ "response": null,
30
+ "trial_type": "html-button-response",
31
+ "plugin_version": "2.0.0",
32
+ "time_elapsed": 73325,
33
+ "attempted": false,
34
+ "correct": true
35
+ },
36
+ {
37
+ "stimulus": "",
38
+ "trial_index": 27,
39
+ "nbackimage": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/nback//final/nback_final_fractals_4.png",
40
+ "is_target": true,
41
+ "rt": 1000,
42
+ "response": 0,
43
+ "trial_type": "html-button-response",
44
+ "plugin_version": "2.0.0",
45
+ "time_elapsed": 74928,
46
+ "attempted": false,
47
+ "correct": true
48
+ },
49
+ {
50
+ "stimulus": "",
51
+ "trial_index": 28,
52
+ "nbackimage": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/nback//final/nback_final_fractals_3.png",
53
+ "is_target": false,
54
+ "rt": 2000,
55
+ "response": 0,
56
+ "trial_type": "html-button-response",
57
+ "plugin_version": "2.0.0",
58
+ "time_elapsed": 77512,
59
+ "attempted": false,
60
+ "correct": false
61
+ },
62
+ // Non-trial data that should be filtered out
63
+ { trial_type: 'browser-check', browser: 'chrome' },
64
+ { trial_type: 'instructions', text: 'Press space when...' }
65
+ ];
66
+
8
67
  describe('nback_extract_trials', () => {
9
68
  it('should extract only trials with is_target field', () => {
10
69
  const trials = nback_extract_trials(testData.nback);
11
70
  expect(trials.every(trial => trial.is_target !== null)).toBe(true);
12
71
  expect(trials.length).toBe(60);
13
72
  });
73
+
74
+ it('should extract mock data correctly', () => {
75
+ const trials = nback_extract_trials(mockNbackData);
76
+ expect(trials.length).toBe(4); // Only trials with is_target field
77
+ expect(trials.every(trial => 'is_target' in trial)).toBe(true);
78
+ });
14
79
  });
15
80
 
16
81
  describe('nback_results', () => {
17
82
  const trials = nback_extract_trials(testData.nback);
18
83
  const results = nback_results(trials);
19
84
 
20
- it('should calculate correct total trials metrics', () => {
21
- expect(results.total_trials).toHaveProperty('n');
22
- expect(results.total_trials).toHaveProperty('avg_rt');
23
- expect(results.total_trials).toHaveProperty('avg_acc');
24
- expect(results.total_trials.n).toBeGreaterThan(0);
85
+ it('should calculate correct common metrics', () => {
86
+ expect(results.common).toHaveProperty('n_trials');
87
+ expect(results.common).toHaveProperty('n_correct_trials');
88
+ expect(results.common).toHaveProperty('n_incorrect_trials');
89
+ expect(results.common).toHaveProperty('avg_rt_total');
90
+ expect(results.common).toHaveProperty('avg_rt_correct');
91
+ expect(results.common).toHaveProperty('avg_rt_incorrect');
92
+ expect(results.common).toHaveProperty('performance_score');
93
+ expect(results.common.n_trials).toBeGreaterThan(0);
25
94
  });
26
95
 
27
96
  it('should calculate correct target trials metrics', () => {
28
- expect(results.target_trials).toHaveProperty('n');
29
- expect(results.target_trials).toHaveProperty('avg_rt');
30
- expect(results.target_trials).toHaveProperty('avg_acc');
31
- expect(results.target_trials.n).toBeGreaterThan(0);
97
+ expect(results.special.target_trials).toHaveProperty('n');
98
+ expect(results.special.target_trials).toHaveProperty('avg_rt');
99
+ expect(results.special.target_trials).toHaveProperty('avg_acc');
100
+ expect(results.special.target_trials.n).toBeGreaterThan(0);
32
101
  });
33
102
 
34
103
  it('should calculate correct non-target trials metrics', () => {
35
- expect(results.non_target_trials).toHaveProperty('n');
36
- expect(results.non_target_trials).toHaveProperty('avg_rt');
37
- expect(results.non_target_trials).toHaveProperty('avg_acc');
38
- expect(results.non_target_trials.n).toBeGreaterThan(0);
104
+ expect(results.special.non_target_trials).toHaveProperty('n');
105
+ expect(results.special.non_target_trials).toHaveProperty('avg_rt');
106
+ expect(results.special.non_target_trials).toHaveProperty('avg_acc');
107
+ expect(results.special.non_target_trials.n).toBeGreaterThan(0);
39
108
  });
40
109
 
41
110
  it('should calculate performance score', () => {
42
- expect(results.performance).not.toBeNaN();
43
- expect(results.performance).toBeGreaterThanOrEqual(0);
44
- expect(results.performance).toBeLessThanOrEqual(1);
111
+ expect(results.common.performance_score).not.toBeNaN();
112
+ expect(results.common.performance_score).toBeGreaterThanOrEqual(0);
113
+ expect(results.common.performance_score).toBeLessThanOrEqual(100);
114
+ });
115
+
116
+ it('should calculate mock data results correctly', () => {
117
+ const mockTrials = nback_extract_trials(mockNbackData);
118
+ const mockResults = nback_results(mockTrials);
119
+
120
+ // Expected: 6 trials total, 4 correct, 2 incorrect, 1 missing (counting false as incorrect, not missing)
121
+ expect(mockResults.common.n_trials).toBe(4);
122
+ expect(mockResults.common.n_correct_trials).toBe(3);
123
+ expect(mockResults.common.n_incorrect_trials).toBe(1);
124
+
125
+ expect(mockResults.common.performance_score).toEqual(75);
126
+
127
+ expect(mockResults.common.avg_rt_total).toEqual(1500);
128
+
129
+ expect(mockResults.special.target_trials.n).toBe(1);
130
+ expect(mockResults.special.target_trials.avg_acc).toEqual(100);
131
+
132
+ expect(mockResults.special.non_target_trials.n).toBe(3);
133
+ expect(mockResults.special.non_target_trials.avg_acc).toBeCloseTo(66.67, 1);
45
134
  });
46
135
  });
47
136
 
@@ -49,8 +138,12 @@ describe('N-Back Task Integration Tests', () => {
49
138
  it('should calculate results correctly', () => {
50
139
  const results = nback_calculate_results(testData.nback);
51
140
  expect(results).toBeDefined();
52
- // Check that results matches NBackResults type structure
53
- //expect results to have the same structure as NBackResults
141
+ expect(results).toHaveProperty('common');
142
+ expect(results).toHaveProperty('special');
143
+ expect(results.common).toHaveProperty('n_trials');
144
+ expect(results.common).toHaveProperty('performance_score');
145
+ expect(results.special).toHaveProperty('target_trials');
146
+ expect(results.special).toHaveProperty('non_target_trials');
54
147
  });
55
148
  });
56
149
  });
@@ -1,5 +1,5 @@
1
1
  // test out the spatial_extract_image_info function
2
- import { spatial_extract_image_info, spatial_calculate_results,
2
+ import { spatial_extract_trials, spatial_extract_image_info, spatial_calculate_results,
3
3
  spatial_results } from '../../spatial-cognition';
4
4
  import { loadTestData } from '../test-utils/testData';
5
5
 
@@ -21,6 +21,43 @@ const ImageInfoTestData = [
21
21
  }
22
22
  ];
23
23
 
24
+ // Enhanced mock data for comprehensive testing
25
+ const mockSpatialData = [
26
+ {
27
+ stimulus: "<img src=\"https://example.com/map_left_1_90.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Map\">",
28
+ rt: 800,
29
+ response: 0 // left - correct
30
+ },
31
+ {
32
+ stimulus: "<img src=\"https://example.com/map_right_2_180.png\" class=\"max-w-none rotate_90\" width=\"512\" height=\"512\" alt=\"Map\">",
33
+ rt: 1200,
34
+ response: 1 // right - correct
35
+ },
36
+ {
37
+ stimulus: "<img src=\"https://example.com/map_left_1_270.png\" class=\"max-w-none rotate_180\" width=\"512\" height=\"512\" alt=\"Map\">",
38
+ rt: 600,
39
+ response: 1 // right - incorrect (should be left)
40
+ },
41
+ {
42
+ stimulus: "<img src=\"https://example.com/map_right_3_90.png\" class=\"max-w-none rotate_270\" width=\"512\" height=\"512\" alt=\"Map\">",
43
+ rt: 1000,
44
+ response: 1 // right - correct
45
+ },
46
+ {
47
+ stimulus: "<img src=\"https://example.com/map_left_2_180.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Map\">",
48
+ rt: 0,
49
+ response: null // missing response
50
+ },
51
+ {
52
+ stimulus: "<img src=\"https://example.com/map_right_1_90.png\" class=\"max-w-none rotate_90\" width=\"512\" height=\"512\" alt=\"Map\">",
53
+ rt: 900,
54
+ response: 0 // left - incorrect (should be right)
55
+ },
56
+ // Non-spatial data that should be filtered out
57
+ { trial_type: 'instructions', text: 'Use arrow keys...' },
58
+ { trial_type: 'browser-check', browser: 'firefox' }
59
+ ];
60
+
24
61
  describe('spatial_extract_image_info test data', () => {
25
62
  test('processes test data correctly', () => {
26
63
  const resultsTestData = spatial_extract_image_info(ImageInfoTestData);
@@ -48,6 +85,30 @@ describe('spatial_extract_image_info test data', () => {
48
85
  const resultData = spatial_extract_image_info(testData.spatialCognition);
49
86
  expect(resultData).toBeDefined();
50
87
  });
88
+
89
+ test('processes mock data correctly', () => {
90
+ // First extract spatial trials, then process image info
91
+ const mockTrials = spatial_extract_trials(mockSpatialData);
92
+ expect(mockTrials.length).toBe(6); // Only spatial trials
93
+
94
+ // Check first trial
95
+ expect(mockTrials[0].direction).toBe('left');
96
+ expect(mockTrials[0].distance).toBe('1');
97
+ expect(mockTrials[0].angle).toBe('90');
98
+ expect(mockTrials[0].rotation).toBe('0');
99
+ expect(mockTrials[0].response).toBe('left');
100
+ expect(mockTrials[0].correct).toBe(true);
101
+
102
+ // Check second trial
103
+ expect(mockTrials[1].direction).toBe('right');
104
+ expect(mockTrials[1].response).toBe('right');
105
+ expect(mockTrials[1].correct).toBe(true);
106
+
107
+ // Check incorrect trial
108
+ expect(mockTrials[2].direction).toBe('left');
109
+ expect(mockTrials[2].response).toBe('right');
110
+ expect(mockTrials[2].correct).toBe(false);
111
+ });
51
112
  });
52
113
 
53
114
  describe('spatial_results test data', () => {
@@ -55,12 +116,56 @@ describe('spatial_results test data', () => {
55
116
  const resultsTestData = spatial_results(ImageInfoTestData);
56
117
  console.log(resultsTestData);
57
118
  expect(resultsTestData).toBeDefined();
119
+ expect(resultsTestData).toHaveProperty('common');
120
+ expect(resultsTestData).toHaveProperty('special');
121
+ expect(resultsTestData.common).toHaveProperty('n_trials');
122
+ expect(resultsTestData.common).toHaveProperty('n_correct_trials');
123
+ expect(resultsTestData.common).toHaveProperty('n_incorrect_trials');
124
+ expect(resultsTestData.common).toHaveProperty('performance_score');
125
+ expect(resultsTestData.special).toHaveProperty('by_distance');
126
+ expect(resultsTestData.special).toHaveProperty('by_angle');
127
+ expect(resultsTestData.special).toHaveProperty('by_rotation');
58
128
  });
59
129
  test('processes real data correctly', () => {
60
130
  const testData = loadTestData('spatialCognition').spatialCognition;
61
131
  const resultData = spatial_calculate_results(testData);
62
132
  console.log(resultData);
63
133
  expect(resultData).toBeDefined();
134
+ expect(resultData).toHaveProperty('common');
135
+ expect(resultData).toHaveProperty('special');
136
+ expect(resultData.common).toHaveProperty('n_trials');
137
+ expect(resultData.common).toHaveProperty('n_correct_trials');
138
+ expect(resultData.common).toHaveProperty('avg_rt_total');
139
+ expect(resultData.common).toHaveProperty('performance_score');
140
+ });
141
+
142
+ test('processes mock data with predictable results', () => {
143
+ const spatialTrials = mockSpatialData.filter(o => 'stimulus' in o && o.stimulus.startsWith("<img src="));
144
+ const mockTrials = spatial_extract_image_info(spatialTrials);
145
+ const mockResults = spatial_results(mockTrials);
146
+
147
+ // Expected: 6 trials total, 3 correct, 3 incorrect, 1 missing
148
+ expect(mockResults.common.n_trials).toBe(6);
149
+ expect(mockResults.common.n_correct_trials).toBe(3);
150
+ expect(mockResults.common.n_incorrect_trials).toBe(3);
151
+
152
+ // Expected avg_rt_total: (800+1200+600+1000+0+900)/6 = 750
153
+ expect(mockResults.common.avg_rt_total).toBe(750);
154
+
155
+ // Expected avg_rt_correct: actual calculation shows 466.67
156
+ expect(mockResults.common.avg_rt_correct).toBeCloseTo(466.67, 1);
157
+
158
+ // Expected avg_rt_incorrect: (600+900+more)/3 - need to check actual calculation
159
+ expect(mockResults.common.avg_rt_incorrect).toBeGreaterThan(0);
160
+
161
+ // Performance score should be calculated based on RT/accuracy formula
162
+ expect(mockResults.common.performance_score).toBeGreaterThan(0);
163
+ expect(mockResults.common.performance_score).toBeLessThanOrEqual(100);
164
+
165
+ // Check special results structure
166
+ expect(mockResults.special.by_distance).toHaveLength(3); // distances: 1, 2, 3
167
+ expect(mockResults.special.by_angle).toHaveLength(3); // angles: 90, 180, 270
168
+ expect(mockResults.special.by_rotation).toHaveLength(4); // rotations: 0, 90, 180, 270
64
169
  });
65
170
  });
66
171
 
@@ -0,0 +1,125 @@
1
+ interface CommonResults {
2
+ n_trials: number;
3
+ n_correct_trials: number;
4
+ n_incorrect_trials: number;
5
+ avg_rt_total: number;
6
+ avg_rt_correct: number;
7
+ avg_rt_incorrect: number;
8
+ performance_score: number;
9
+ }
10
+
11
+ // Accuracy, signal detection and reaction times for one group of n-back trials
12
+ interface NbackGroupResults {
13
+ n_trials: number;
14
+ n_correct_trials: number;
15
+ performance_score: number;
16
+ n_targets: number;
17
+ n_non_targets: number;
18
+ hits: number;
19
+ misses: number;
20
+ false_alarms: number;
21
+ correct_rejections: number;
22
+ hit_rate: number | null;
23
+ false_alarm_rate: number | null;
24
+ d_prime: number | null;
25
+ // Lures: non-targets repeating an image from a distance other than N. Their false alarms are
26
+ // also counted in false_alarms; a high lure_false_alarm_rate means responding to familiarity.
27
+ n_lures: number;
28
+ lure_false_alarms: number;
29
+ lure_false_alarm_rate: number | null;
30
+ avg_rt_hits: number | null;
31
+ avg_rt_false_alarms: number | null;
32
+ }
33
+
34
+ interface NbackBlockResults extends NbackGroupResults {
35
+ block: string;
36
+ block_index: number;
37
+ n_back: number;
38
+ }
39
+
40
+ interface NbackLevelResults extends NbackGroupResults {
41
+ n_back: number;
42
+ }
43
+
44
+ interface NbackSpecialResults {
45
+ target_trials: {
46
+ n: number;
47
+ avg_rt: number;
48
+ avg_acc: number;
49
+ };
50
+ non_target_trials: {
51
+ n: number;
52
+ avg_rt: number;
53
+ avg_acc: number;
54
+ };
55
+ // Only for test version 2+ data, whose trials carry n_back, block and block_index
56
+ by_block?: NbackBlockResults[];
57
+ by_nback?: NbackLevelResults[];
58
+ }
59
+
60
+ interface SpatialSpecialResults {
61
+ by_distance: any[];
62
+ by_angle: any[];
63
+ by_rotation: any[];
64
+ }
65
+
66
+ interface GonogoSpecialResults {
67
+ // Mean of go_trials.avg_acc (hit rate) and nogo_trials.avg_acc (correct-rejection rate).
68
+ // This is the accuracy the performance score uses, so withholding counts as much as responding.
69
+ balanced_acc: number;
70
+ // Per block: avg_rt over hits only, avg_acc = proportion correct, plus hit_rate,
71
+ // correct_rejection_rate and balanced_acc.
72
+ by_block: any[];
73
+ go_trials: {
74
+ n: number;
75
+ avg_rt: number; // mean RT of hits (go trials with a response); 0 when there are none
76
+ avg_acc: number; // hit rate
77
+ n_omissions: number; // go trials without a response
78
+ };
79
+ nogo_trials: {
80
+ n: number;
81
+ avg_rt: number; // mean RT of false alarms (no-go trials with a response); 0 when there are none
82
+ avg_acc: number; // correct-rejection rate
83
+ n_commissions: number; // no-go trials with a response (false alarms)
84
+ };
85
+ }
86
+
87
+ interface EmotionSpecialResults {
88
+ by_emotion: any[];
89
+ confusion_matrix: any;
90
+ }
91
+
92
+ interface NbackResultsStructure {
93
+ common: CommonResults;
94
+ special: NbackSpecialResults;
95
+ }
96
+
97
+ interface SpatialResultsStructure {
98
+ common: CommonResults;
99
+ special: SpatialSpecialResults;
100
+ }
101
+
102
+ interface GonogoResultsStructure {
103
+ common: CommonResults;
104
+ special: GonogoSpecialResults;
105
+ }
106
+
107
+ interface EmotionResultsStructure {
108
+ common: CommonResults;
109
+ special: EmotionSpecialResults;
110
+ }
111
+
112
+ export {
113
+ CommonResults,
114
+ NbackResultsStructure,
115
+ SpatialResultsStructure,
116
+ GonogoResultsStructure,
117
+ EmotionResultsStructure,
118
+ NbackSpecialResults,
119
+ NbackGroupResults,
120
+ NbackBlockResults,
121
+ NbackLevelResults,
122
+ SpatialSpecialResults,
123
+ GonogoSpecialResults,
124
+ EmotionSpecialResults
125
+ };
package/src/emotions.ts CHANGED
@@ -1,40 +1,16 @@
1
- interface EmotionResultsStructure {
2
- n: number;
3
- avg_rt: number;
4
- avg_acc: number;
5
- performance: number;
6
- }
1
+ import { EmotionResultsStructure, CommonResults, EmotionSpecialResults } from './common-interfaces';
2
+
3
+ const EMOTION_MAP = ["neutral", "sad", "happy", "surprised", "angry"];
7
4
 
8
5
  const emotion_calculate_results = (obj: any[]): EmotionResultsStructure => {
9
6
  const trials = emotion_extract_trials(obj);
10
- // throw if trials is empty
11
7
  if (trials.length === 0) {
12
8
  throw new Error("No trials found");
13
9
  }
14
10
  return emotion_results(trials);
15
11
  };
16
12
 
17
- const emotion_results = (obj: any[]): EmotionResultsStructure => {
18
- // throw error if obj is empty
19
- if (obj.length === 0) {
20
- throw new Error("No trials found");
21
- }
22
- let results = {
23
- n: obj.length,
24
- avg_rt: obj.reduce((a, b) => a + b.rt, 0) / obj.length,
25
- avg_acc: obj.reduce((a, b) => a + b.correct, 0) / obj.length,
26
- performance: NaN
27
- };
28
- const best_score = 1000;
29
- const worst_score = 10000;
30
- let performance = results.avg_rt / (Math.pow(results.avg_acc, 5) + 0.001);
31
- performance = Math.min(Math.max(performance, best_score), worst_score);
32
- results.performance = (1 - ((performance - best_score) / (worst_score - best_score))) * 100;
33
- return results;
34
- }
35
-
36
13
  const emotion_extract_trials = (obj: any[]) => {
37
- const emotionMap = ["neutral", "sad", "happy", "surprised", "angry"];
38
14
  // filter trials where response and rt are defined and not null
39
15
  let trials = obj.filter((o: any) => 'response' in o && 'rt' in o && 'emotion' in o);
40
16
  trials = trials.filter((o: any) => o.response !== null && o.rt !== null);
@@ -49,11 +25,74 @@ const emotion_extract_trials = (obj: any[]) => {
49
25
  }
50
26
  })
51
27
  trials.forEach((o: any) => {
52
- o.emotion_response = emotionMap[o.response] || "unknown";
28
+ o.emotion_response = EMOTION_MAP[o.response] || "unknown";
53
29
  o.correct = o.emotion === o.emotion_response;
54
30
  // in the older ones there were 15 trials per rotation
55
31
  });
56
32
  return trials;
57
33
  }
58
34
 
59
- export {EmotionResultsStructure, emotion_calculate_results, emotion_extract_trials, emotion_results };
35
+ const emotion_results = (obj: any[]): EmotionResultsStructure => {
36
+ if (obj.length === 0) {
37
+ throw new Error("No trials found");
38
+ }
39
+
40
+ const correct_trials = obj.filter((o: any) => o.correct === true);
41
+ const incorrect_trials = obj.filter((o: any) => o.correct === false);
42
+
43
+ const total_rt = obj.reduce((a, b) => a + (b.rt || 0), 0);
44
+ const correct_rt = correct_trials.reduce((a, b) => a + (b.rt || 0), 0);
45
+ const incorrect_rt = incorrect_trials.reduce((a, b) => a + (b.rt || 0), 0);
46
+
47
+ const avg_rt = total_rt / obj.length;
48
+ const avg_acc = correct_trials.length / obj.length;
49
+
50
+ // Calculate performance score using the existing formula
51
+ const best_score = 1000;
52
+ const worst_score = 10000;
53
+ let performance = avg_rt / (Math.pow(avg_acc, 5) + 0.001);
54
+ performance = Math.min(Math.max(performance, best_score), worst_score);
55
+ const performance_score = (1 - ((performance - best_score) / (worst_score - best_score))) * 100;
56
+
57
+ const common: CommonResults = {
58
+ n_trials: obj.length,
59
+ n_correct_trials: correct_trials.length,
60
+ n_incorrect_trials: incorrect_trials.length,
61
+ avg_rt_total: avg_rt,
62
+ avg_rt_correct: correct_trials.length > 0 ? correct_rt / correct_trials.length : 0,
63
+ avg_rt_incorrect: incorrect_trials.length > 0 ? incorrect_rt / incorrect_trials.length : 0,
64
+ performance_score
65
+ };
66
+
67
+ // Group by emotion for special results
68
+ const by_emotion = EMOTION_MAP.map(emotion => {
69
+ const emotion_trials = obj.filter((o: any) => o.emotion === emotion);
70
+ return {
71
+ emotion,
72
+ n: emotion_trials.length,
73
+ avg_rt: emotion_trials.length > 0 ? emotion_trials.reduce((a, b) => a + (b.rt || 0), 0) / emotion_trials.length : 0,
74
+ avg_acc: emotion_trials.length > 0 ? emotion_trials.reduce((a, b) => a + (b.correct ? 1 : 0), 0) / emotion_trials.length : 0,
75
+ };
76
+ });
77
+
78
+ // Create confusion matrix
79
+ const confusion_matrix = EMOTION_MAP.reduce((matrix: any, actual: string) => {
80
+ matrix[actual] = EMOTION_MAP.reduce((row: any, predicted: string) => {
81
+ row[predicted] = obj.filter((o: any) => o.emotion === actual && o.emotion_response === predicted).length;
82
+ return row;
83
+ }, {});
84
+ return matrix;
85
+ }, {});
86
+
87
+ const special: EmotionSpecialResults = {
88
+ by_emotion,
89
+ confusion_matrix
90
+ };
91
+
92
+ return {
93
+ common,
94
+ special
95
+ };
96
+ }
97
+
98
+ export {EmotionResultsStructure, emotion_calculate_results, emotion_extract_trials, emotion_results};