analysis(matlab): convert previous (Forouzan) study to our long format

Add forouzan_load_data.m (joins raw/Data_Forouzan.csv + raw/Total_Success.csv)
and make_forouzan_data.m, producing analysis/forouzan_reach_data.csv in our
curated column format (subject, group, day, success, total). Group is mapped
Anodal -> b2_f (tDCS), Control -> a2_f (control); Hand ignored; day 0-indexed
(Session 1 = day 0 = paper 'Day 1'); early-stopped sessions (total=0) dropped.

Conversion validated by refitting the paper's own LME on the converted data:
interaction t(227)=2.66, F(1)=7.09, p=0.0083 -- reproduces the paper's
reported t(227)=2.68, F(1)=7.12, p=0.008, N=24 (12 Anodal / 12 Control).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Experiments DB Dev
2026-07-22 14:18:47 -04:00
parent a4dff31772
commit d9b65a3279
3 changed files with 348 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
function T = forouzan_load_data()
%FOROUZAN_LOAD_DATA Load the previous (Forouzan) tDCS reaching study into our
% long format, with group labels matching our convention.
%
% T = FOROUZAN_LOAD_DATA() reads two raw files under analysis/matlab/raw/:
% Data_Forouzan.csv long, fill-down RatName; per rat/session: success,
% failure, total (attempts). Sessions 1..10; rats that
% stopped early have blank success and total = 0
% (encoded #DIV/0!), which are dropped.
% Total_Success.csv per-rat Group (Anodal | Control) and Hand.
%
% and returns a table with the same variables as TDCS_LOAD_DATA:
% subject (string) rat name
% group (categorical) 'b2_f' for Anodal (tDCS), 'a2_f' for Control
% day (double) Session - 1, so day 0 = the paper's "Day 1"
% (matching our 0-indexed day convention)
% success (double) successful reaches in the session
% total (double) total attempts in the session
%
% Hand is ignored per request. The "_f" suffix marks this as the Forouzan
% (previous) dataset, distinct from our Electrode-Box-A2/B2 animals.
thisDir = fileparts(mfilename('fullpath'));
longFile = fullfile(thisDir, 'raw', 'Data_Forouzan.csv');
grpFile = fullfile(thisDir, 'raw', 'Total_Success.csv');
% --- group map: rat -> Anodal|Control -> b2_f|a2_f -----------------------
G = readtable(grpFile, 'VariableNamingRule', 'preserve');
ratNames = string(G{:, 1}); % col 1 = Rat_Name (BOM-safe by index)
rawGroup = string(G{:, end}); % last col = Group (Anodal|Control)
% Keep only the real Anodal/Control rows (drops any header/blank artifact that
% readtable mis-parses as data because of the numeric 0..9 column headers).
keep = ismember(rawGroup, ["Anodal", "Control"]);
ratNames = ratNames(keep);
rawGroup = rawGroup(keep);
label = repmat("a2_f", numel(ratNames), 1);
label(rawGroup == "Anodal") = "b2_f"; % Control -> a2_f (default), Anodal -> b2_f
grpMap = containers.Map(cellstr(ratNames), cellstr(label));
% --- parse the long success/total file (fill-down RatName) ---------------
lines = readlines(longFile);
lines = lines(strlength(strip(lines)) > 0);
lines(1) = erase(lines(1), char(65279)); % strip UTF-8 BOM from the header
subj = strings(0, 1); day = []; success = []; total = [];
curRat = "";
for i = 2:numel(lines) % skip header row
p = strsplit(lines(i), ',', 'CollapseDelimiters', false);
name = strtrim(p(1));
if strlength(name) > 0
curRat = name;
end
suc = str2double(p(3));
tot = str2double(p(5));
if isnan(suc) || isnan(tot) || tot == 0 % no session that day -> drop
continue
end
subj(end + 1, 1) = curRat; %#ok<AGROW>
day(end + 1, 1) = str2double(p(2)) - 1; %#ok<AGROW> Session 1 -> day 0
success(end + 1, 1) = suc; %#ok<AGROW>
total(end + 1, 1) = tot; %#ok<AGROW>
end
% --- attach group and finalise -------------------------------------------
grp = strings(numel(subj), 1);
for i = 1:numel(subj)
if ~isKey(grpMap, char(subj(i)))
error('forouzan_load_data:missingRat', ...
'Rat "%s" in Data_Forouzan.csv has no group in Total_Success.csv.', subj(i));
end
grp(i) = grpMap(char(subj(i)));
end
T = table(subj, categorical(grp), day, success, total, ...
'VariableNames', {'subject', 'group', 'day', 'success', 'total'});
T = sortrows(T, {'subject', 'day'});
end