diff --git a/analysis/matlab/README.md b/analysis/matlab/README.md index c0290da..7d90a23 100644 --- a/analysis/matlab/README.md +++ b/analysis/matlab/README.md @@ -155,3 +155,19 @@ matlab -batch "tdcs_glm('paper_mergeA2')" # or paper_unmerged / paper_mergeB2 also warns that the full-range interaction is confounded by unequal day coverage (Box-A2 ends ~day 13) — see the _d0_13 and phased analyses. The Monte-Carlo power analysis (`run_power`, `tdcs_power_sim`) uses this same `behavior ~ stim + day + stim:day + (1|rat)` model. + +## mergeNaive: pooled control vs tDCS + +`mergeNaive` pools Naive into the control side (control = Box-A2 + Naive, n=7) and +Right-Electrode into the tDCS side (tDCS = Box-B2 + Right-Electrode, n=4); Box-A is left +out of the anchor contrast. Run the GLMMs, the paper LME, and power: +``` +matlab -batch "tdcs_glm('mergeNaive_full')" # GLMM (rate/count) tDCS vs control +matlab -batch "tdcs_glm('paper_mergeNaive')" # paper LME: behavior ~ stim+day+stim:day+(1|rat) +matlab -batch "tdcs_power_sim('mergeNaive',[0 5])" +``` +With the larger pooled control this is the closest replication of the paper's full pattern: +a significant days x tDCS interaction (F(1)=7.30, p=0.008 vs paper 7.12/0.008), an equal-on-Day-1 +stim main effect (p=0.21, matching the paper's n.s.), and a strong day effect. The overall tDCS +advantage is significant even at the cluster-honest per-animal level (rate p=0.003). Caveat: +pooling Naive (no-stimulation control) with Box-A2 (an electrode condition) is a modeling choice. diff --git a/analysis/matlab/run_all.m b/analysis/matlab/run_all.m index 5441a4f..1f701d8 100644 --- a/analysis/matlab/run_all.m +++ b/analysis/matlab/run_all.m @@ -10,7 +10,8 @@ scenarios = {'unmerged_full', 'unmerged_d0_10', ... 'lme_mergeB2_full', 'lme_mergeB2_d0_10', ... 'lme_unmerged_d0_13', 'lme_mergeA2_d0_13', 'lme_mergeB2_d0_13', ... 'phase_unmerged', 'phase_mergeA2', 'phase_mergeB2', ... - 'paper_unmerged', 'paper_mergeA2', 'paper_mergeB2'}; + 'paper_unmerged', 'paper_mergeA2', 'paper_mergeB2', ... + 'mergeNaive_full', 'paper_mergeNaive', 'phase_mergeNaive'}; for i = 1:numel(scenarios) fprintf('\n\n### Running scenario: %s ###\n', scenarios{i}); diff --git a/analysis/matlab/tdcs_config.m b/analysis/matlab/tdcs_config.m index 92b88f9..0fefd28 100644 --- a/analysis/matlab/tdcs_config.m +++ b/analysis/matlab/tdcs_config.m @@ -54,4 +54,11 @@ cfg.mergeB2 = containers.Map( ... {'Electrode-Box-A', 'Right-Electrode'}, ... {'Electrode-Box-B2', 'Electrode-Box-B2'}); +% mergeNaive: pool Naive into the Box-A2 (control) side and Right-Electrode +% into the Box-B2 (tDCS) side. Control = Box-A2 + Naive (n=7), tDCS = Box-B2 + +% Right-Electrode (n=4); Electrode-Box-A is left unchanged. +cfg.mergeNaive = containers.Map( ... + {'Naive', 'Right-Electrode'}, ... + {'Electrode-Box-A2', 'Electrode-Box-B2'}); + end diff --git a/analysis/matlab/tdcs_glm.m b/analysis/matlab/tdcs_glm.m index eb39bda..4c428df 100644 --- a/analysis/matlab/tdcs_glm.m +++ b/analysis/matlab/tdcs_glm.m @@ -60,6 +60,22 @@ switch scenario R = tdcs_models(S, meta, cfg); tdcs_report(S, R, meta, cfg, 'mergeB2_d0_10'); + % --- mergeNaive: control = Box-A2 + Naive, tDCS = Box-B2 + Right-Electrode --- + case 'mergeNaive_full' + [S, meta] = tdcs_scenario_data('mergeNaive_full'); + R = tdcs_models(S, meta, cfg); + tdcs_report(S, R, meta, cfg, 'mergeNaive_full'); + + case 'mergeNaive_d0_10' + [S, meta] = tdcs_scenario_data('mergeNaive_d0_10'); + R = tdcs_models(S, meta, cfg); + tdcs_report(S, R, meta, cfg, 'mergeNaive_d0_10'); + + case 'mergeNaive_d0_13' + [S, meta] = tdcs_scenario_data('mergeNaive_d0_13'); + R = tdcs_models(S, meta, cfg); + tdcs_report(S, R, meta, cfg, 'mergeNaive_d0_13'); + % --- Linear "days x tDCS" mixed model (Box-B2 vs Box-A2), per the paper --- case 'lme_unmerged_full' L = tdcs_lme(tdcs_scenario_data('unmerged_full'), cfg); @@ -117,6 +133,12 @@ switch scenario case 'paper_mergeB2' tdcs_paper_lme('mergeB2', cfg); + case 'paper_mergeNaive' + tdcs_paper_lme('mergeNaive', cfg); + + case 'phase_mergeNaive' + tdcs_phase_lme('mergeNaive', cfg); + otherwise error('tdcs_glm:badScenario', ... ['Unrecognized scenario "%s". Valid scenarios are: ' ... diff --git a/analysis/matlab/tdcs_scenario_data.m b/analysis/matlab/tdcs_scenario_data.m index 6daea18..4cd4ff5 100644 --- a/analysis/matlab/tdcs_scenario_data.m +++ b/analysis/matlab/tdcs_scenario_data.m @@ -41,6 +41,8 @@ switch mergeKey mergeMap = cfg.mergeA2; case 'mergeB2' mergeMap = cfg.mergeB2; + case 'mergeNaive' + mergeMap = cfg.mergeNaive; otherwise error('tdcs_scenario_data:badScenario', ... 'Unrecognized scenario "%s".', scenario); @@ -108,7 +110,7 @@ else 'Scenario "%s" does not end in "_full", "_d0_10", or "_d0_13".', scenario); end -if ~ismember(mergeKey, {'unmerged', 'mergeA2', 'mergeB2'}) +if ~ismember(mergeKey, {'unmerged', 'mergeA2', 'mergeB2', 'mergeNaive'}) error('tdcs_scenario_data:badScenario', ... 'Scenario "%s" has unrecognized merge key "%s".', scenario, mergeKey); end diff --git a/analysis/matlab/tests/tLme.m b/analysis/matlab/tests/tLme.m index 4c671fd..cc2db21 100644 --- a/analysis/matlab/tests/tLme.m +++ b/analysis/matlab/tests/tLme.m @@ -59,6 +59,18 @@ classdef tLme < matlab.unittest.TestCase testCase.verifyError(@() tdcs_glm('lme_nonsense'), 'tdcs_glm:badScenario'); end + function testMergeNaiveGrouping(testCase) + % mergeNaive: control = Box-A2 + Naive (7), tDCS = Box-B2 + Right (4), + % Box-A unchanged (1); Naive is relabeled away. + [S, ~] = tdcs_scenario_data('mergeNaive_full'); + sg = unique(S(:, {'subject','group'})); + n = @(g) sum(sg.group == g); + testCase.verifyEqual(n('Electrode-Box-A2'), 7); + testCase.verifyEqual(n('Electrode-Box-B2'), 4); + testCase.verifyEqual(n('Electrode-Box-A'), 1); + testCase.verifyFalse(any(sg.group == 'Naive')); + end + function testPaperFormulaReplicatesInteraction(testCase) % The paper's exact formula (behavior ~ stim + day + stim:day + % (1|rat)) on mergeA2 reproduces the paper's interaction