rapidtide 3.0.11__py3-none-any.whl → 3.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. rapidtide/Colortables.py +492 -27
  2. rapidtide/OrthoImageItem.py +1049 -46
  3. rapidtide/RapidtideDataset.py +1533 -86
  4. rapidtide/_version.py +3 -3
  5. rapidtide/calccoherence.py +196 -29
  6. rapidtide/calcnullsimfunc.py +188 -40
  7. rapidtide/calcsimfunc.py +242 -42
  8. rapidtide/correlate.py +1203 -383
  9. rapidtide/data/examples/src/testLD +56 -0
  10. rapidtide/data/examples/src/testalign +1 -1
  11. rapidtide/data/examples/src/testdelayvar +0 -1
  12. rapidtide/data/examples/src/testfmri +53 -3
  13. rapidtide/data/examples/src/testglmfilt +5 -5
  14. rapidtide/data/examples/src/testhappy +29 -7
  15. rapidtide/data/examples/src/testppgproc +17 -0
  16. rapidtide/data/examples/src/testrolloff +11 -0
  17. rapidtide/data/models/model_cnn_pytorch/best_model.pth +0 -0
  18. rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
  19. rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
  20. rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
  21. rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
  22. rapidtide/decorators.py +91 -0
  23. rapidtide/dlfilter.py +2226 -110
  24. rapidtide/dlfiltertorch.py +4842 -0
  25. rapidtide/externaltools.py +327 -12
  26. rapidtide/fMRIData_class.py +79 -40
  27. rapidtide/filter.py +1899 -810
  28. rapidtide/fit.py +2011 -581
  29. rapidtide/genericmultiproc.py +93 -18
  30. rapidtide/happy_supportfuncs.py +2047 -172
  31. rapidtide/helper_classes.py +584 -43
  32. rapidtide/io.py +2370 -372
  33. rapidtide/linfitfiltpass.py +346 -99
  34. rapidtide/makelaggedtcs.py +210 -24
  35. rapidtide/maskutil.py +448 -62
  36. rapidtide/miscmath.py +827 -121
  37. rapidtide/multiproc.py +210 -22
  38. rapidtide/patchmatch.py +242 -42
  39. rapidtide/peakeval.py +31 -31
  40. rapidtide/ppgproc.py +2203 -0
  41. rapidtide/qualitycheck.py +352 -39
  42. rapidtide/refinedelay.py +431 -57
  43. rapidtide/refineregressor.py +494 -189
  44. rapidtide/resample.py +671 -185
  45. rapidtide/scripts/applyppgproc.py +28 -0
  46. rapidtide/scripts/showxcorr_legacy.py +7 -7
  47. rapidtide/scripts/stupidramtricks.py +15 -17
  48. rapidtide/simFuncClasses.py +1052 -77
  49. rapidtide/simfuncfit.py +269 -69
  50. rapidtide/stats.py +540 -238
  51. rapidtide/tests/happycomp +9 -0
  52. rapidtide/tests/test_cleanregressor.py +1 -2
  53. rapidtide/tests/test_dlfiltertorch.py +627 -0
  54. rapidtide/tests/test_findmaxlag.py +24 -8
  55. rapidtide/tests/test_fullrunhappy_v1.py +0 -2
  56. rapidtide/tests/test_fullrunhappy_v2.py +0 -2
  57. rapidtide/tests/test_fullrunhappy_v3.py +11 -4
  58. rapidtide/tests/test_fullrunhappy_v4.py +10 -2
  59. rapidtide/tests/test_fullrunrapidtide_v7.py +1 -1
  60. rapidtide/tests/test_getparsers.py +11 -3
  61. rapidtide/tests/test_refinedelay.py +0 -1
  62. rapidtide/tests/test_simroundtrip.py +16 -8
  63. rapidtide/tests/test_stcorrelate.py +3 -1
  64. rapidtide/tests/utils.py +9 -8
  65. rapidtide/tidepoolTemplate.py +142 -38
  66. rapidtide/tidepoolTemplate_alt.py +165 -44
  67. rapidtide/tidepoolTemplate_big.py +189 -52
  68. rapidtide/util.py +1217 -118
  69. rapidtide/voxelData.py +684 -37
  70. rapidtide/wiener.py +136 -23
  71. rapidtide/wiener2.py +113 -7
  72. rapidtide/workflows/adjustoffset.py +105 -3
  73. rapidtide/workflows/aligntcs.py +85 -2
  74. rapidtide/workflows/applydlfilter.py +87 -10
  75. rapidtide/workflows/applyppgproc.py +540 -0
  76. rapidtide/workflows/atlasaverage.py +210 -47
  77. rapidtide/workflows/atlastool.py +100 -3
  78. rapidtide/workflows/calcSimFuncMap.py +288 -69
  79. rapidtide/workflows/calctexticc.py +201 -9
  80. rapidtide/workflows/ccorrica.py +101 -6
  81. rapidtide/workflows/cleanregressor.py +165 -31
  82. rapidtide/workflows/delayvar.py +171 -23
  83. rapidtide/workflows/diffrois.py +81 -3
  84. rapidtide/workflows/endtidalproc.py +144 -4
  85. rapidtide/workflows/fdica.py +195 -15
  86. rapidtide/workflows/filtnifti.py +70 -3
  87. rapidtide/workflows/filttc.py +74 -3
  88. rapidtide/workflows/fitSimFuncMap.py +202 -51
  89. rapidtide/workflows/fixtr.py +73 -3
  90. rapidtide/workflows/gmscalc.py +113 -3
  91. rapidtide/workflows/happy.py +801 -199
  92. rapidtide/workflows/happy2std.py +144 -12
  93. rapidtide/workflows/happy_parser.py +163 -23
  94. rapidtide/workflows/histnifti.py +118 -2
  95. rapidtide/workflows/histtc.py +84 -3
  96. rapidtide/workflows/linfitfilt.py +117 -4
  97. rapidtide/workflows/localflow.py +328 -28
  98. rapidtide/workflows/mergequality.py +79 -3
  99. rapidtide/workflows/niftidecomp.py +322 -18
  100. rapidtide/workflows/niftistats.py +174 -4
  101. rapidtide/workflows/pairproc.py +98 -4
  102. rapidtide/workflows/pairwisemergenifti.py +85 -2
  103. rapidtide/workflows/parser_funcs.py +1421 -40
  104. rapidtide/workflows/physiofreq.py +137 -11
  105. rapidtide/workflows/pixelcomp.py +207 -5
  106. rapidtide/workflows/plethquality.py +103 -21
  107. rapidtide/workflows/polyfitim.py +151 -11
  108. rapidtide/workflows/proj2flow.py +75 -2
  109. rapidtide/workflows/rankimage.py +111 -4
  110. rapidtide/workflows/rapidtide.py +368 -76
  111. rapidtide/workflows/rapidtide2std.py +98 -2
  112. rapidtide/workflows/rapidtide_parser.py +109 -9
  113. rapidtide/workflows/refineDelayMap.py +144 -33
  114. rapidtide/workflows/refineRegressor.py +675 -96
  115. rapidtide/workflows/regressfrommaps.py +161 -37
  116. rapidtide/workflows/resamplenifti.py +85 -3
  117. rapidtide/workflows/resampletc.py +91 -3
  118. rapidtide/workflows/retrolagtcs.py +99 -9
  119. rapidtide/workflows/retroregress.py +176 -26
  120. rapidtide/workflows/roisummarize.py +174 -5
  121. rapidtide/workflows/runqualitycheck.py +71 -3
  122. rapidtide/workflows/showarbcorr.py +149 -6
  123. rapidtide/workflows/showhist.py +86 -2
  124. rapidtide/workflows/showstxcorr.py +160 -3
  125. rapidtide/workflows/showtc.py +159 -3
  126. rapidtide/workflows/showxcorrx.py +190 -10
  127. rapidtide/workflows/showxy.py +185 -15
  128. rapidtide/workflows/simdata.py +264 -38
  129. rapidtide/workflows/spatialfit.py +77 -2
  130. rapidtide/workflows/spatialmi.py +250 -27
  131. rapidtide/workflows/spectrogram.py +305 -32
  132. rapidtide/workflows/synthASL.py +154 -3
  133. rapidtide/workflows/tcfrom2col.py +76 -2
  134. rapidtide/workflows/tcfrom3col.py +74 -2
  135. rapidtide/workflows/tidepool.py +2971 -130
  136. rapidtide/workflows/utils.py +19 -14
  137. rapidtide/workflows/utils_doc.py +293 -0
  138. rapidtide/workflows/variabilityizer.py +116 -3
  139. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/METADATA +10 -8
  140. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/RECORD +144 -128
  141. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/entry_points.txt +1 -0
  142. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/WHEEL +0 -0
  143. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/licenses/LICENSE +0 -0
  144. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,10 @@
