matlab(tdcs): data loading + curated group map
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
function T = tdcs_load_data()
|
||||
%TDCS_LOAD_DATA Load, join, and curate the raw tDCS reaching matrices.
|
||||
% T = TDCS_LOAD_DATA() reads analysis/matlab/raw/raw_success.csv and
|
||||
% raw_total.csv (app-exported matrix format: metric header line, blank
|
||||
% line, file "Group" line, "# Days Reach" subject-header line, then one
|
||||
% row per day with one value per subject column), reshapes each to long
|
||||
% form, joins them on (subject, day), applies the curated 12-animal
|
||||
% group map from tdcs_config (the file's own Group row is ignored),
|
||||
% and restricts to day >= 0.
|
||||
%
|
||||
% Returns a table with variables:
|
||||
% subject (string) animal name
|
||||
% group (categorical) curated group (Naive, Electrode-Box-A,
|
||||
% Electrode-Box-A2, Electrode-Box-B2,
|
||||
% Right-Electrode)
|
||||
% day (double) "# Days Reach" value, >= 0
|
||||
% success (double) successful reaches in the session
|
||||
% total (double) total attempts in the session
|
||||
%
|
||||
% Only the 12 curated animals (per tdcs_config().groups) are retained;
|
||||
% any other animal present in the raw export is dropped.
|
||||
|
||||
thisDir = fileparts(mfilename('fullpath'));
|
||||
successFile = fullfile(thisDir, 'raw', 'raw_success.csv');
|
||||
totalFile = fullfile(thisDir, 'raw', 'raw_total.csv');
|
||||
|
||||
Tsuccess = localReadMatrix(successFile, 'success');
|
||||
Ttotal = localReadMatrix(totalFile, 'total');
|
||||
|
||||
% Left join keyed on (subject, day): every non-blank Success cell becomes
|
||||
% a row; Total is looked up for the same (subject, day). Per the plan's
|
||||
% Global Constraints, Total is fully populated wherever Success is, so
|
||||
% this should never introduce a missing total — but the join is a left
|
||||
% join (not an inner join) specifically so that an unmatched Success row
|
||||
% would still be kept (with total = NaN) rather than silently dropped.
|
||||
T = outerjoin(Tsuccess, Ttotal, 'Keys', {'subject', 'day'}, ...
|
||||
'MergeKeys', true, 'Type', 'left');
|
||||
|
||||
% Apply the curated map: keep only the 12 curated animals, and assign
|
||||
% group from the map (overriding the file's own Group row).
|
||||
cfg = tdcs_config();
|
||||
subjectsCell = cellstr(T.subject);
|
||||
keep = isKey(cfg.groups, subjectsCell);
|
||||
T = T(keep, :);
|
||||
subjectsCell = subjectsCell(keep);
|
||||
|
||||
groupVals = cell(height(T), 1);
|
||||
for i = 1:height(T)
|
||||
groupVals{i} = cfg.groups(subjectsCell{i});
|
||||
end
|
||||
T.group = categorical(groupVals);
|
||||
|
||||
% Restrict to day >= 0 (analysis window).
|
||||
T = T(T.day >= 0, :);
|
||||
|
||||
% Final variable order and a deterministic row order.
|
||||
T = T(:, {'subject', 'group', 'day', 'success', 'total'});
|
||||
T = sortrows(T, {'subject', 'day'});
|
||||
|
||||
end
|
||||
|
||||
function Tlong = localReadMatrix(filePath, valueName)
|
||||
%LOCALREADMATRIX Parse one app-exported day-by-subject matrix to long form.
|
||||
% Line 1 = "Data,<metric>" (skipped), line 2 blank (skipped), line 3 =
|
||||
% "Group,<...>" (skipped -- grouping comes from the curated map, not
|
||||
% this row), line 4 = "# Days Reach,<subject headers>" (columns 2:end
|
||||
% are the subject names), lines 5+ = "<day>,<value per subject>" with
|
||||
% blank cells meaning "no session that day for that subject".
|
||||
|
||||
lines = readlines(filePath);
|
||||
% Defensive: drop any wholly-blank trailing line (e.g. if the file ends
|
||||
% with a newline). Real data rows always start with a day number and are
|
||||
% never zero-length.
|
||||
while numel(lines) > 0 && strlength(strip(lines(end))) == 0
|
||||
lines(end) = [];
|
||||
end
|
||||
|
||||
headerParts = strsplit(lines(4), ',', 'CollapseDelimiters', false);
|
||||
subjects = string(headerParts(2:end));
|
||||
nSubjects = numel(subjects);
|
||||
|
||||
dataLines = lines(5:end);
|
||||
dataLines = dataLines(strlength(strip(dataLines)) > 0);
|
||||
nRows = numel(dataLines);
|
||||
|
||||
day = nan(nRows * nSubjects, 1);
|
||||
subjectCol = strings(nRows * nSubjects, 1);
|
||||
value = nan(nRows * nSubjects, 1);
|
||||
|
||||
idx = 0;
|
||||
for r = 1:nRows
|
||||
parts = strsplit(dataLines(r), ',', 'CollapseDelimiters', false);
|
||||
dayVal = str2double(parts(1));
|
||||
for c = 1:nSubjects
|
||||
col = c + 1;
|
||||
if col <= numel(parts)
|
||||
cellStr = strtrim(parts(col));
|
||||
else
|
||||
cellStr = "";
|
||||
end
|
||||
if strlength(cellStr) == 0
|
||||
continue
|
||||
end
|
||||
idx = idx + 1;
|
||||
day(idx) = dayVal;
|
||||
subjectCol(idx) = subjects(c);
|
||||
value(idx) = str2double(cellStr);
|
||||
end
|
||||
end
|
||||
|
||||
day = day(1:idx);
|
||||
subjectCol = subjectCol(1:idx);
|
||||
value = value(1:idx);
|
||||
|
||||
Tlong = table(subjectCol, day, value, 'VariableNames', {'subject', 'day', valueName});
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user