Skip to content

Commit 57f0046

Browse files
authored
Merge pull request #8 from ubcspin/readme-ramp-up
fsr x event, calibrated words x interview
2 parents 8a9bfe9 + 8e4641f commit 57f0046

File tree

5 files changed

+311
-19
lines changed

5 files changed

+311
-19
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ matlab/tableau/processed_data_pxx
66
matlab/tableau/*.csv
77
matlab/experiments/results
88
matlab/util/load_all_data.m
9+
**/*.ipynb

matlab/experiments/event_fsr.m

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
load_all_processed;
2+
tprocess_scenes;
3+
4+
resultVarTypes = {'double', 'string', 'double', 'string', 'string', 'double', 'double' ...
5+
'double', 'double', 'double', 'double', 'double', 'double', 'double', 'double', 'double', 'double', ...
6+
'double', 'double', 'double', 'double', 'double', 'double', 'double', 'double', 'double', 'double', ...
7+
'double', 'double'};
8+
resultVarNames = {'timestamp_ms', 'label', 'pnum', 'type', 'scene', 'A0abs', 'A0var', ...
9+
'A0slp', 'A0slp2', 'A1abs', 'A1var', 'A1slp', 'A1slp2', 'A2abs', 'A2var', 'A2slp', 'A2slp2', ...
10+
'A3abs', 'A3var', 'A3slp', 'A3slp2', 'A4abs', 'A4var', 'A4slp', 'A4slp2', 'maxabs', 'maxvar', ...
11+
'maxslp', 'maxslp2' };
12+
result = table('Size', [0 29], 'VariableTypes', resultVarTypes, ...
13+
'VariableNames', resultVarNames);
14+
15+
for i = 1:size(all_data,1)
16+
pfile = all_data{i,1};
17+
18+
if ~isempty(pfile)
19+
fprintf('Extracting event {x} fsr data from participant %d...\n', i);
20+
21+
%% Combine all game and sound event data together, sort by timestamp
22+
g_rows = size(pfile.events.game_controlled_visual, 1);
23+
s_rows = size(pfile.events.game_controlled_sound, 1);
24+
25+
pfile.events.game_controlled_visual.pnum = ones(g_rows, 1) * i;
26+
pfile.events.game_controlled_sound.pnum = ones(s_rows, 1) * i;
27+
pfile.events.game_controlled_visual.type = repmat({'game_controlled_visual'}, g_rows, 1);
28+
pfile.events.game_controlled_sound.type = repmat({'game_controlled_sound'}, s_rows, 1);
29+
30+
% Label scene data for each event
31+
32+
offset = 500; % offset in ms for labeling scene data (events at the edge of scenes)
33+
34+
scenes_game = cell(size(pfile.events.game_controlled_visual,1),1);
35+
for j = 1:size(pfile.events.game_controlled_visual,1)
36+
ts = pfile.events.game_controlled_visual{j,1};
37+
rows = all_scenes.pnum == i & (all_scenes.start_ms - offset) <= ts & (all_scenes.end_ms + offset) >= ts;
38+
scene = all_scenes(rows, {'scene'});
39+
if size(scene,1) > 0
40+
scenes_game{j} = scene{1,1};
41+
else
42+
scenes_game{j} = 'none';
43+
end
44+
end
45+
pfile.events.game_controlled_visual.scene = scenes_game;
46+
47+
scenes_sound = cell(size(pfile.events.game_controlled_sound,1),1);
48+
for j = 1:size(pfile.events.game_controlled_sound,1)
49+
ts = pfile.events.game_controlled_sound{j,1};
50+
rows = all_scenes.pnum == i & (all_scenes.start_ms - offset) <= ts & (all_scenes.end_ms + offset) >= ts;
51+
scene = all_scenes(rows, {'scene'});
52+
scene_row = pfile.events.game_controlled_sound(j,:);
53+
if size(scene,1) > 0
54+
scenes_sound{j} = scene{1,1};
55+
else
56+
scenes_sound{j} = 'none';
57+
end
58+
end
59+
pfile.events.game_controlled_sound.scene = scenes_sound;
60+
61+
gs_events = vertcat(pfile.events.game_controlled_visual, pfile.events.game_controlled_sound);
62+
gs_events = sortrows(gs_events, [1,2]);
63+
gs_events = clean_up_events(gs_events);
64+
65+
% resample, linearly interpolate and smooth fsr data
66+
Fs = 500;
67+
fsr_abs = max( ...
68+
[pfile.fsr.A0, ...
69+
pfile.fsr.A1, ...
70+
pfile.fsr.A2, ...
71+
pfile.fsr.A3, ...
72+
pfile.fsr.A4], [], 2);
73+
74+
new_timestamp_ms = 0:2:pfile.fsr.timestamp_ms(length(pfile.fsr.timestamp_ms));
75+
fsr_A0 = interp1(pfile.fsr.timestamp_ms, pfile.fsr.A0, new_timestamp_ms);
76+
fsr_A1 = interp1(pfile.fsr.timestamp_ms, pfile.fsr.A1, new_timestamp_ms);
77+
fsr_A2 = interp1(pfile.fsr.timestamp_ms, pfile.fsr.A2, new_timestamp_ms);
78+
fsr_A3 = interp1(pfile.fsr.timestamp_ms, pfile.fsr.A3, new_timestamp_ms);
79+
fsr_A4 = interp1(pfile.fsr.timestamp_ms, pfile.fsr.A4, new_timestamp_ms);
80+
fsr_max = interp1(pfile.fsr.timestamp_ms, fsr_abs, new_timestamp_ms);
81+
82+
fsr_A0 = lowpass(fsr_A0, 0.1, Fs); % 0.1 is arbitrary based on inspection in plotting
83+
fsr_A1 = lowpass(fsr_A1, 0.1, Fs);
84+
fsr_A2 = lowpass(fsr_A2, 0.1, Fs);
85+
fsr_A3 = lowpass(fsr_A3, 0.1, Fs);
86+
fsr_A4 = lowpass(fsr_A4, 0.1, Fs);
87+
fsr_max = lowpass(fsr_max, 0.1, Fs);
88+
89+
%% Extract fsr data based on window around event timestamp
90+
[gs_events.A0abs, gs_events.A0var, gs_events.A0slp, gs_events.A0slp2] = ...
91+
extract_fsr(array2table([new_timestamp_ms', fsr_A0'], 'VariableNames', {'timestamp_ms', 'fsr'}), ...
92+
gs_events(:, 'timestamp_ms'), 1000, 5000);
93+
[gs_events.A1abs, gs_events.A1var, gs_events.A1slp, gs_events.A1slp2] = ...
94+
extract_fsr(array2table([new_timestamp_ms', fsr_A1'], 'VariableNames', {'timestamp_ms', 'fsr'}), ...
95+
gs_events(:, 'timestamp_ms'), 1000, 5000);
96+
[gs_events.A2abs, gs_events.A2var, gs_events.A2slp, gs_events.A2slp2] = ...
97+
extract_fsr(array2table([new_timestamp_ms', fsr_A2'], 'VariableNames', {'timestamp_ms', 'fsr'}), ...
98+
gs_events(:, 'timestamp_ms'), 1000, 5000);
99+
[gs_events.A3abs, gs_events.A3var, gs_events.A3slp, gs_events.A3slp2] = ...
100+
extract_fsr(array2table([new_timestamp_ms', fsr_A3'], 'VariableNames', {'timestamp_ms', 'fsr'}), ...
101+
gs_events(:, 'timestamp_ms'), 1000, 5000);
102+
[gs_events.A4abs, gs_events.A4var, gs_events.A4slp, gs_events.A4slp2] = ...
103+
extract_fsr(array2table([new_timestamp_ms', fsr_A4'], 'VariableNames', {'timestamp_ms', 'fsr'}), ...
104+
gs_events(:, 'timestamp_ms'), 1000, 5000);
105+
[gs_events.maxabs, gs_events.maxvar, gs_events.maxslp, gs_events.maxslp2] = ...
106+
extract_fsr(array2table([new_timestamp_ms', fsr_max'], 'VariableNames', {'timestamp_ms', 'fsr'}), ...
107+
gs_events(:, 'timestamp_ms'), 1000, 5000);
108+
109+
gs_events.Var3 = [];
110+
gs_events = gs_events(~ismissing(gs_events.label), :);
111+
112+
result = vertcat(result, gs_events);
113+
end
114+
end
115+
116+
writetable(result, './experiments/results/event_fsr_asym.csv')
117+
118+
function events = clean_up_events(events)
119+
events.label = strtrim(events.label);
120+
events.label = strrep(events.label, ' ', '-');
121+
events.label = lower(events.label);
122+
end
123+
124+
function [abs, variance, slp, slp2] = extract_fsr(timeseries, timestamps, low_off, high_off)
125+
abs = zeros(height(timestamps), 1);
126+
variance = zeros(height(timestamps), 1);
127+
slp = zeros(height(timestamps), 1);
128+
slp2 = zeros(height(timestamps), 1);
129+
130+
for j = 1:height(timestamps)
131+
ts = timestamps{j,1};
132+
rows = timeseries.timestamp_ms <= ts + high_off & ...
133+
timeseries.timestamp_ms >= ts - low_off;
134+
fsr_in_window = timeseries(rows, {'timestamp_ms', 'fsr'});
135+
if size(fsr_in_window, 1) > 0
136+
abs(j,1) = max(fsr_in_window.fsr);
137+
variance(j,1) = var(fsr_in_window.fsr);
138+
139+
% Calculate average slope in window
140+
slp(j,1) = (fsr_in_window.fsr(end) - fsr_in_window.fsr(1)) ...
141+
/ (fsr_in_window.timestamp_ms(end) - fsr_in_window.timestamp_ms(1));
142+
143+
% Calculate individual slopes in window and average
144+
slopes = diff(fsr_in_window.fsr)./diff(fsr_in_window.timestamp_ms);
145+
slp2(j,1) = mean(slopes);
146+
else
147+
abs(j,1) = NaN;
148+
variance(j,1) = NaN;
149+
slp(j,1) = NaN;
150+
slp2(j,1) = NaN;
151+
end
152+
end
153+
return;
154+
end

