|
346 | 346 |
|
347 | 347 | end |
348 | 348 |
|
| 349 | + function obj = addConfoundsToDesignMatrix(obj, varargin) |
| 350 | + % |
| 351 | + % Add some typical confounds to the design matrix of bids stat model. |
| 352 | + % |
| 353 | + % This will update the design matrix of the root node of the model. |
| 354 | + % |
| 355 | + % Similar to the :func:`nilearn.interfaces.fmriprep.load_confounds` |
| 356 | + % |
| 357 | + % USAGE:: |
| 358 | + % |
| 359 | + % bm = bm.addConfoundsToDesignMatrix('strategy', strategy); |
| 360 | + % |
| 361 | + % |
| 362 | + % :param bm: bids stats model. |
| 363 | + % :type bm: :obj:`BidsModel` instance or path |
| 364 | + % to a ``_smdl.json`` file |
| 365 | + % |
| 366 | + % :type strategy: struct |
| 367 | + % :param strategy: structure describing the confoudd strategy. |
| 368 | + % |
| 369 | + % The structure must have the following field: |
| 370 | + % |
| 371 | + % - ``strategy``: cell array of char |
| 372 | + % with the strategies to apply. |
| 373 | + % |
| 374 | + % The structure may have the following field: |
| 375 | + % |
| 376 | + % - ``motion``: motion regressors strategy |
| 377 | + % - ``scrub``: scrubbing strategy |
| 378 | + % - ``wm_csf``: white matter |
| 379 | + % and cerebrospinal fluid regressors strategy |
| 380 | + % - ``non_steady_state``: |
| 381 | + % non steady state regressors strategy |
| 382 | + % |
| 383 | + % See the nilearn documentation (mentioned above) |
| 384 | + % for more information on the possible values |
| 385 | + % those strategies can take. |
| 386 | + % |
| 387 | + % :type updateName: logical |
| 388 | + % :param updateName: Append the name of the root node |
| 389 | + % with a string describing the counfounds added. |
| 390 | + % |
| 391 | + % ``rp-{motion}_scrub-{scrub}_tissue-{wm_csf}_nsso-{non_steady_state}`` |
| 392 | + % |
| 393 | + % default = ``false`` |
| 394 | + % |
| 395 | + % |
| 396 | + % :rtype: :obj:`BidsModel` instance |
| 397 | + % :return: bids stats model with the confounds added. |
| 398 | + % |
| 399 | + % EXAMPLE: |
| 400 | + % |
| 401 | + % .. code-block:: matlab |
| 402 | + % |
| 403 | + % |
| 404 | + % strategy.strategies = {'motion', 'wm_csf', 'scrub', 'non_steady_state'}; |
| 405 | + % strategy.motion = 'full'; |
| 406 | + % strategy.scrub = true; |
| 407 | + % strategy.non_steady_state = true; |
| 408 | + % |
| 409 | + % bm = bm.addConfoundsToDesignMatrix('strategy', strategy); |
| 410 | + % |
| 411 | + % |
| 412 | + |
| 413 | + % (C) Copyright 2023 bidspm developers |
| 414 | + |
| 415 | + args = inputParser; |
| 416 | + args.CaseSensitive = false; |
| 417 | + args.KeepUnmatched = false; |
| 418 | + args.FunctionName = 'addConfoundsToDesignMatrix'; |
| 419 | + |
| 420 | + addParameter(args, 'strategy', defaultStrategy(), @isstruct); |
| 421 | + addParameter(args, 'updateName', false, @islogical); |
| 422 | + |
| 423 | + parse(args, varargin{:}); |
| 424 | + |
| 425 | + strategy = args.Results.strategy; |
| 426 | + strategy = setFieldsStrategy(strategy); |
| 427 | + |
| 428 | + [~, name] = obj.get_root_node(); |
| 429 | + [~, idx] = obj.get_nodes('Name', name); |
| 430 | + designMatrix = obj.get_design_matrix('Name', name); |
| 431 | + |
| 432 | + strategiesToApply = strategy.strategies; |
| 433 | + for i = 1:numel(strategiesToApply) |
| 434 | + |
| 435 | + switch strategiesToApply{i} |
| 436 | + |
| 437 | + case 'motion' |
| 438 | + switch strategy.motion{1} |
| 439 | + case 'none' |
| 440 | + case 'basic' |
| 441 | + designMatrix{end + 1} = 'rot_?'; %#ok<*AGROW> |
| 442 | + designMatrix{end + 1} = 'trans_?'; |
| 443 | + case {'power2', 'derivatives' } |
| 444 | + notImplemented(mfilename(), ... |
| 445 | + sprintf('motion "%s" not implemented.', strategy.motion)); |
| 446 | + case 'full' |
| 447 | + designMatrix{end + 1} = 'rot_*'; |
| 448 | + designMatrix{end + 1} = 'trans_*'; |
| 449 | + end |
| 450 | + |
| 451 | + case 'non_steady_state' |
| 452 | + if strategy.non_steady_state{1} |
| 453 | + designMatrix{end + 1} = 'non_steady_state_outlier*'; |
| 454 | + end |
| 455 | + |
| 456 | + case 'scrub' |
| 457 | + if strategy.scrub{1} |
| 458 | + designMatrix{end + 1} = 'motion_outlier*'; |
| 459 | + end |
| 460 | + |
| 461 | + case 'wm_csf' |
| 462 | + switch strategy.wm_csf{1} |
| 463 | + case 'none' |
| 464 | + case 'basic' |
| 465 | + designMatrix{end + 1} = 'csf'; |
| 466 | + designMatrix{end + 1} = 'white'; |
| 467 | + case 'full' |
| 468 | + designMatrix{end + 1} = 'csf_*'; |
| 469 | + designMatrix{end + 1} = 'white_*'; |
| 470 | + otherwise |
| 471 | + notImplemented(mfilename(), ... |
| 472 | + sprintf('wm_csf "%s" not implemented.', strategiesToApply{i})); |
| 473 | + end |
| 474 | + |
| 475 | + case {'global_signal', 'compcorstr', 'n_compcorstr'} |
| 476 | + notImplemented(mfilename(), ... |
| 477 | + sprintf(['Strategey "%s" not implemented.\n', ... |
| 478 | + 'Supported strategies are:%s'], ... |
| 479 | + strategiesToApply{i}, ... |
| 480 | + bids.internal.create_unordered_list(supportedStrategies()))); |
| 481 | + otherwise |
| 482 | + logger('WARNING', sprintf('Unknown strategey: "%s".', ... |
| 483 | + strategiesToApply{i}), ... |
| 484 | + 'filename', mfilename(), ... |
| 485 | + 'id', 'unknownStrategy'); |
| 486 | + end |
| 487 | + end |
| 488 | + |
| 489 | + designMatrix = cleanDesignMatrix(designMatrix); |
| 490 | + |
| 491 | + obj.Nodes{idx}.Model.X = designMatrix; |
| 492 | + |
| 493 | + if args.Results.updateName |
| 494 | + obj.Nodes{idx}.Name = appendSuffixToNodeName(obj.Nodes{idx}.Name, strategy); |
| 495 | + end |
| 496 | + |
| 497 | + end |
| 498 | + |
349 | 499 | function validateConstrasts(obj) |
350 | 500 | % validate all contrasts spec in the model |
351 | 501 |
|
@@ -425,3 +575,71 @@ function bidsModelError(obj, id, msg) |
425 | 575 |
|
426 | 576 | end |
427 | 577 | end |
| 578 | + |
| 579 | +function name = appendSuffixToNodeName(name, strategy) |
| 580 | + if ~isempty(name) |
| 581 | + name = [name, '_']; |
| 582 | + end |
| 583 | + suffix = sprintf('rp-%s_scrub-%i_tissue-%s_nsso-%i', ... |
| 584 | + strategy.motion{1}, ... |
| 585 | + strategy.scrub{1}, ... |
| 586 | + strategy.wm_csf{1}, ... |
| 587 | + strategy.non_steady_state{1}); |
| 588 | + |
| 589 | + name = [name suffix]; |
| 590 | +end |
| 591 | + |
| 592 | +function value = supportedStrategies() |
| 593 | + value = {'motion', 'non_steady_state', 'wm_csf', 'scrub'}; |
| 594 | +end |
| 595 | + |
| 596 | +function value = defaultStrategy() |
| 597 | + value.strategies = {}; |
| 598 | + value.motion = 'none'; |
| 599 | + value.scrub = false; |
| 600 | + value.wm_csf = 'none'; |
| 601 | + value.non_steady_state = false; |
| 602 | +end |
| 603 | + |
| 604 | +function designMatrix = cleanDesignMatrix(designMatrix) |
| 605 | + % remove empty and duplicate |
| 606 | + toClean = cellfun(@(x) isempty(x), designMatrix); |
| 607 | + designMatrix(toClean) = []; |
| 608 | + |
| 609 | + if isempty(designMatrix) |
| 610 | + return |
| 611 | + end |
| 612 | + if size(designMatrix, 1) > 1 |
| 613 | + designMatrix = designMatrix'; |
| 614 | + end |
| 615 | + |
| 616 | + numeric = cellfun(@(x) isnumeric(x), designMatrix); |
| 617 | + tmp = unique(designMatrix(~numeric)); |
| 618 | + |
| 619 | + designMatrix = cat(2, tmp, designMatrix(numeric)); |
| 620 | +end |
| 621 | + |
| 622 | +function strategy = setFieldsStrategy(strategy) |
| 623 | + |
| 624 | + tmp = defaultStrategy(); |
| 625 | + |
| 626 | + strategies = fieldnames(defaultStrategy()); |
| 627 | + for i = 1:numel(strategies) |
| 628 | + |
| 629 | + if ~isfield(strategy, strategies{i}) |
| 630 | + strategy.(strategies{i}) = tmp.(strategies{i}); |
| 631 | + end |
| 632 | + |
| 633 | + if ~iscell(strategy.(strategies{i})) |
| 634 | + strategy.(strategies{i}) = {strategy.(strategies{i})}; |
| 635 | + end |
| 636 | + |
| 637 | + if ~isempty(strategy.(strategies{i})) && ... |
| 638 | + isnumeric(strategy.(strategies{i}){1}) && ... |
| 639 | + isnan(strategy.(strategies{i}){1}) |
| 640 | + strategy.(strategies{i}){1} = tmp.(strategies{i}); |
| 641 | + end |
| 642 | + |
| 643 | + end |
| 644 | + |
| 645 | +end |
0 commit comments