Skip to content

Commit 8c831f8

Browse files
committed
added non steady state detector
1 parent 5744d60 commit 8c831f8

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

nipype/algorithms/confounds.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,79 @@ def _list_outputs(self):
561561
outputs['detrended_file'] = op.abspath(self.inputs.detrended_file)
562562
return outputs
563563

564+
565+
class NonSteadyStateDetectorInputSpec(BaseInterfaceInputSpec):
566+
in_file = File(exists=True, mandatory=True, desc='4D NIFTI EPI file')
567+
568+
569+
class NonSteadyStateDetectorOutputSpec(TraitedSpec):
570+
n_volumes_to_discard = traits.Int(desc='Number of non-steady state volumes'
571+
'detected in the beginning of the scan.')
572+
573+
574+
class NonSteadyStateDetector(BaseInterface):
575+
"""
576+
Returns the number of non-steady state volumes detected at the beginning
577+
of the scan.
578+
"""
579+
580+
input_spec = NonSteadyStateDetectorInputSpec
581+
output_spec = NonSteadyStateDetectorOutputSpec
582+
583+
def _run_interface(self, runtime):
584+
in_nii = nb.load(self.inputs.in_plots)
585+
global_signal = in_nii.get_data()[:,:,:,:50].mean(axis=0).mean(axis=0).mean(axis=0)
586+
587+
self._results = {
588+
'out_file': _is_outlier(global_signal)
589+
}
590+
591+
return runtime
592+
593+
def _list_outputs(self):
594+
return self._results
595+
596+
def _is_outlier(points, thresh=3.5):
597+
"""
598+
Returns a boolean array with True if points are outliers and False
599+
otherwise.
600+
601+
Parameters:
602+
-----------
603+
points : An numobservations by numdimensions array of observations
604+
thresh : The modified z-score to use as a threshold. Observations with
605+
a modified z-score (based on the median absolute deviation) greater
606+
than this value will be classified as outliers.
607+
608+
Returns:
609+
--------
610+
mask : A numobservations-length boolean array.
611+
612+
References:
613+
----------
614+
Boris Iglewicz and David Hoaglin (1993), "Volume 16: How to Detect and
615+
Handle Outliers", The ASQC Basic References in Quality Control:
616+
Statistical Techniques, Edward F. Mykytka, Ph.D., Editor.
617+
"""
618+
if len(points.shape) == 1:
619+
points = points[:, None]
620+
median = np.median(points, axis=0)
621+
diff = np.sum((points - median) ** 2, axis=-1)
622+
diff = np.sqrt(diff)
623+
med_abs_deviation = np.median(diff)
624+
625+
modified_z_score = 0.6745 * diff / med_abs_deviation
626+
627+
timepoints_to_discard = 0
628+
for i in range(len(modified_z_score)):
629+
if modified_z_score[i] <= thresh:
630+
break
631+
else:
632+
timepoints_to_discard += 1
633+
634+
return timepoints_to_discard
635+
636+
564637
def regress_poly(degree, data, remove_mean=True, axis=-1):
565638
''' returns data with degree polynomial regressed out.
566639
Be default it is calculated along the last axis (usu. time).

nipype/algorithms/tests/test_confounds.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
import pytest
88
from nipype.testing import example_data
9-
from nipype.algorithms.confounds import FramewiseDisplacement, ComputeDVARS
9+
from nipype.algorithms.confounds import FramewiseDisplacement, ComputeDVARS, \
10+
_is_outlier
1011
import numpy as np
1112

1213

@@ -45,3 +46,10 @@ def test_dvars(tmpdir):
4546

4647
dv1 = np.loadtxt(res.outputs.out_std)
4748
assert (np.abs(dv1 - ground_truth).sum()/ len(dv1)) < 0.05
49+
50+
51+
def test_outliers(tmpdir):
52+
in_data = np.random.randn(100)
53+
in_data[0] += 10
54+
55+
assert _is_outlier(in_data) == 1

0 commit comments

Comments
 (0)