16
16
  # limitations under the License.
17
17
  #
18
18
  #
19
+ from typing import Any
20
+
19
21
  import numpy as np
22
+ from numpy.typing import NDArray
20
23
  from scipy.special import factorial
21
24
  from tqdm import tqdm
22
25
 
@@ -27,38 +30,89 @@ import rapidtide.multiproc as tide_multiproc
27
30
 
28
31
 
29
32
  def _procOneRegressionFitItem(
30
- vox, theevs, thedata, rt_floatset=np.float64, rt_floattype="float64"
31
- ):
33
+ vox: int,
34
+ theevs: NDArray,
35
+ thedata: NDArray,
36
+ rt_floattype: np.dtype = np.float64,
37
+ ) -> tuple[int, float, float, float, float | NDArray, Any, NDArray, NDArray]:
38
+ """
39
+ Perform single regression fit on voxel data and return fit results.
40
+
41
+ This function fits a linear regression model to the provided evs and data,
42
+ handling both univariate and multivariate cases. It computes fit coefficients,
43
+ R-squared value, and residual data.
44
+
45
+ Parameters
46
+ ----------
47
+ vox : int
48
+ Voxel index.
49
+ theevs : NDArray
50
+ Experimental design matrix. If 2D, dimension 0 is number of points,
51
+ dimension 1 is number of evs.
52
+ thedata : NDArray
53
+ Dependent variable data corresponding to the evs.
54
+ rt_floattype : str, optional
55
+ String representation of the floating-point type, default is ``np.float64``.
56
+
57
+ Returns
58
+ -------
59
+ tuple[int, float, float, float, float, Any, NDArray, NDArray]
60
+ A tuple containing:
61
+ - voxel index (`int`)
62
+ - intercept term (`float`)
63
+ - signed square root of R-squared (`float`)
64
+ - R-squared value (`float`)
65
+ - fit coefficients (`float` or `NDArray`)
66
+ - normalized fit coefficients (`Any`)
67
+ - data removed by fitting (`NDArray`)
68
+ - residuals (`NDArray`)
69
+
70
+ Notes
71
+ -----
72
+ For multivariate regressions (2D `theevs`), the function computes the fit
73
+ using `tide_fit.mlregress`. If the fit fails, a zero matrix is returned.
74
+ For univariate regressions (1D `theevs`), the function directly computes
75
+ the fit and handles edge cases such as zero coefficients.
76
+
77
+ Examples
78
+ --------
79
+ >>> import numpy as np
80
+ >>> theevs = np.array([[1, 2], [3, 4], [5, 6]], dtype=np.float64)
81
+ >>> thedata = np.array([1, 2, 3], dtype=np.float64)
82
+ >>> result = _procOneRegressionFitItem(0, theevs, thedata)
83
+ >>> print(result[0]) # voxel index
84
+ 0
85
+ """
32
86
  # NOTE: if theevs is 2D, dimension 0 is number of points, dimension 1 is number of evs