matlab/experiments/event_joystick.m

+30-18
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@
6868
[gs_events.w4abs, gs_events.w4var, gs_events.w4slp, gs_events.w4slp2] = ...
6969
extract_joystick(pfile.joystick, gs_events(:, 'timestamp_ms'), 750, 5000);
7070

71+
gs_events.Var3 = [];
72+
gs_events = gs_events(~ismissing(gs_events.label), :);
73+
7174
result = vertcat(result, gs_events);
7275
end
7376
end
@@ -116,28 +119,31 @@
116119
% from such a distribution, using the one-sample Kolmogorov-Smirnov test. The result h is 1 if
117120
% the test rejects the null hypothesis at the 5% significance level, or 0 otherwise.
118121

122+
% Here we use Shapiro-Wilk test for normality testing (h = swtest(x, alpha)). It works similarly
123+
% but has the best power for a given significance compared to other normality tests.
124+
119125
d_res = {};
120126
d_res{1,1} = 'label';
121-
d_res{1,2} = 'spread-abs';
122-
d_res{1,3} = 'spread-var';
123-
d_res{1,4} = 'mean-abs';
124-
d_res{1,5} = 'sd-abs';
125-
d_res{1,6} = 'h-abs';
126-
d_res{1,7} = 'p-abs';
127-
d_res{1,8} = 'n-abs';
128-
d_res{1,9} = 'kurt-abs';
129-
d_res{1,10} = 'skew-abs';
127+
d_res{1,2} = 'spread_abs';
128+
d_res{1,3} = 'spread_var';
129+
d_res{1,4} = 'mean_abs';
130+
d_res{1,5} = 'sd_abs';
131+
d_res{1,6} = 'h_abs';
132+
d_res{1,7} = 'p_abs';
133+
d_res{1,8} = 'n_abs';
134+
d_res{1,9} = 'kurt_abs';
135+
d_res{1,10} = 'skew_abs';
130136

