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:
@@ -0,0 +1,232 @@
|
|||||||
|
subject,group,day,success,total
|
||||||
|
Afrasyab,a2_f,0,1,13
|
||||||
|
Afrasyab,a2_f,1,8,24
|
||||||
|
Afrasyab,a2_f,2,13,35
|
||||||
|
Afrasyab,a2_f,3,8,29
|
||||||
|
Afrasyab,a2_f,4,21,43
|
||||||
|
Afrasyab,a2_f,5,14,42
|
||||||
|
Afrasyab,a2_f,6,18,46
|
||||||
|
Afrasyab,a2_f,7,21,52
|
||||||
|
Afrasyab,a2_f,8,21,41
|
||||||
|
Afrasyab,a2_f,9,21,55
|
||||||
|
Arash,b2_f,0,9,34
|
||||||
|
Arash,b2_f,1,12,41
|
||||||
|
Arash,b2_f,2,15,43
|
||||||
|
Arash,b2_f,3,18,44
|
||||||
|
Arash,b2_f,4,16,33
|
||||||
|
Arash,b2_f,5,18,47
|
||||||
|
Arash,b2_f,6,13,48
|
||||||
|
Arash,b2_f,7,9,38
|
||||||
|
Arash,b2_f,8,18,43
|
||||||
|
Arash,b2_f,9,15,47
|
||||||
|
Ashkas,a2_f,0,2,30
|
||||||
|
Ashkas,a2_f,1,11,35
|
||||||
|
Ashkas,a2_f,2,10,30
|
||||||
|
Ashkas,a2_f,3,9,33
|
||||||
|
Ashkas,a2_f,4,9,41
|
||||||
|
Ashkas,a2_f,5,10,36
|
||||||
|
Ashkas,a2_f,6,13,46
|
||||||
|
Ashkas,a2_f,7,9,42
|
||||||
|
Ashkas,a2_f,8,6,36
|
||||||
|
Ashkas,a2_f,9,15,48
|
||||||
|
Fariborz,a2_f,0,8,32
|
||||||
|
Fariborz,a2_f,1,10,32
|
||||||
|
Fariborz,a2_f,2,9,34
|
||||||
|
Fariborz,a2_f,3,11,35
|
||||||
|
Fariborz,a2_f,4,13,35
|
||||||
|
Fariborz,a2_f,5,5,38
|
||||||
|
Fariborz,a2_f,6,12,44
|
||||||
|
Garsivaz,b2_f,0,10,34
|
||||||
|
Garsivaz,b2_f,1,12,30
|
||||||
|
Garsivaz,b2_f,2,14,35
|
||||||
|
Garsivaz,b2_f,3,27,56
|
||||||
|
Garsivaz,b2_f,4,22,41
|
||||||
|
Garsivaz,b2_f,5,30,57
|
||||||
|
Garsivaz,b2_f,6,34,52
|
||||||
|
Garsivaz,b2_f,7,25,49
|
||||||
|
Garsivaz,b2_f,8,29,46
|
||||||
|
Garsivaz,b2_f,9,25,44
|
||||||
|
Iraj,b2_f,0,6,29
|
||||||
|
Iraj,b2_f,1,13,37
|
||||||
|
Iraj,b2_f,2,15,32
|
||||||
|
Iraj,b2_f,3,20,49
|
||||||
|
Iraj,b2_f,4,17,47
|
||||||
|
Iraj,b2_f,5,21,47
|
||||||
|
Iraj,b2_f,6,24,49
|
||||||
|
Khosrow,b2_f,0,6,23
|
||||||
|
Khosrow,b2_f,1,7,27
|
||||||
|
Khosrow,b2_f,2,10,27
|
||||||
|
Khosrow,b2_f,3,10,26
|
||||||
|
Khosrow,b2_f,4,14,35
|
||||||
|
Khosrow,b2_f,5,20,41
|
||||||
|
Khosrow,b2_f,6,11,37
|
||||||
|
Khosrow,b2_f,7,13,34
|
||||||
|
Khosrow,b2_f,8,13,30
|
||||||
|
Khosrow,b2_f,9,15,40
|
||||||
|
Kiyanoush,b2_f,0,11,19
|
||||||
|
Kiyanoush,b2_f,1,22,52
|
||||||
|
Kiyanoush,b2_f,2,34,58
|
||||||
|
Kiyanoush,b2_f,3,30,52
|
||||||
|
Kiyanoush,b2_f,4,27,56
|
||||||
|
Kiyanoush,b2_f,5,46,65
|
||||||
|
Kiyanoush,b2_f,6,32,58
|
||||||
|
Kiyanoush,b2_f,7,47,61
|
||||||
|
Kiyanoush,b2_f,8,49,68
|
||||||
|
Kiyanoush,b2_f,9,41,49
|
||||||
|
Manuchehr,b2_f,0,1,22
|
||||||
|
Manuchehr,b2_f,1,8,18
|
||||||
|
Manuchehr,b2_f,2,9,41
|
||||||
|
Manuchehr,b2_f,3,14,47
|
||||||
|
Manuchehr,b2_f,4,25,60
|
||||||
|
Manuchehr,b2_f,5,22,70
|
||||||
|
Manuchehr,b2_f,6,37,74
|
||||||
|
Manuchehr,b2_f,7,40,75
|
||||||
|
Manuchehr,b2_f,8,32,55
|
||||||
|
Manuchehr,b2_f,9,29,69
|
||||||
|
Mehrab,a2_f,0,8,28
|
||||||
|
Mehrab,a2_f,1,13,26
|
||||||
|
Mehrab,a2_f,2,16,45
|
||||||
|
Mehrab,a2_f,3,17,34
|
||||||
|
Mehrab,a2_f,4,21,45
|
||||||
|
Mehrab,a2_f,5,21,55
|
||||||
|
Mehrab,a2_f,6,22,51
|
||||||
|
Mehrab,a2_f,7,21,48
|
||||||
|
Mehrab,a2_f,8,21,48
|
||||||
|
Mehrab,a2_f,9,23,45
|
||||||
|
Merdas,a2_f,0,7,30
|
||||||
|
Merdas,a2_f,1,9,36
|
||||||
|
Merdas,a2_f,2,8,33
|
||||||
|
Merdas,a2_f,3,17,48
|
||||||
|
Merdas,a2_f,4,21,45
|
||||||
|
Merdas,a2_f,5,13,39
|
||||||
|
Merdas,a2_f,6,19,43
|
||||||
|
Merdas,a2_f,7,10,48
|
||||||
|
Merdas,a2_f,8,19,52
|
||||||
|
Merdas,a2_f,9,20,47
|
||||||
|
Nozar,a2_f,0,4,20
|
||||||
|
Nozar,a2_f,1,5,23
|
||||||
|
Nozar,a2_f,2,8,43
|
||||||
|
Nozar,a2_f,3,8,48
|
||||||
|
Nozar,a2_f,4,11,40
|
||||||
|
Nozar,a2_f,5,13,53
|
||||||
|
Nozar,a2_f,6,15,50
|
||||||
|
Nozar,a2_f,7,19,46
|
||||||
|
Nozar,a2_f,8,18,56
|
||||||
|
Nozar,a2_f,9,20,47
|
||||||
|
Pashang,b2_f,0,5,32
|
||||||
|
Pashang,b2_f,1,7,51
|
||||||
|
Pashang,b2_f,2,18,49
|
||||||
|
Pashang,b2_f,3,16,43
|
||||||
|
Pashang,b2_f,4,21,36
|
||||||
|
Pashang,b2_f,5,16,53
|
||||||
|
Pashang,b2_f,6,18,42
|
||||||
|
Pashang,b2_f,7,23,44
|
||||||
|
Pashang,b2_f,8,20,43
|
||||||
|
Pashang,b2_f,9,34,57
|
||||||
|
Pashin,a2_f,0,7,27
|
||||||
|
Pashin,a2_f,1,9,22
|
||||||
|
Pashin,a2_f,2,11,41
|
||||||
|
Pashin,a2_f,3,14,40
|
||||||
|
Pashin,a2_f,4,12,36
|
||||||
|
Pashin,a2_f,5,14,43
|
||||||
|
Pashin,a2_f,6,11,42
|
||||||
|
Pashin,a2_f,7,14,47
|
||||||
|
Pashin,a2_f,8,13,50
|
||||||
|
Pashin,a2_f,9,18,45
|
||||||
|
Rostam,b2_f,0,9,29
|
||||||
|
Rostam,b2_f,1,12,36
|
||||||
|
Rostam,b2_f,2,14,42
|
||||||
|
Rostam,b2_f,3,18,41
|
||||||
|
Rostam,b2_f,4,15,45
|
||||||
|
Rostam,b2_f,5,20,49
|
||||||
|
Rostam,b2_f,6,21,51
|
||||||
|
Rostam,b2_f,7,24,48
|
||||||
|
Rostam,b2_f,8,19,46
|
||||||
|
Rostam,b2_f,9,23,48
|
||||||
|
Salm,a2_f,0,7,28
|
||||||
|
Salm,a2_f,1,10,36
|
||||||
|
Salm,a2_f,2,16,50
|
||||||
|
Salm,a2_f,3,18,49
|
||||||
|
Salm,a2_f,4,21,62
|
||||||
|
Salm,a2_f,5,26,60
|
||||||
|
Salm,a2_f,6,27,53
|
||||||
|
Salm,a2_f,7,32,62
|
||||||
|
Salm,a2_f,8,16,40
|
||||||
|
Salm,a2_f,9,16,43
|
||||||
|
Sam,b2_f,0,8,28
|
||||||
|
Sam,b2_f,1,11,32
|
||||||
|
Sam,b2_f,2,10,26
|
||||||
|
Sam,b2_f,3,11,39
|
||||||
|
Sam,b2_f,4,12,36
|
||||||
|
Sam,b2_f,5,14,34
|
||||||
|
Sam,b2_f,6,15,43
|
||||||
|
Sam,b2_f,7,9,31
|
||||||
|
Sam,b2_f,8,23,51
|
||||||
|
Sam,b2_f,9,19,45
|
||||||
|
Siavash,a2_f,0,10,21
|
||||||
|
Siavash,a2_f,1,3,25
|
||||||
|
Siavash,a2_f,2,7,39
|
||||||
|
Siavash,a2_f,3,13,36
|
||||||
|
Siavash,a2_f,4,12,35
|
||||||
|
Siavash,a2_f,5,18,32
|
||||||
|
Siavash,a2_f,6,18,45
|
||||||
|
Siavash,a2_f,7,22,37
|
||||||
|
Siavash,a2_f,8,18,36
|
||||||
|
Siavash,a2_f,9,17,34
|
||||||
|
Sohrab,b2_f,0,8,23
|
||||||
|
Sohrab,b2_f,1,11,34
|
||||||
|
Sohrab,b2_f,2,15,45
|
||||||
|
Sohrab,b2_f,3,19,47
|
||||||
|
Sohrab,b2_f,4,18,50
|
||||||
|
Sohrab,b2_f,5,18,45
|
||||||
|
Sohrab,b2_f,6,16,45
|
||||||
|
Sohrab,b2_f,7,20,49
|
||||||
|
Sohrab,b2_f,8,14,32
|
||||||
|
Sohrab,b2_f,9,23,43
|
||||||
|
Tahmasb,b2_f,0,7,32
|
||||||
|
Tahmasb,b2_f,1,11,38
|
||||||
|
Tahmasb,b2_f,2,8,37
|
||||||
|
Tahmasb,b2_f,3,7,26
|
||||||
|
Tahmasb,b2_f,4,12,35
|
||||||
|
Tahmasb,b2_f,5,13,39
|
||||||
|
Tahmasb,b2_f,6,11,41
|
||||||
|
Tahmasb,b2_f,7,15,52
|
||||||
|
Tahmasb,b2_f,8,23,61
|
||||||
|
Tahmasb,b2_f,9,22,61
|
||||||
|
Tur,a2_f,0,6,39
|
||||||
|
Tur,a2_f,1,11,36
|
||||||
|
Tur,a2_f,2,9,35
|
||||||
|
Tur,a2_f,3,16,45
|
||||||
|
Tur,a2_f,4,19,52
|
||||||
|
Tur,a2_f,5,14,47
|
||||||
|
Tur,a2_f,6,21,46
|
||||||
|
Tus,b2_f,0,4,32
|
||||||
|
Tus,b2_f,1,9,39
|
||||||
|
Tus,b2_f,2,5,40
|
||||||
|
Tus,b2_f,3,15,42
|
||||||
|
Tus,b2_f,4,21,50
|
||||||
|
Tus,b2_f,5,16,52
|
||||||
|
Tus,b2_f,6,19,59
|
||||||
|
Tus,b2_f,7,28,57
|
||||||
|
Tus,b2_f,8,27,62
|
||||||
|
Tus,b2_f,9,27,60
|
||||||
|
Zal,a2_f,0,5,34
|
||||||
|
Zal,a2_f,1,12,36
|
||||||
|
Zal,a2_f,2,16,37
|
||||||
|
Zal,a2_f,3,18,40
|
||||||
|
Zal,a2_f,4,14,34
|
||||||
|
Zal,a2_f,5,15,41
|
||||||
|
Zal,a2_f,6,16,46
|
||||||
|
Zal,a2_f,7,15,44
|
||||||
|
Zal,a2_f,8,20,46
|
||||||
|
Zal,a2_f,9,18,50
|
||||||
|
Zav,a2_f,0,4,28
|
||||||
|
Zav,a2_f,1,7,37
|
||||||
|
Zav,a2_f,2,7,41
|
||||||
|
Zav,a2_f,3,4,32
|
||||||
|
Zav,a2_f,4,8,37
|
||||||
|
Zav,a2_f,5,4,37
|
||||||
|
Zav,a2_f,6,7,36
|
||||||
|
Zav,a2_f,7,15,56
|
||||||
|
Zav,a2_f,8,19,54
|
||||||
|
Zav,a2_f,9,21,49
|
||||||
|
@@ -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
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
function make_forouzan_data()
|
||||||
|
%MAKE_FOROUZAN_DATA Convert the previous (Forouzan) study to our long format.
|
||||||
|
% MAKE_FOROUZAN_DATA() calls FOROUZAN_LOAD_DATA and writes the curated long
|
||||||
|
% table to analysis/forouzan_reach_data.csv (same columns as our
|
||||||
|
% analysis/tdcs_reach_data.csv: subject, group, day, success, total), with
|
||||||
|
% group in {a2_f (Control), b2_f (Anodal)}. It then prints a descriptives
|
||||||
|
% summary and, as a conversion sanity-check, fits the paper's LME
|
||||||
|
% (behavior ~ stim + day + stim:day + (1|rat)) and compares the interaction
|
||||||
|
% to the paper's reported values (t(227)=2.68, F(1)=7.12, p=0.008, N=24).
|
||||||
|
|
||||||
|
thisDir = fileparts(mfilename('fullpath'));
|
||||||
|
T = forouzan_load_data();
|
||||||
|
|
||||||
|
outFile = fullfile(fileparts(thisDir), 'forouzan_reach_data.csv');
|
||||||
|
writetable(T, outFile);
|
||||||
|
fprintf('Wrote %s (%d rows)\n\n', outFile, height(T));
|
||||||
|
|
||||||
|
fprintf('%-6s %6s %10s %12s %10s %8s\n', ...
|
||||||
|
'group', 'nRats', 'nSessions', 'mean_success', 'mean_rate', 'max_day');
|
||||||
|
for g = categories(T.group)'
|
||||||
|
r = T.group == g{1};
|
||||||
|
fprintf('%-6s %6d %10d %12.1f %10.3f %8d\n', g{1}, ...
|
||||||
|
numel(unique(T.subject(r))), sum(r), mean(T.success(r)), ...
|
||||||
|
sum(T.success(r)) / sum(T.total(r)), max(T.day(r)));
|
||||||
|
end
|
||||||
|
|
||||||
|
% --- conversion sanity-check: the paper's own LME on the converted data ---
|
||||||
|
tbl = table(T.success, T.day, double(T.group == 'b2_f'), categorical(T.subject), ...
|
||||||
|
'VariableNames', {'behavior', 'day', 'stim', 'rat'});
|
||||||
|
m = fitlme(tbl, 'behavior ~ stim + day + stim:day + (1|rat)');
|
||||||
|
C = m.Coefficients; A = anova(m);
|
||||||
|
ii = strcmp(C.Name, 'day:stim'); ai = strcmp(A.Term, 'day:stim');
|
||||||
|
fprintf(['\nPaper-LME check (behavior ~ stim + day + stim:day + (1|rat), N=%d):\n' ...
|
||||||
|
' stim x day interaction: t(%d)=%.2f, F(1)=%.2f, p=%.4g\n' ...
|
||||||
|
' paper reported: t(227)=2.68, F(1)=7.12, p=0.008\n'], ...
|
||||||
|
numel(unique(tbl.rat)), C.DF(ii), C.tStat(ii), A.FStat(ai), C.pValue(ii));
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user