33
87
  thefit, R2 = tide_fit.mlregress(theevs, thedata)
34
88
  if theevs.ndim > 1:
35
89
  if thefit is None:
36
90
  thefit = np.matrix(np.zeros((1, theevs.shape[1] + 1), dtype=rt_floattype))
37
- fitcoeffs = rt_floatset(thefit[0, 1:])
91
+ fitcoeffs = (thefit[0, 1:]).astype(rt_floattype)
38
92
  if fitcoeffs[0, 0] < 0.0:
39
93
  coeffsign = -1.0
40
94
  else:
41
95
  coeffsign = 1.0
42
96
  datatoremove = theevs[:, 0] * 0.0
43
97
  for j in range(theevs.shape[1]):
44
- datatoremove += rt_floatset(rt_floatset(thefit[0, 1 + j]) * theevs[:, j])
98
+ datatoremove += (thefit[0, 1 + j] * theevs[:, j]).astype(rt_floattype)
45
99
  if np.any(fitcoeffs) != 0.0:
46
100
  pass
47
101
  else:
48
102
  R2 = 0.0
49
103
  return (
50
104
  vox,
51
- rt_floatset(thefit[0, 0]),
52
- rt_floatset(coeffsign * np.sqrt(R2)),
53
- rt_floatset(R2),
105
+ thefit[0, 0],
106
+ coeffsign * np.sqrt(R2),
107
+ R2,
54
108
  fitcoeffs,
55
- rt_floatset(thefit[0, 1:] / thefit[0, 0]),
109
+ (thefit[0, 1:] / thefit[0, 0]).astype(rt_floattype),
56
110
  datatoremove,
57
- rt_floatset(thedata - datatoremove),
111
+ (thedata - datatoremove).astype(rt_floattype),
58
112
  )