131-
d_res{1,11} = 'mean-var';
132-
d_res{1,12} = 'sd-var';
133-
d_res{1,13} = 'h-var';
134-
d_res{1,14} = 'p-var';
135-
d_res{1,15} = 'n-var';
136-
d_res{1,16} = 'kurt-var';
137-
d_res{1,17} = 'skew-var';
137+
d_res{1,11} = 'mean_var';
138+
d_res{1,12} = 'sd_var';
139+
d_res{1,13} = 'h_var';
140+
d_res{1,14} = 'p_var';
141+
d_res{1,15} = 'n_var';
142+
d_res{1,16} = 'kurt_var';
143+
d_res{1,17} = 'skew_var';
138144

139-
d_res{1,18} = 'iqr-abs';
140-
d_res{1,19} = 'iqr-var';
145+
d_res{1,18} = 'iqr_abs';
146+
d_res{1,19} = 'iqr_var';
141147
d_res{1,20} = 'scene';
142148

143149
for i = 1:size(fe,1)
@@ -251,6 +257,8 @@
251257
end
252258
end
253259

260+
event_x_joystick = cell2table(d_res(2:end,:),'VariableNames',d_res(1,:));
261+
writetable(event_x_joystick, './experiments/results/event_x_joystick.csv');
254262
clearvars -except all_data result
255263