59
113
  else:
60
- fitcoeff = rt_floatset(thefit[0, 1])
61
- datatoremove = rt_floatset(fitcoeff * theevs)
114
+ fitcoeff = (thefit[0, 1]).astype(rt_floattype)
115
+ datatoremove = (fitcoeff * theevs).astype(rt_floattype)
62
116
  if fitcoeff < 0.0:
63
117
  coeffsign = -1.0
64
118
  else:
@@ -67,47 +121,140 @@ def _procOneRegressionFitItem(
67
121
  R2 = 0.0
68
122
  return (
69
123
  vox,
70
- rt_floatset(thefit[0, 0]),
71
- rt_floatset(coeffsign * np.sqrt(R2)),
72
- rt_floatset(R2),
124
+ thefit[0, 0],
125
+ coeffsign * np.sqrt(R2),
126
+ R2,
73
127
  fitcoeff,
74
- rt_floatset(thefit[0, 1] / thefit[0, 0]),
128
+ (thefit[0, 1] / thefit[0, 0]).astype(rt_floattype),
75
129
  datatoremove,
76
- rt_floatset(thedata - datatoremove),
130
+ (thedata - datatoremove).astype(rt_floattype),
77
131
  )
78
132
 
79
133
 
80
134
  def linfitfiltpass(
81
- numprocitems,
82
- fmri_data,
83
- threshval,
84
- theevs,
85
- meanvalue,
86
- rvalue,
87
- r2value,
88
- fitcoeff,
89
- fitNorm,
90
- datatoremove,
91
- filtereddata,
92
- nprocs=1,
93
- alwaysmultiproc=False,
94
- voxelspecific=True,
95
- confoundregress=False,
96
- coefficientsonly=False,
97
- procbyvoxel=True,
98
- showprogressbar=True,
99
- chunksize=1000,
100
- rt_floatset=np.float64,
101
- rt_floattype="float64",
102
- verbose=True,
103
- debug=False,
104
- ):
135
+ numprocitems: int,
136
+ fmri_data: NDArray,
137
+ threshval: float | None,
138
+ theevs: NDArray,
139
+ meanvalue: NDArray | None,
140
+ rvalue: NDArray | None,
141
+ r2value: NDArray,
142
+ fitcoeff: NDArray | None,
143
+ fitNorm: NDArray | None,
144
+ datatoremove: NDArray | None,
145
+ filtereddata: NDArray | None,
146
+ nprocs: int = 1,
147
+ alwaysmultiproc: bool = False,
148
+ constantevs: bool = False,
149
+ confoundregress: bool = False,
150
+ coefficientsonly: bool = False,
151
+ procbyvoxel: bool = True,
152
+ showprogressbar: bool = True,
153
+ chunksize: int = 1000,
154
+ rt_floattype: np.dtype = np.float64,
155
+ verbose: bool = True,
156
+ debug: bool = False,
157
+ ) -> int:
158
+ """
159
+ Perform linear regression fitting and filtering on fMRI data.
160
+
161
+ This function fits a linear model to fMRI data using specified experimental variables
162
+ and applies filtering to remove noise. It supports both voxel-wise and timepoint-wise
163
+ processing, with optional multiprocessing for performance.
164
+
165
+ Parameters
166
+ ----------
167
+ numprocitems : int
168
+ Number of items to process (voxels or timepoints depending on ``procbyvoxel``).
169
+ fmri_data : ndarray
170
+ Input fMRI data array with shape ``(n_voxels, n_timepoints)`` or ``(n_timepoints, n_voxels)``.
171
+ threshval : float, optional
172
+ Threshold value for masking. If ``None``, no masking is applied.
173
+ theevs : ndarray
174
+ Experimental variables (design matrix) with shape ``(n_voxels, n_timepoints)`` or ``(n_timepoints, n_voxels)``.
175
+ meanvalue : ndarray, optional
176
+ Array to store mean values of the data. Shape depends on ``procbyvoxel``.
177
+ rvalue : ndarray, optional
178
+ Array to store correlation coefficients. Shape depends on ``procbyvoxel``.
179
+ r2value : ndarray
180
+ Array to store R-squared values. Shape depends on ``procbyvoxel``.
181
+ fitcoeff : ndarray, optional
182
+ Array to store fit coefficients. Shape depends on ``procbyvoxel`` and ``constantevs``.
183
+ fitNorm : ndarray, optional
184
+ Array to store normalized fit coefficients. Shape depends on ``procbyvoxel``.
185
+ datatoremove : ndarray, optional
186
+ Array to store data to be removed after fitting. Shape depends on ``procbyvoxel``.
187
+ filtereddata : ndarray
188
+ Array to store filtered data after regression. Shape depends on ``procbyvoxel``.
189
+ nprocs : int, default: 1
190
+ Number of processes to use for multiprocessing. If 1 and ``alwaysmultiproc`` is False, uses single-threaded processing.
191
+ alwaysmultiproc : bool, default: False
192
+ If True, always use multiprocessing even if ``nprocs`` is 1.
193
+ constantevs : bool, default: False
194
+ If True, treat experimental variables as constant across voxels/timepoints.
195
+ confoundregress : bool, default: False
196
+ If True, perform confound regression only (no output of coefficients or residuals).
197
+ coefficientsonly : bool, default: False
198
+ If True, store only regression coefficients and R-squared values.
199
+ procbyvoxel : bool, default: True
200
+ If True, process data voxel-wise; otherwise, process by timepoint.
201
+ showprogressbar : bool, default: True
202
+ If True, display a progress bar during processing.
203
+ chunksize : int, default: 1000
204
+ Size of chunks for multiprocessing.
205
+ rt_floattype : str, default: np.float64
206
+ Data type for internal floating-point calculations.
207
+ verbose : bool, default: True
208
+ If True, print verbose output.
209
+ debug : bool, default: False
210
+ If True, enable debug printing.
211
+
212
+ Returns
213
+ -------
214
+ int
215
+ Total number of items processed.
216
+
217
+ Notes
218
+ -----
219
+ - The function modifies the output arrays in-place.
220
+ - For ``confoundregress=True``, only ``r2value`` and ``filtereddata`` are populated.
221
+ - When ``coefficientsonly=True``, only ``meanvalue``, ``rvalue``, ``r2value``, ``fitcoeff``, and ``fitNorm`` are populated.
222
+ - If ``threshval`` is provided, a mask is generated based on mean or standard deviation of the data.
223
+
224
+ Examples
225
+ --------
226
+ >>> import numpy as np
227
+ >>> from typing import NDArray
228
+ >>> fmri_data = np.random.rand(100, 200)
229
+ >>> theevs = np.random.rand(100, 200)
230
+ >>> r2value = np.zeros(100)
231
+ >>> filtereddata = np.zeros_like(fmri_data)
232
+ >>> numprocitems = 100
233
+ >>> items_processed = linfitfiltpass(
234
+ ... numprocitems=numprocitems,
235
+ ... fmri_data=fmri_data,
236
+ ... threshval=None,
237
+ ... theevs=theevs,
238
+ ... meanvalue=None,
239
+ ... rvalue=None,
240
+ ... r2value=r2value,
241
+ ... fitcoeff=None,
242
+ ... fitNorm=None,
243
+ ... datatoremove=None,
244
+ ... filtereddata=filtereddata,
245
+ ... nprocs=4,
246
+ ... procbyvoxel=True,
247
+ ... showprogressbar=True
248
+ ... )
249
+ >>> print(f"Processed {items_processed} items.")
250
+ """
105
251
  inputshape = np.shape(fmri_data)
106
252
  if debug:
107
253
  print(f"{numprocitems=}")
108
254
  print(f"{fmri_data.shape=}")
109
255
  print(f"{threshval=}")
110
- print(f"{theevs.shape=}")
256
+ print(f"{theevs.shape=}, {np.min(theevs)=}, {np.max(theevs)=}")
257
+ print(f"{theevs=}")
111
258
  if procbyvoxel:
112
259
  indexaxis = 0
113
260
  procunit = "voxels"
@@ -143,13 +290,12 @@ def linfitfiltpass(
143
290
 
144
291
  # process and send the data
145
292
  if procbyvoxel:
146
- if confoundregress or (not voxelspecific):
293
+ if confoundregress or constantevs:
147
294
  outQ.put(
148
295
  _procOneRegressionFitItem(
149
296
  val,
150
297
  theevs,
151
298
  fmri_data[val, :],
152
- rt_floatset=rt_floatset,
153
299
  rt_floattype=rt_floattype,
154
300
  )
155
301
  )
@@ -159,18 +305,16 @@ def linfitfiltpass(
159
305
  val,
160
306
  theevs[val, :],
161
307
  fmri_data[val, :],
162
- rt_floatset=rt_floatset,
163
308
  rt_floattype=rt_floattype,
164
309
  )
165
310
  )