256264
%% HELPER FUNCTIONS
@@ -261,6 +269,10 @@
261269
events.label = lower(events.label);
262270
end
263271

272+
% abs: max value of joystick data in time window
273+
% variance: variance of joystick data in time window
274+
% slp: end - start / time in a time window
275+
% slp2: average of slopes of every 2 adjacent points in time window
264276
function [abs, variance, slp, slp2] = extract_joystick(timeseries, timestamps, low_off, high_off)
265277
abs = zeros(height(timestamps), 1);
266278
variance = zeros(height(timestamps), 1);
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
load_all_processed;
2+
tprocess_scenes;
3+
4+
resultVarTypes = {'double', 'double', 'string', 'string', 'double', 'double', 'double' ...
5+
'double', 'double', 'double', 'double', 'double', 'double'};
6+
resultVarNames = {'pnum', 'timestamp_ms', 'emotion_words', 'calibrated_words', 'calibrated_values', 'w1abs', 'w1var', ...
7+
'w1slp', 'w1slp2', 'w2abs', 'w2var', 'w2slp', 'w2slp2'};
8+
result = table('Size', [0 13], 'VariableTypes', resultVarTypes, ...
9+
'VariableNames', resultVarNames);
10+
11+
for i = 1:size(all_data,1)
12+
pfile = all_data{i,1};
13+
14+
if ~isempty(pfile)
15+
fprintf('Extracting interview {x} joystick data from participant %d...\n', i);
16+
17+
interviews = pfile.calibrated_words;
18+
rows = size(interviews, 1);
19+
interviews.pnum = ones(rows, 1) * str2num(pfile.scalars.trial_number);
20+
21+
%% normalize joystick to [-10 10] scale
22+
pfile.joystick.joystick = (pfile.joystick.joystick * 20 / max(pfile.joystick.joystick)) - 10;
23+
24+
%% Extract joystick data based on window around event timestamp
25+
[interviews.w1abs, interviews.w1var, interviews.w1slp, interviews.w1slp2] = ...
26+
extract_joystick(pfile.joystick, interviews(:, 'timestamp_ms'), 1000, 1000);
27+
[interviews.w2abs, interviews.w2var, interviews.w2slp, interviews.w2slp2] = ...
28+
extract_joystick(pfile.joystick, interviews(:, 'timestamp_ms'), 2000, 2000);
29+
30+
result = vertcat(result, interviews);
31+
end
32+
end
33+
34+
writetable(result, './experiments/results/interview_joystick_asym.csv')
35+
clearvars -except all_data result
36+
37+
38+
% abs: max value of joystick data in time window
39+
% variance: variance of joystick data in time window
40+
% slp: end - start / time in a time window
41+
% slp2: average of slopes of every 2 adjacent points in time window
42+
function [abs, variance, slp, slp2] = extract_joystick(timeseries, timestamps, low_off, high_off)
43+
abs = zeros(height(timestamps), 1);
44+
variance = zeros(height(timestamps), 1);
45+
slp = zeros(height(timestamps), 1);
46+
slp2 = zeros(height(timestamps), 1);
47+
48+
for j = 1:height(timestamps)
49+
ts = timestamps{j,1};
50+
rows = timeseries.timestamp_ms <= ts + high_off & ...
51+
timeseries.timestamp_ms >= ts - low_off;
52+
joystick_in_window = timeseries(rows, {'timestamp_ms', 'joystick'});
53+
if size(joystick_in_window, 1) > 0
54+
abs(j,1) = max(joystick_in_window.joystick);
55+
variance(j,1) = var(joystick_in_window.joystick);
56+
57+
% Calculate average slope in window
58+
slp(j,1) = (joystick_in_window.joystick(end) - joystick_in_window.joystick(1)) ...
59+
/ (joystick_in_window.timestamp_ms(end) - joystick_in_window.timestamp_ms(1));
60+
61+
% Calculate individual slopes in window and average
62+
slopes = diff(joystick_in_window.joystick)./diff(joystick_in_window.timestamp_ms);
63+
slp2(j,1) = mean(slopes);
64+
else
65+
abs(j,1) = NaN;
66+
variance(j,1) = NaN;
67+
slp(j,1) = NaN;
68+
slp2(j,1) = NaN;
69+
end
70+
end
71+
return;
72+
end

matlab/readme.md

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
Data Pre-Processing Scripts:
2+
## Data Pre-Processing Scripts:
33

44
These are used with matlab to pre-process raw data from the EEG study.
55
To use:
@@ -87,3 +87,56 @@ Matlab will then process the video with frame numbers so that you can get the pr
8787

8888
-
8989

90+
91+
## tableau
92+
93+
scripts created to prepare csv for tableau
94+
95+
***
96+
97+
Before you start
98+
99+
***
100+
- Add `processed_data_pxx` to path (right click Add to Path, selected folders and subfolders, icon solidifies to indicate added to path)
101+
- Directory structure (for folders related to tableau)
102+
```
103+
-- matlab
104+
|-- tableau
105+
| |-- xxx.m
106+
| |-- xxx.csv
107+
|-- experiments
108+
|-- xxx.m
109+
|-- results
110+
|-- xxx.csv
111+
```
112+
***
113+
114+
Event x joystick analysis
115+
116+
***
117+
118+
`matlab/experiments/event_joystick.m`
119+
120+
1. Extracting joystick data
121+
- For every event of each participant, extract joystick data around the event timestamp
122+
- Compare four time windows (250ms, 500ms, 750ms, 1000ms before and 5000ms after the event timestamp)
123+
- `abs`: max value of joystick data in time window
124+
- `variance`: variance of joystick data in time window
125+
- `slp`: end - start / time in a time window
126+
- `slp2`: average of slopes of every 2 adjacent points in time window
127+
- Output saved in `event_joystick_asym.csv`
128+
2. Spread in time window
129+
- Calculate spread of joystick data for events that have been experienced by at least 16 participants
130+
- Record abs, var, slp for each of four time windows
131+
- Output saved in `ej_spread_5500_9500.csv`
132+
3. Normality testing
133+
- [Shapiro-Wilk test](https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/13964/versions/2/previews/swtest.m/index.html) at alpha = 0.05. It has the best power for a given significance compared to other normality tests.
134+
- Perform normality testing on value (`abs`) and variance (`var`) with time window 1s before and 5s after
135+
- Output saved in `event_x_joystick.csv`
136+
- Interpretation:
137+
- "h-abs/h-var" refers to the results of the normality hypothesis testing - 0 means the null hypothesis is NOT rejected and p>0.05, and hence the distribution is NORMAL. "p-abs/p-var" are p-values.
138+
- "kurt-abs/kurt-var" is kurtosis - kurt > 3 means the distribution is more outlier-prone than standard normal distribution (aka the distribution is heavier tailed than standard normal) and vice versa.
139+
- "skew-abs/skew-var" is skewness of the distribution - negative means skewed to the left, positive means skewed to the right
140+
- "spread-abs/spread-var" is based on the results of the normality testing
141+
142+

0 commit comments

Comments
 (0)