166
311
  else:
167
- if confoundregress or (not voxelspecific):
312
+ if confoundregress or constantevs:
168
313
  outQ.put(
169
314
  _procOneRegressionFitItem(
170
315
  val,
171
316
  theevs,
172
317
  fmri_data[:, val],
173
- rt_floatset=rt_floatset,
174
318
  rt_floattype=rt_floattype,
175
319
  )
176
320
  )
@@ -180,7 +324,6 @@ def linfitfiltpass(
180
324
  val,
181
325
  theevs[:, val],
182
326
  fmri_data[:, val],
183
- rt_floatset=rt_floatset,
184
327
  rt_floattype=rt_floattype,
185
328
  )
186
329
  )
@@ -214,7 +357,7 @@ def linfitfiltpass(
214
357
  meanvalue[voxel[0]] = voxel[1]
215
358
  rvalue[voxel[0]] = voxel[2]
216
359
  r2value[voxel[0]] = voxel[3]
217
- if theevs.ndim > 1:
360
+ if fitcoeff.ndim > 1:
218
361
  fitcoeff[voxel[0], :] = voxel[4]
219
362
  fitNorm[voxel[0], :] = voxel[5]
220
363
  else:
@@ -226,7 +369,7 @@ def linfitfiltpass(
226
369
  meanvalue[voxel[0]] = voxel[1]
227
370
  rvalue[voxel[0]] = voxel[2]
228
371
  r2value[voxel[0]] = voxel[3]
229
- if theevs.ndim > 1:
372
+ if fitcoeff.ndim > 1:
230
373
  fitcoeff[voxel[0], :] = voxel[4]
231
374
  fitNorm[voxel[0], :] = voxel[5]
232
375
  else:
@@ -246,7 +389,7 @@ def linfitfiltpass(
246
389
  meanvalue[timepoint[0]] = timepoint[1]
247
390
  rvalue[timepoint[0]] = timepoint[2]
248
391
  r2value[timepoint[0]] = timepoint[3]
249
- if theevs.ndim > 1:
392
+ if fitcoeff.ndim > 1:
250
393
  fitcoeff[:, timepoint[0]] = timepoint[4]
251
394
  fitNorm[:, timepoint[0]] = timepoint[5]
252
395
  else:
@@ -258,7 +401,7 @@ def linfitfiltpass(
258
401
  meanvalue[timepoint[0]] = timepoint[1]
259
402
  rvalue[timepoint[0]] = timepoint[2]
260
403
  r2value[timepoint[0]] = timepoint[3]
261
- if theevs.ndim > 1:
404
+ if fitcoeff.ndim > 1:
262
405
  fitcoeff[:, timepoint[0]] = timepoint[4]
263
406
  fitNorm[:, timepoint[0]] = timepoint[5]
264
407
  else:
@@ -270,6 +413,7 @@ def linfitfiltpass(
270
413
 
271
414
  del data_out
272
415
  else:
416
+ # this is the single proc path
273
417
  itemstotal = 0
274
418
  if procbyvoxel:
275
419
  for vox in tqdm(
@@ -294,11 +438,10 @@ def linfitfiltpass(
294
438
  vox,
295
439
  theevs,
296
440
  thedata,
297
- rt_floatset=rt_floatset,
298
441
  rt_floattype=rt_floattype,
299
442
  )
300
443
  elif coefficientsonly:
301
- if voxelspecific:
444
+ if not constantevs:
302
445
  (
303
446
  dummy,
304
447
  meanvalue[vox],
@@ -312,7 +455,6 @@ def linfitfiltpass(
312
455
  vox,
313
456
  theevs[vox, :],
314
457
  thedata,
315
- rt_floatset=rt_floatset,
316
458
  rt_floattype=rt_floattype,
317
459
  )
318
460
  else:
@@ -329,7 +471,6 @@ def linfitfiltpass(
329
471
  vox,
330
472
  theevs,
331
473
  thedata,
332
- rt_floatset=rt_floatset,
333
474
  rt_floattype=rt_floattype,
334
475
  )
335
476
  else:
@@ -346,7 +487,6 @@ def linfitfiltpass(
346
487
  vox,
347
488
  theevs[vox, :],
348
489
  thedata,
349
- rt_floatset=rt_floatset,
350
490
  rt_floattype=rt_floattype,
351
491
  )
352
492
  itemstotal += 1
@@ -373,26 +513,41 @@ def linfitfiltpass(
373
513
  timepoint,
374
514
  theevs,
375
515
  thedata,
376
- rt_floatset=rt_floatset,
377
516
  rt_floattype=rt_floattype,
378
517
  )
379
518
  elif coefficientsonly:
380
- (
381
- dummy,
382
- meanvalue[timepoint],
383
- rvalue[timepoint],
384
- r2value[timepoint],
385
- fitcoeff[timepoint],
386
- fitNorm[timepoint],
387
- dummy,
388
- dummy,
389
- ) = _procOneRegressionFitItem(
390
- timepoint,
391
- theevs[:, timepoint],
392
- thedata,
393
- rt_floatset=rt_floatset,
394
- rt_floattype=rt_floattype,
395
- )
519
+ if not constantevs:
520
+ (
521
+ dummy,
522
+ meanvalue[timepoint],
523
+ rvalue[timepoint],
524
+ r2value[timepoint],
525
+ fitcoeff[timepoint],
526
+ fitNorm[timepoint],
527
+ dummy,
528
+ dummy,
529
+ ) = _procOneRegressionFitItem(
530
+ timepoint,
531
+ theevs[:, timepoint],
532
+ thedata,
533
+ rt_floattype=rt_floattype,
534
+ )
535
+ else:
536
+ (
537
+ dummy,
538
+ meanvalue[timepoint],
539
+ rvalue[timepoint],
540
+ r2value[timepoint],
541
+ fitcoeff[timepoint],
542
+ fitNorm[timepoint],
543
+ dummy,
544
+ dummy,
545
+ ) = _procOneRegressionFitItem(
546
+ timepoint,
547
+ theevs,
548
+ thedata,
549
+ rt_floattype=rt_floattype,
550
+ )
396
551
  else:
397
552
  (
398
553
  dummy,
@@ -407,33 +562,58 @@ def linfitfiltpass(
407
562
  timepoint,
408
563
  theevs[:, timepoint],
409
564
  thedata,
410
- rt_floatset=rt_floatset,
411
565
  rt_floattype=rt_floattype,
412
566
  )
567
+
413
568
  itemstotal += 1
414
569
  if showprogressbar:
415
570
  print()
416
571
  return itemstotal
417
572
 
418
573
 
419
- def makevoxelspecificderivs(theevs, nderivs=1, debug=False):
420
- r"""Perform multicomponent expansion on theevs (each ev replaced by itself,
421
- its square, its cube, etc.).
574
+ def makevoxelspecificderivs(theevs: NDArray, nderivs: int = 1, debug: bool = False) -> NDArray:
575
+ """
576
+ Perform multicomponent expansion on voxel-specific explanatory variables by computing
577
+ derivatives up to a specified order.
578
+
579
+ This function takes an array of voxel-specific timecourses and expands each one
580
+ into a set of components representing the original signal and its derivatives.
581
+ Each component corresponds to a higher-order derivative of the signal, scaled by
582
+ the inverse factorial of the derivative order (Taylor series coefficients).
422
583
 
423
584
  Parameters
424
585
  ----------
425
586
  theevs : 2D numpy array
426
- NxP array of voxel specific explanatory variables (one timecourse per voxel)
427
- :param theevs:
587
+ NxP array of voxel-specific explanatory variables, where N is the number of voxels
588
+ and P is the number of timepoints.
589
+ nderivs : int, optional
590
+ Number of derivative components to compute for each voxel. Default is 1.
591
+ If 0, the original `theevs` are returned without modification.
592
+ debug : bool, optional
593
+ If True, print debugging information including input and output shapes.
594
+ Default is False.
428
595
 
429
- nderivs : integer
430
- Number of components to use for each ev. Each successive component is a
431
- higher power of the initial ev (initial, square, cube, etc.)
432
- :param nderivs:
596
+ Returns
597
+ -------
598
+ 3D numpy array
599
+ Array of shape (N, P, nderivs + 1) containing the original signal and its
600
+ derivatives up to order `nderivs` for each voxel. The first component is the
601
+ original signal, followed by the first, second, ..., up to `nderivs`-th derivative.
433
602
 
434
- debug: bool
435
- Flag to toggle debugging output
436
- :param debug:
603
+ Notes
604
+ -----
605
+ - The function uses `numpy.gradient` to compute numerical derivatives.
606
+ - Each derivative component is scaled by the inverse factorial of the derivative order
607
+ to align with Taylor series expansion coefficients.
608
+ - If `nderivs=0`, the function returns a copy of the input `theevs`.
609
+
610
+ Examples
611
+ --------
612
+ >>> import numpy as np
613
+ >>> theevs = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
614
+ >>> result = makevoxelspecificderivs(theevs, nderivs=2)
615
+ >>> print(result.shape)
616
+ (2, 4, 3)
437
617
  """
438
618
  if debug:
439
619
  print(f"{theevs.shape=}")
@@ -459,19 +639,86 @@ def makevoxelspecificderivs(theevs, nderivs=1, debug=False):
459
639
 
460
640
 
461
641
  def confoundregress(
462
- theregressors,
463
- theregressorlabels,
464
- thedataarray,
465
- tr,
466
- nprocs=1,
467
- orthogonalize=True,
468
- tcstart=0,
469
- tcend=-1,
470
- tchp=None,
471
- tclp=None,
472
- showprogressbar=True,
473
- debug=False,
474
- ):
642
+ theregressors: NDArray,
643
+ theregressorlabels: list[str],
644
+ thedataarray: NDArray,
645
+ tr: float,
646
+ nprocs: int = 1,
647
+ orthogonalize: bool = True,
648
+ tcstart: int = 0,
649
+ tcend: int = -1,
650
+ tchp: float | None = None,
651
+ tclp: float | None = None,
652
+ showprogressbar: bool = True,
653
+ debug: bool = False,
654
+ ) -> tuple[NDArray, list[str], NDArray, NDArray]:
655
+ """
656
+ Perform confound regression on fMRI data using linear regression.
657
+
658
+ This function applies confound regression to remove noise from fMRI time series
659
+ by regressing out specified confounding variables (e.g., motion parameters,
660
+ physiological signals). It supports optional filtering, orthogonalization of
661
+ regressors, and parallel processing for performance.
662
+
663
+ Parameters
664
+ ----------
665
+ theregressors : ndarray
666
+ Array of confounding variables with shape (n_regressors, n_timepoints).
667
+ theregressorlabels : list of str
668
+ List of labels corresponding to each regressor.
669
+ thedataarray : ndarray
670
+ 3D or 4D array of fMRI data with shape (n_voxels, n_timepoints) or
671
+ (n_voxels, n_timepoints, n_volumes).
672
+ tr : float
673
+ Repetition time (TR) in seconds.
674
+ nprocs : int, optional
675
+ Number of processes to use for parallel processing. Default is 1.
676
+ orthogonalize : bool, optional
677
+ If True, orthogonalize the regressors to reduce multicollinearity.
678
+ Default is True.
679
+ tcstart : int, optional
680
+ Start timepoint index for regressor data. Default is 0.
681
+ tcend : int, optional
682
+ End timepoint index for regressor data. If -1, use all timepoints
683
+ from `tcstart`. Default is -1.
684
+ tchp : float, optional
685
+ High-pass cutoff frequency for filtering. If None, no high-pass filtering
686
+ is applied.
687
+ tclp : float, optional
688
+ Low-pass cutoff frequency for filtering. If None, no low-pass filtering
689
+ is applied.
690
+ showprogressbar : bool, optional
691
+ If True, display a progress bar during processing. Default is True.
692
+ debug : bool, optional
693
+ If True, enable debug output. Default is False.
694
+
695
+ Returns
696
+ -------
697
+ tuple of (NDArray, list of str, NDArray, NDArray)
698
+ - `theregressors`: Processed regressors (possibly orthogonalized).
699
+ - `theregressorlabels`: Updated labels for the regressors.
700
+ - `filtereddata`: Data with confounds removed.
701
+ - `r2value`: R-squared values for each voxel (or None if not computed).
702
+
703
+ Notes
704
+ -----
705
+ - The function applies standard deviation normalization to regressors for
706
+ numerical stability.
707
+ - If `orthogonalize` is True, Gram-Schmidt orthogonalization is applied to
708
+ the regressors.
709
+ - Filtering is applied using a trapezoidal filter if `tchp` or `tclp` are provided.
710
+ - The function uses `linfitfiltpass` internally for the actual regression.
711
+
712
+ Examples
713
+ --------
714
+ >>> regressors = np.random.rand(3, 100)
715
+ >>> labels = ['motion_x', 'motion_y', 'motion_z']
716
+ >>> data = np.random.rand(50, 100)
717
+ >>> tr = 2.0
718
+ >>> processed_regressors, labels, filtered_data, r2 = confoundregress(
719
+ ... regressors, labels, data, tr, nprocs=4
720
+ ... )
721
+ """
475
722
  if tcend == -1:
476
723
  theregressors = theregressors[:, tcstart:]
477
724
  else: