antspymm 1.1.2__py3-none-any.whl → 1.1.3__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.
antspymm/__init__.py CHANGED
@@ -56,6 +56,8 @@ from .mm import average_mm_df
56
56
  from .mm import get_names_from_data_frame
57
57
  from .mm import read_mm_csv
58
58
  from .mm import assemble_modality_specific_dataframes
59
+ from .mm import aggregate_antspymm_results
60
+ from .mm import aggregate_antspymm_results_sdf
59
61
  from .mm import mc_denoise
60
62
  from .mm import mc_reg
61
63
  from .mm import dti_reg
@@ -93,3 +95,4 @@ from .mm import merge_wides_to_study_dataframe
93
95
  from .mm import map_scalar_to_labels
94
96
  from .mm import template_figure_with_overlay
95
97
  from .mm import brainmap_figure
98
+ from .mm import bold_perfusion
antspymm/mm.py CHANGED
@@ -79,6 +79,8 @@ __all__ = ['version',
79
79
  'novelty_detection_loop',
80
80
  'novelty_detection_quantile',
81
81
  'generate_mm_dataframe',
82
+ 'aggregate_antspymm_results',
83
+ 'aggregate_antspymm_results_sdf',
82
84
  'study_dataframe_from_matched_dataframe',
83
85
  'merge_wides_to_study_dataframe',
84
86
  'wmh']
@@ -159,11 +161,11 @@ def get_valid_modalities( long=False, asString=False, qc=False ):
159
161
  asString - concat list to string
160
162
  """
161
163
  if long:
162
- mymod = ["T1w", "NM2DMT", "rsfMRI", "rsfMRI_LR", "rsfMRI_RL", "DTI", "DTI_LR","DTI_RL","T2Flair", "dwi", "func" ]
164
+ mymod = ["T1w", "NM2DMT", "rsfMRI", "rsfMRI_LR", "rsfMRI_RL", "DTI", "DTI_LR","DTI_RL","T2Flair", "dwi", "func", "perf" ]
163
165
  elif qc:
164
- mymod = [ 'T1w', 'T2Flair', 'NM2DMT','DTIdwi','DTIb0', 'rsfMRI']
166
+ mymod = [ 'T1w', 'T2Flair', 'NM2DMT','DTIdwi','DTIb0', 'rsfMRI', "perf" ]
165
167
  else:
166
- mymod = ["T1w", "NM2DMT", "DTI","T2Flair", "rsfMRI" ]
168
+ mymod = ["T1w", "NM2DMT", "DTI","T2Flair", "rsfMRI", "perf" ]
167
169
  if not asString:
168
170
  return mymod
169
171
  else:
@@ -184,7 +186,8 @@ def generate_mm_dataframe(
184
186
  flair_filename=[],
185
187
  rsf_filenames=[],
186
188
  dti_filenames=[],
187
- nm_filenames=[]
189
+ nm_filenames=[],
190
+ perf_filename=[]
188
191
  ):
189
192
  from os.path import exists
190
193
  valid_modalities = get_valid_modalities()
@@ -219,6 +222,17 @@ def generate_mm_dataframe(
219
222
  flair_filename=flair_filename[0]
220
223
  if flair_filename is not None and not "lair" in flair_filename:
221
224
  raise ValueError("flair is not flair filename " + flair_filename)
225
+ ## perfusion
226
+ if perf_filename is not None:
227
+ if isinstance(perf_filename,list):
228
+ if (len(perf_filename) == 0):
229
+ perf_filename=None
230
+ else:
231
+ print("Take first entry from perf_filename list")
232
+ perf_filename=perf_filename[0]
233
+ if perf_filename is not None and not "perf" in perf_filename:
234
+ raise ValueError("perf_filename is not perf filename " + perf_filename)
235
+
222
236
  for k in nm_filenames:
223
237
  if k is not None:
224
238
  if not "NM" in k:
@@ -231,7 +245,10 @@ def generate_mm_dataframe(
231
245
  if k is not None:
232
246
  if not "fMRI" in k and not "func" in k:
233
247
  raise ValueError("rsfMRI/func is not rsfmri filename " + k)
234
- allfns = [t1_filename] + [flair_filename] + nm_filenames + dti_filenames + rsf_filenames
248
+ if perf_filename is not None:
249
+ if not "perf" in perf_filename:
250
+ raise ValueError("perf_filename is not a valid perfusion (perf) filename " + k)
251
+ allfns = [t1_filename] + [flair_filename] + nm_filenames + dti_filenames + rsf_filenames + [perf_filename]
235
252
  for k in allfns:
236
253
  if k is not None:
237
254
  if not isinstance(k, str):
@@ -247,7 +264,8 @@ def generate_mm_dataframe(
247
264
  source_image_directory,
248
265
  output_image_directory,
249
266
  t1_filename,
250
- flair_filename]
267
+ flair_filename,
268
+ perf_filename]
251
269
  mydata0 = coredata + rsf_filenames + dti_filenames
252
270
  mydata = mydata0 + nm_filenames
253
271
  corecols = [
@@ -259,7 +277,8 @@ def generate_mm_dataframe(
259
277
  'sourcedir',
260
278
  'outputdir',
261
279
  'filename',
262
- 'flairid']
280
+ 'flairid',
281
+ 'perfid']
263
282
  mycols0 = corecols + [
264
283
  'rsfid1', 'rsfid2',
265
284
  'dtid1', 'dtid2']
@@ -268,10 +287,6 @@ def generate_mm_dataframe(
268
287
  'nmid6', 'nmid7','nmid8', 'nmid9', 'nmid10', 'nmid11'
269
288
  ]
270
289
  mycols = mycols0 + nmext
271
- print(len(mydata0))
272
- print(len(nm_filenames))
273
- print(len(mycols0))
274
- print(len(nmext))
275
290
  studycsv = pd.DataFrame([ mydata ],
276
291
  columns=mycols)
277
292
  return studycsv
@@ -343,6 +358,10 @@ def nrg_2_bids( nrg_filename ):
343
358
  bids_modality_folder = 'func'
344
359
  bids_modality_filename = 'func'
345
360
 
361
+ if nrg_modality == 'perf' :
362
+ bids_modality_folder = 'perf'
363
+ bids_modality_filename = 'perf'
364
+
346
365
  bids_suffix = nrg_suffix[1:]
347
366
  bids_filename = f'{bids_subject}_{bids_session}_{bids_modality_filename}.{bids_suffix}'
348
367
 
@@ -385,6 +404,9 @@ def bids_2_nrg( bids_filename, project_name, date, nrg_modality=None ):
385
404
  if bids_modality == 'func' and nrg_modality is None :
386
405
  nrg_modality = 'rsfMRI'
387
406
 
407
+ if bids_modality == 'perf' and nrg_modality is None :
408
+ nrg_modality = 'perf'
409
+
388
410
  nrg_suffix = bids_suffix[1:]
389
411
  nrg_filename = f'{project_name}-{nrg_subject_id}-{date}-{nrg_modality}-{nrg_image_id}.{nrg_suffix}'
390
412
 
@@ -2308,7 +2330,9 @@ def get_average_rsf( x, min_t=10, max_t=35 ):
2308
2330
  bavg = bavg + ants.registration(oavg,b0,'Rigid',outprefix=ofn)['warpedmovout']
2309
2331
  import shutil
2310
2332
  shutil.rmtree(output_directory, ignore_errors=True )
2311
- return ants.n4_bias_field_correction(bavg)
2333
+ bavg = ants.iMath( bavg, 'Normalize' )
2334
+ return bavg
2335
+ # return ants.n4_bias_field_correction(bavg, mask=ants.get_mask( bavg ) )
2312
2336
 
2313
2337
 
2314
2338
  def get_average_dwi_b0( x, fixed_b0=None, fixed_dwi=None, fast=False ):
@@ -4335,7 +4359,6 @@ def resting_state_fmri_networks( fmri, fmri_template, t1, t1segmentation,
4335
4359
  reg_iterations=[40,20,5] )
4336
4360
  if verbose:
4337
4361
  print("End rsfmri motion correction")
4338
- # ants.image_write( corrmo['motion_corrected'], '/tmp/temp.nii.gz' )
4339
4362
 
4340
4363
  mytsnr = tsnr( corrmo['motion_corrected'], bmask )
4341
4364
  mytsnrThresh = np.quantile( mytsnr.numpy(), 0.995 )
@@ -4345,8 +4368,6 @@ def resting_state_fmri_networks( fmri, fmri_template, t1, t1segmentation,
4345
4368
  t1reg = ants.registration( und, t1, "SyNBold" )
4346
4369
  if verbose:
4347
4370
  print("t1 2 bold done")
4348
- # ants.image_write( und, '/tmp/template_bold_masked.nii.gz' )
4349
- # ants.image_write( t1reg['warpedmovout'], '/tmp/t1tobold.nii.gz' )
4350
4371
  boldseg = ants.apply_transforms( und, t1segmentation,
4351
4372
  t1reg['fwdtransforms'], interpolator = 'genericLabel' ) * bmask
4352
4373
  gmseg = ants.threshold_image( t1segmentation, 2, 2 )
@@ -4524,6 +4545,216 @@ def resting_state_fmri_networks( fmri, fmri_template, t1, t1segmentation,
4524
4545
  return outdict
4525
4546
 
4526
4547
 
4548
+ def bold_perfusion( fmri, fmri_template, t1head, t1, t1segmentation, t1dktcit, f=[0.0,math.inf], FD_threshold=0.5, spa = 1.5, nc = 6, type_of_transform='Rigid', tc='alternating', deepmask=False, add_FD_to_nuisance=False, verbose=False ):
4549
+ """
4550
+ Estimate perfusion from a BOLD time series image. Will attempt to figure out the T-C labels from the data.
4551
+
4552
+ Arguments
4553
+ ---------
4554
+ fmri : BOLD fmri antsImage
4555
+
4556
+ fmri_template : reference space for BOLD
4557
+
4558
+ t1head : ANTsImage
4559
+ input 3-D T1 brain image (not brain extracted)
4560
+
4561
+ t1 : ANTsImage
4562
+ input 3-D T1 brain image (brain extracted)
4563
+
4564
+ t1segmentation : ANTsImage
4565
+ t1 segmentation - a six tissue segmentation image in T1 space
4566
+
4567
+ t1dktcit : ANTsImage
4568
+ t1 dkt cortex plus cit parcellation
4569
+
4570
+ f : band pass limits for frequency filtering
4571
+
4572
+ spa : gaussian smoothing for spatial and temporal component e.g. (1,1,1,0) in physical space coordinates
4573
+
4574
+ nc : number of components for compcor filtering
4575
+
4576
+ type_of_transform : SyN or Rigid
4577
+
4578
+ tc: string either alternating or split (default is alternating ie CTCTCT; split is CCCCTTTT)
4579
+
4580
+ deepmask: boolean
4581
+
4582
+ add_FD_to_nuisance: boolean
4583
+
4584
+ verbose : boolean
4585
+
4586
+ Returns
4587
+ ---------
4588
+ a dictionary containing the derived network maps
4589
+
4590
+ """
4591
+ import numpy as np
4592
+ import pandas as pd
4593
+ import re
4594
+ import math
4595
+ from sklearn.linear_model import LinearRegression
4596
+
4597
+ ex_path = os.path.expanduser( "~/.antspyt1w/" )
4598
+ cnxcsvfn = ex_path + "dkt_cortex_cit_deep_brain.csv"
4599
+
4600
+ def replicate_list(user_list, target_size):
4601
+ # Calculate the number of times the list should be replicated
4602
+ replication_factor = target_size // len(user_list)
4603
+ # Replicate the list and handle any remaining elements
4604
+ replicated_list = user_list * replication_factor
4605
+ remaining_elements = target_size % len(user_list)
4606
+ replicated_list += user_list[:remaining_elements]
4607
+ return replicated_list
4608
+
4609
+ def one_hot_encode(char_list):
4610
+ unique_chars = list(set(char_list))
4611
+ encoding_dict = {char: [1 if char == c else 0 for c in unique_chars] for char in unique_chars}
4612
+ encoded_matrix = np.array([encoding_dict[char] for char in char_list])
4613
+ return encoded_matrix
4614
+
4615
+ A = np.zeros((1,1))
4616
+ fmri = ants.iMath( fmri, 'Normalize' )
4617
+ if deepmask:
4618
+ bmask = antspynet.brain_extraction( fmri_template, 'bold' ).threshold_image(0.5,1).morphology("close",2).iMath("FillHoles")
4619
+ else:
4620
+ rig = ants.registration( fmri_template, t1head, 'BOLDRigid' )
4621
+ bmask = ants.apply_transforms( fmri_template, ants.threshold_image(t1segmentation,1,6), rig['fwdtransforms'][0], interpolator='genericLabel' )
4622
+ if verbose:
4623
+ print("Begin perfusion motion correction")
4624
+ mytrim=4 # trim will guarantee an even length
4625
+ if fmri.shape[3] % 2 == 1:
4626
+ mytrim = 5
4627
+ corrmo = timeseries_reg(
4628
+ fmri, fmri_template,
4629
+ type_of_transform=type_of_transform,
4630
+ total_sigma=0.0,
4631
+ fdOffset=2.0,
4632
+ trim = mytrim,
4633
+ output_directory=None,
4634
+ verbose=verbose,
4635
+ syn_metric='cc',
4636
+ syn_sampling=2,
4637
+ reg_iterations=[40,20,5] )
4638
+ if verbose:
4639
+ print("End rsfmri motion correction")
4640
+
4641
+ ntp = corrmo['motion_corrected'].shape[3]
4642
+ if tc == 'alternating':
4643
+ tclist = replicate_list( ['C','T'], ntp )
4644
+ else:
4645
+ tclist = replicate_list( ['C'], int(ntp/2) ) + replicate_list( ['T'], int(ntp/2) )
4646
+
4647
+ tclist = one_hot_encode( tclist )
4648
+ mytsnr = tsnr( corrmo['motion_corrected'], bmask )
4649
+ mytsnrThresh = np.quantile( mytsnr.numpy(), 0.995 )
4650
+ tsnrmask = ants.threshold_image( mytsnr, 0, mytsnrThresh ).morphology("close",2)
4651
+ bmask = bmask * ants.iMath( tsnrmask, "FillHoles" )
4652
+ und = fmri_template * bmask
4653
+ t1reg = ants.registration( und, t1, "SyNBold" )
4654
+ if verbose:
4655
+ print("t1 2 bold done")
4656
+ boldseg = ants.apply_transforms( und, t1segmentation,
4657
+ t1reg['fwdtransforms'], interpolator = 'genericLabel' ) * bmask
4658
+ dktseg = ants.apply_transforms( und, t1dktcit,
4659
+ t1reg['fwdtransforms'], interpolator = 'genericLabel' ) * bmask
4660
+ gmseg = ants.threshold_image( t1segmentation, 2, 2 )
4661
+ gmseg = gmseg + ants.threshold_image( t1segmentation, 4, 4 )
4662
+ gmseg = ants.threshold_image( gmseg, 1, 4 )
4663
+ gmseg = ants.iMath( gmseg, 'MD', 1 )
4664
+ gmseg = ants.apply_transforms( und, gmseg,
4665
+ t1reg['fwdtransforms'], interpolator = 'genericLabel' ) * bmask
4666
+ csfAndWM = ( ants.threshold_image( t1segmentation, 1, 1 ) +
4667
+ ants.threshold_image( t1segmentation, 3, 3 ) ).morphology("erode",1)
4668
+ csfAndWM = ants.apply_transforms( und, csfAndWM,
4669
+ t1reg['fwdtransforms'], interpolator = 'nearestNeighbor' ) * bmask
4670
+
4671
+ mycompcor = ants.compcor( corrmo['motion_corrected'],
4672
+ ncompcor=nc, quantile=0.90, mask = csfAndWM,
4673
+ filter_type='polynomial', degree=2 )
4674
+
4675
+ nt = corrmo['motion_corrected'].shape[3]
4676
+ tr = ants.get_spacing( corrmo['motion_corrected'] )[3]
4677
+ highMotionTimes = np.where( corrmo['FD'] >= 1.0 )
4678
+ goodtimes = np.where( corrmo['FD'] < 0.5 )
4679
+ simg = ants.smooth_image(corrmo['motion_corrected'],
4680
+ spa, sigma_in_physical_coordinates = True )
4681
+
4682
+ nuisance = mycompcor[ 'components' ]
4683
+ nuisance = np.c_[ nuisance, mycompcor['basis'] ]
4684
+ if add_FD_to_nuisance:
4685
+ nuisance = np.c_[ nuisance, corrmo['FD'] ]
4686
+
4687
+ if verbose:
4688
+ print("make sure nuisance is independent of TC")
4689
+ nuisance = ants.regress_components( nuisance, tclist )
4690
+
4691
+ regression_mask = bmask.clone()
4692
+ gmmat = ants.timeseries_to_matrix( simg, regression_mask )
4693
+ if f[0] > 0 and f[1] < 1: # some would argue against this
4694
+ gmmat = ants.bandpass_filter_matrix( gmmat, tr = tr, lowf=f[0], highf=f[1] )
4695
+ # gmmat = ants.regress_components( gmmat, nuisance )
4696
+ # Perform linear regression to estimate perfusion
4697
+ regression_model = LinearRegression()
4698
+ regvars = np.hstack( (nuisance, tclist ))
4699
+ coefind = regvars.shape[1]-1
4700
+ regvars = regvars[:,range(coefind)]
4701
+ regression_model.fit( regvars, gmmat )
4702
+ coefind = regression_model.coef_.shape[1]-1
4703
+ perfimg = ants.make_image( regression_mask, regression_model.coef_[:,coefind] )
4704
+ meangmval = ( perfimg[ gmseg == 1 ] ).mean()
4705
+ if meangmval < 0:
4706
+ perfimg = perfimg * (-1.0)
4707
+ meangmval = ( perfimg[ gmseg == 1 ] ).mean()
4708
+ if verbose:
4709
+ print("Coefficients:", regression_model.coef_)
4710
+ print("Coef mean", regression_model.coef_.mean(axis=0))
4711
+ print( regression_model.coef_.shape )
4712
+ print( perfimg.max() )
4713
+ gsrbold = ants.matrix_to_timeseries(simg, gmmat, regression_mask)
4714
+ outdict = {}
4715
+ outdict['meanBold'] = und
4716
+ outdict['brainmask'] = bmask
4717
+ rsfNuisance = pd.DataFrame( nuisance )
4718
+ rsfNuisance['FD']=corrmo['FD']
4719
+
4720
+
4721
+ nonbrainmask = ants.iMath( bmask, "MD",2) - bmask
4722
+ trimmask = ants.iMath( bmask, "ME",2)
4723
+ edgemask = ants.iMath( bmask, "ME",1) - trimmask
4724
+
4725
+ if verbose:
4726
+ print("perfusion dataframe begin")
4727
+ df_perf = antspyt1w.map_intensity_to_dataframe(
4728
+ 'dkt_cortex_cit_deep_brain',
4729
+ perfimg,
4730
+ dktseg)
4731
+ df_perf = antspyt1w.merge_hierarchical_csvs_to_wide_format(
4732
+ {'perf' : df_perf},
4733
+ col_names = ['Mean'] )
4734
+ if verbose:
4735
+ print("perfusion dataframe end")
4736
+ print( df_perf )
4737
+
4738
+ outdict['perfusion']=perfimg
4739
+ outdict['perfusion_gm_mean']=meangmval
4740
+ outdict['perf_dataframe']=df_perf
4741
+ outdict['motion_corrected'] = corrmo['motion_corrected']
4742
+ outdict['gmseg'] = gmseg
4743
+ outdict['gsrbold'] = gsrbold
4744
+ outdict['brain_mask'] = bmask
4745
+ outdict['nuisance'] = rsfNuisance
4746
+ outdict['tsnr'] = mytsnr
4747
+ outdict['ssnr'] = slice_snr( corrmo['motion_corrected'], csfAndWM, gmseg )
4748
+ outdict['dvars'] = dvars( corrmo['motion_corrected'], gmseg )
4749
+ outdict['high_motion_count'] = (rsfNuisance['FD'] > FD_threshold ).sum()
4750
+ outdict['high_motion_pct'] = (rsfNuisance['FD'] > FD_threshold ).sum() / rsfNuisance.shape[0]
4751
+ outdict['FD_max'] = rsfNuisance['FD'].max()
4752
+ outdict['FD_mean'] = rsfNuisance['FD'].mean()
4753
+ outdict['bold_evr'] = antspyt1w.patch_eigenvalue_ratio( und, 512, [16,16,16], evdepth = 0.9, mask = bmask )
4754
+ outdict['t1reg'] = t1reg
4755
+ return outdict
4756
+
4757
+
4527
4758
 
4528
4759
  def write_bvals_bvecs(bvals, bvecs, prefix ):
4529
4760
  ''' Write FSL FDT bvals and bvecs files
@@ -4595,6 +4826,7 @@ def mm(
4595
4826
  flair_image=None,
4596
4827
  nm_image_list=None,
4597
4828
  dw_image=[], bvals=[], bvecs=[],
4829
+ perfusion_image=None,
4598
4830
  srmodel=None,
4599
4831
  do_tractography = False,
4600
4832
  do_kk = False,
@@ -4631,6 +4863,8 @@ def mm(
4631
4863
 
4632
4864
  bvecs : list of bvecs file names
4633
4865
 
4866
+ perfusion_image : single perfusion image
4867
+
4634
4868
  srmodel : optional srmodel
4635
4869
 
4636
4870
  do_tractography : boolean
@@ -4699,7 +4933,8 @@ def mm(
4699
4933
  'FA_summ' : None,
4700
4934
  'MD_summ' : None,
4701
4935
  'tractography' : None,
4702
- 'tractography_connectivity' : None
4936
+ 'tractography_connectivity' : None,
4937
+ 'perf' : None,
4703
4938
  }
4704
4939
  normalization_dict = {
4705
4940
  'kk_norm': None,
@@ -4707,6 +4942,7 @@ def mm(
4707
4942
  'DTI_norm': None,
4708
4943
  'FA_norm' : None,
4709
4944
  'MD_norm' : None,
4945
+ 'perf_norm' : None,
4710
4946
  'alff_norm' : None,
4711
4947
  'falff_norm' : None,
4712
4948
  'CinguloopercularTaskControl_norm' : None,
@@ -4727,6 +4963,19 @@ def mm(
4727
4963
  print('kk')
4728
4964
  output_dict['kk'] = antspyt1w.kelly_kapowski_thickness( hier['brain_n4_dnz'],
4729
4965
  labels=hier['dkt_parc']['dkt_cortex'], iterations=45 )
4966
+ if perfusion_image is not None:
4967
+ boldTemplate=get_average_rsf(perfusion_image)
4968
+ if perfusion_image.shape[3] > 8: # FIXME - better heuristic?
4969
+ output_dict['perf'] = bold_perfusion(
4970
+ perfusion_image,
4971
+ boldTemplate,
4972
+ t1_image,
4973
+ hier['brain_n4_dnz'],
4974
+ t1atropos,
4975
+ hier['dkt_parc']['dkt_cortex'] + hier['cit168lab'],
4976
+ f=[0.0,math.inf],
4977
+ spa = (1.0, 1.0, 1.0, 0.0),
4978
+ nc = 6, verbose=verbose )
4730
4979
  ################################## do the rsf .....
4731
4980
  if len(rsf_image) > 0:
4732
4981
  rsf_image = [i for i in rsf_image if i is not None]
@@ -4907,7 +5156,7 @@ def mm(
4907
5156
  if verbose:
4908
5157
  print('normalization')
4909
5158
  # might reconsider this template space - cropped and/or higher res?
4910
- template = ants.resample_image( template, [1,1,1], use_voxels=False )
5159
+ # template = ants.resample_image( template, [1,1,1], use_voxels=False )
4911
5160
  # t1reg = ants.registration( template, hier['brain_n4_dnz'], "antsRegistrationSyNQuickRepro[s]")
4912
5161
  t1reg = do_normalization
4913
5162
  if do_kk:
@@ -4933,6 +5182,11 @@ def mm(
4933
5182
  normalization_dict[rsfkey] = ants.apply_transforms(
4934
5183
  group_template, rsfpro[netid],
4935
5184
  group_transform+rsfrig['fwdtransforms'] )
5185
+ if output_dict['perf'] is not None: # zizzer
5186
+ comptx = group_transform + output_dict['perf']['t1reg']['invtransforms']
5187
+ normalization_dict['perf_norm'] = ants.apply_transforms( group_template,
5188
+ output_dict['perf']['perfusion'], comptx,
5189
+ whichtoinvert=[False,False,True,False] )
4936
5190
  if nm_image_list is not None:
4937
5191
  nmpro = output_dict['NM']
4938
5192
  nmrig = nmpro['t1_to_NM_transform'] # this is an inverse tx
@@ -4993,6 +5247,7 @@ def write_mm( output_prefix, mm, mm_norm=None, t1wide=None, separator='_', verbo
4993
5247
  image_write_with_thumbnail( mm['NM'][mykey], tempfn, thumb=False )
4994
5248
 
4995
5249
  faderk = mdderk = fat1derk = mdt1derk = None
5250
+
4996
5251
  if mm['DTI'] is not None:
4997
5252
  mydti = mm['DTI']
4998
5253
  myop = output_prefix + separator
@@ -5105,6 +5360,26 @@ def write_mm( output_prefix, mm, mm_norm=None, t1wide=None, separator='_', verbo
5105
5360
  # mm_wide.to_csv( fdfn )
5106
5361
  else:
5107
5362
  mm_wide['dti_FD_mean'] = mm_wide['dti_FD_max'] = 'NA'
5363
+
5364
+ if mm['perf'] is not None:
5365
+ perfpro = mm['perf']
5366
+ mm_wide['gm_mean'] = perfpro['perfusion_gm_mean']
5367
+ mm_wide['tsnr_mean'] = perfpro['tsnr'].mean()
5368
+ mm_wide['dvars_mean'] = perfpro['dvars'].mean()
5369
+ mm_wide['ssnr_mean'] = perfpro['ssnr'].mean()
5370
+ mm_wide['high_motion_count'] = perfpro['high_motion_count']
5371
+ mm_wide['evr'] = perfpro['bold_evr']
5372
+ mm_wide['FD_mean'] = perfpro['FD_mean']
5373
+ mm_wide['FD_max'] = perfpro['FD_max']
5374
+ if 'perf_dataframe' in perfpro.keys():
5375
+ pderk = perfpro['perf_dataframe'].iloc[: , 1:]
5376
+ mm_wide = pd.concat( [ mm_wide, pderk ], axis=1 )
5377
+ else:
5378
+ print("FIXME - perfusion dataframe")
5379
+ mykey='perfusion'
5380
+ tempfn = output_prefix + separator + mykey + '.nii.gz'
5381
+ image_write_with_thumbnail( mm['perf'][mykey], tempfn )
5382
+
5108
5383
  mmwidefn = output_prefix + separator + 'mmwide.csv'
5109
5384
  mm_wide.to_csv( mmwidefn )
5110
5385
  if verbose:
@@ -5611,7 +5886,7 @@ def mm_nrg(
5611
5886
  write_mm( output_prefix=mymm, mm=tabPro, mm_norm=normPro, t1wide=t1wide, separator=mysep, verbose=True )
5612
5887
  for mykey in normPro.keys():
5613
5888
  if normPro[mykey] is not None:
5614
- if visualize and normPro[mykey].components == 1:
5889
+ if visualize and normPro[mykey].components == 1 and False:
5615
5890
  ants.plot( template, normPro[mykey], axis=2, nslices=21, ncol=7, crop=True, title=mykey, filename=mymm+mysep+mykey+".png" )
5616
5891
  if overmodX == nrg_modality_list[ len( nrg_modality_list ) - 1 ]:
5617
5892
  return
@@ -5760,6 +6035,8 @@ def mm_csv(
5760
6035
  imfns=['filename']
5761
6036
  elif locmod == 'T2Flair':
5762
6037
  imfns=['flairid']
6038
+ elif locmod == 'perf':
6039
+ imfns=['perfid']
5763
6040
  elif locmod == 'NM2DMT':
5764
6041
  imfns=[]
5765
6042
  for i in range(11):
@@ -5918,6 +6195,9 @@ def mm_csv(
5918
6195
  hier['brain_n4_dnz'],
5919
6196
  normalization_template_transform_type,
5920
6197
  outprefix = normout, verbose=False )
6198
+ myjac = ants.create_jacobian_determinant_image( template,
6199
+ greg['fwdtransforms'][0], do_log=True, geom=True )
6200
+ image_write_with_thumbnail( myjac, normout + "logjacobian.nii.gz", thumb=False )
5921
6201
  if verbose:
5922
6202
  print("end group template registration")
5923
6203
  else:
@@ -6095,6 +6375,22 @@ def mm_csv(
6095
6375
  axis=2, nslices=maxslice, ncol=7, crop=True, title='DefaultMode', filename=mymm+mysep+"boldDefaultMode.png" )
6096
6376
  ants.plot( tabPro['rsf']['meanBold'], tabPro['rsf']['FrontoparietalTaskControl'],
6097
6377
  axis=2, nslices=maxslice, ncol=7, crop=True, title='FrontoparietalTaskControl', filename=mymm+mysep+"boldFrontoparietalTaskControl.png" )
6378
+ if ( mymod == 'perf' ) and ishapelen == 4:
6379
+ dowrite=True
6380
+ tabPro, normPro = mm( t1, hier,
6381
+ perfusion_image=img,
6382
+ srmodel=None,
6383
+ do_tractography=False,
6384
+ do_kk=False,
6385
+ do_normalization=templateTx,
6386
+ group_template = normalization_template,
6387
+ group_transform = groupTx,
6388
+ test_run=test_run,
6389
+ verbose=True )
6390
+ if tabPro['perf'] is not None and visualize:
6391
+ maxslice = np.min( [21, tabPro['perf']['meanBold'].shape[2] ] )
6392
+ ants.plot( tabPro['perf']['perfusion'],
6393
+ axis=2, nslices=maxslice, ncol=7, crop=True, title='perfusion image', filename=mymm+mysep+"perfusion.png" )
6098
6394
  if ( mymod == 'DTI_LR' or mymod == 'DTI_RL' or mymod == 'DTI' ) and ishapelen == 4:
6099
6395
  bvalfn = re.sub( '.nii.gz', '.bval' , myimg )
6100
6396
  bvecfn = re.sub( '.nii.gz', '.bvec' , myimg )
@@ -6158,7 +6454,7 @@ def mm_csv(
6158
6454
  write_mm( output_prefix=mymm, mm=tabPro, mm_norm=normPro, t1wide=t1wide, separator=mysep )
6159
6455
  for mykey in normPro.keys():
6160
6456
  if normPro[mykey] is not None and normPro[mykey].components == 1:
6161
- if visualize:
6457
+ if visualize and False:
6162
6458
  ants.plot( template, normPro[mykey], axis=2, nslices=21, ncol=7, crop=True, title=mykey, filename=mymm+mysep+mykey+".png" )
6163
6459
  if overmodX == nrg_modality_list[ len( nrg_modality_list ) - 1 ]:
6164
6460
  return
@@ -6566,7 +6862,7 @@ progress=False, verbose=False ):
6566
6862
  """
6567
6863
  extend a study data frame with wide outputs
6568
6864
 
6569
- sdf : the input study dataframe
6865
+ sdf : the input study dataframe from antspymm QC output
6570
6866
 
6571
6867
  processing_dir: the directory location of the processed data
6572
6868
 
@@ -6589,8 +6885,8 @@ progress=False, verbose=False ):
6589
6885
  for k in range(len(musthavecols)):
6590
6886
  if not musthavecols[k] in sdf.keys():
6591
6887
  raise ValueError('sdf is missing column ' +musthavecols[k] + ' in merge_wides_to_study_dataframe' )
6592
- possible_iids = [ 'imageID', 'imageID', 'imageID', 'flairid', 'dtid1', 'dtid2', 'rsfid1', 'rsfid2', 'nmid1', 'nmid2', 'nmid3', 'nmid4', 'nmid5', 'nmid6', 'nmid7', 'nmid8', 'nmid9', 'nmid10' ]
6593
- modality_ids = [ 'T1wHierarchical', 'T1wHierarchicalSR', 'T1w', 'T2Flair', 'DTI', 'DTI', 'rsfMRI', 'rsfMRI', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT']
6888
+ possible_iids = [ 'imageID', 'imageID', 'imageID', 'flairid', 'dtid1', 'dtid2', 'rsfid1', 'rsfid2', 'nmid1', 'nmid2', 'nmid3', 'nmid4', 'nmid5', 'nmid6', 'nmid7', 'nmid8', 'nmid9', 'nmid10', 'perfid' ]
6889
+ modality_ids = [ 'T1wHierarchical', 'T1wHierarchicalSR', 'T1w', 'T2Flair', 'DTI', 'DTI', 'rsfMRI', 'rsfMRI', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'NM2DMT', 'perf']
6594
6890
  alldf=pd.DataFrame()
6595
6891
  for myk in sdf.index:
6596
6892
  if progress > 0 and int(myk) % int(progress) == 0:
@@ -7763,7 +8059,7 @@ def novelty_detection_quantile(df_train, df_test):
7763
8059
  return myqs
7764
8060
 
7765
8061
 
7766
- def brainmap_figure(statistical_df, data_dictionary_path, output_prefix, edge_image_path, overlay_cmap='bwr', nslices=21, ncol=7, edge_image_dilation = 0, verbose=False ):
8062
+ def brainmap_figure(statistical_df, data_dictionary_path, output_prefix, brain_image, overlay_cmap='bwr', nslices=21, ncol=7, edge_image_dilation = 0, black_bg=True, axes = [0,1,2], fixed_overlay_range=None, crop=True, verbose=False ):
7767
8063
  """
7768
8064
  Create figures based on statistical data and an underlying brain image.
7769
8065
 
@@ -7771,17 +8067,25 @@ def brainmap_figure(statistical_df, data_dictionary_path, output_prefix, edge_im
7771
8067
 
7772
8068
  Parameters:
7773
8069
  - statistical_df (pandas dataframe): with 2 columns named anat and value
8070
+ the anat column should have names that meet *partial matching* criterion
8071
+ with respect to regions that are measured in antspymm. value will be
8072
+ the value to be displayed. if two examples of a given region exist in
8073
+ statistical_df, then the largest absolute value will be taken for display.
7774
8074
  - data_dictionary_path (str): Path to the data dictionary CSV file.
7775
8075
  - output_prefix (str): Prefix for the output figure filenames.
7776
- - edge_image_path (str): Path to the edge image in NIfTI format.
8076
+ - brain_image (antsImage): the brain image on which results will overlay.
7777
8077
  - overlay_cmap (str): see matplotlib
7778
- - nslices: number of slices to show
7779
- - ncol: number of columns to show
7780
- - edge_image_dilation: integer greater than or equal to zero
7781
- - verbose: boolean
8078
+ - nslices (int): number of slices to show
8079
+ - ncol (int): number of columns to show
8080
+ - edge_image_dilation (int): integer greater than or equal to zero
8081
+ - black_bg (bool): boolean
8082
+ - axes (list): integer list typically [0,1,2] sagittal coronal axial
8083
+ - fixed_overlay_range (list): scalar pair will try to keep a constant cbar and will truncate the overlay at these min/max values
8084
+ - crop (bool): crops the image to display by the extent of the overlay
8085
+ - verbose (bool): boolean
7782
8086
 
7783
8087
  Returns:
7784
- values mapped to the associated regions
8088
+ an image with values mapped to the associated regions
7785
8089
  """
7786
8090
 
7787
8091
  # Read the statistical file
@@ -7792,7 +8096,7 @@ def brainmap_figure(statistical_df, data_dictionary_path, output_prefix, edge_im
7792
8096
  mydict = mydict[~mydict['Measurement'].str.contains("tractography-based connectivity", na=False)]
7793
8097
 
7794
8098
  # Load image and process it
7795
- edgeimg = ants.image_read(edge_image_path).iMath("Normalize")
8099
+ edgeimg = ants.iMath(brain_image,"Normalize")
7796
8100
  if edge_image_dilation > 0:
7797
8101
  edgeimg = ants.iMath( edgeimg, "MD", edge_image_dilation)
7798
8102
 
@@ -7873,16 +8177,381 @@ def brainmap_figure(statistical_df, data_dictionary_path, output_prefix, edge_im
7873
8177
 
7874
8178
  if verbose:
7875
8179
  print('Done Adding')
7876
- for axx in range(3):
8180
+ for axx in axes:
7877
8181
  figfn=output_prefix+f"fig{col2viz}ax{axx}_py.jpg"
7878
- cmask = ants.threshold_image( addemC,1e-5, 1e9 ).iMath("MD",3) + ants.threshold_image( addemC,-1e9, -1e-5 ).iMath("MD",3)
7879
- addemC = ants.crop_image( addem, cmask )
7880
- edgeimgC = ants.crop_image( edgeimg, cmask )
8182
+ if crop:
8183
+ cmask = ants.threshold_image( addem,1e-5, 1e9 ).iMath("MD",3) + ants.threshold_image( addem,-1e9, -1e-5 ).iMath("MD",3)
8184
+ addemC = ants.crop_image( addem, cmask )
8185
+ edgeimgC = ants.crop_image( edgeimg, cmask )
8186
+ else:
8187
+ addemC = addem
8188
+ edgeimgC = edgeimg
8189
+ if fixed_overlay_range is not None:
8190
+ addemC[0:3,0:3,0:3]=fixed_overlay_range[0]
8191
+ addemC[4:7,4:7,4:7]=fixed_overlay_range[1]
8192
+ addemC[ addemC < fixed_overlay_range[0] ] = fixed_overlay_range[0]
8193
+ addemC[ addemC > fixed_overlay_range[1] ] = fixed_overlay_range[1]
7881
8194
  ants.plot(edgeimgC, addemC, axis=axx, nslices=nslices, ncol=ncol,
7882
8195
  overlay_cmap=overlay_cmap, resample=False,
7883
- filename=figfn, cbar=True, crop=True, black_bg=True )
8196
+ filename=figfn, cbar=axx==axes[0], crop=True, black_bg=black_bg )
7884
8197
  if verbose:
7885
8198
  print(f"{col2viz} done")
7886
8199
  if verbose:
7887
8200
  print("DONE brain map figures")
7888
8201
  return addem
8202
+
8203
+
8204
+ def aggregate_antspymm_results(input_csv, subject_col='subjectID', date_col='date', image_col='imageID', date_column='ses-1', base_path="./Processed/ANTsExpArt/", hiervariable='T1wHierarchical', valid_modalities=None, verbose=False ):
8205
+ """
8206
+ Aggregate ANTsPyMM results from the specified CSV file and save the aggregated results to a new CSV file.
8207
+
8208
+ Parameters:
8209
+ - input_csv (str): File path of the input CSV file containing ANTsPyMM QC results averaged and with outlier measurements.
8210
+ - subject_col (str): Name of the column to store subject IDs.
8211
+ - date_col (str): Name of the column to store date information.
8212
+ - image_col (str): Name of the column to store image IDs.
8213
+ - date_column (str): Name of the column representing the date information.
8214
+ - base_path (str): Base path for search paths. Defaults to "./Processed/ANTsExpArt/".
8215
+ - hiervariable (str) : the string variable denoting the Hierarchical output
8216
+ - valid_modalities (str array) : identifies for each modality; if None will be replaced by get_valid_modalities(long=True)
8217
+ - verbose : boolean
8218
+
8219
+ Note:
8220
+ This function is tested under limited circumstances. Use with caution.
8221
+
8222
+ Example usage:
8223
+ agg_df = aggregate_antspymm_results("qcdfaol.csv", subject_col='subjectID', date_col='date', image_col='imageID', date_column='ses-1', base_path="./Your/Custom/Path/")
8224
+
8225
+ Author:
8226
+ Avants and ChatGPT
8227
+ """
8228
+ import pandas as pd
8229
+ import numpy as np
8230
+ from glob import glob
8231
+
8232
+ def filter_df( indf, myprefix ):
8233
+ nums = [isinstance(indf[col].iloc[0], (int, float)) for col in indf.columns]
8234
+ indf = indf.loc[:, nums]
8235
+ indf=indf.loc[:, indf.dtypes != 'object' ]
8236
+ indf = indf.loc[:, ~indf.columns.str.contains('Unnamed*', na=False, regex=True)]
8237
+ indf = pd.DataFrame(indf.mean(axis=0, skipna=True)).T
8238
+ indf = indf.add_prefix( myprefix )
8239
+ return( indf )
8240
+
8241
+ def myread_csv(x, cnms):
8242
+ """
8243
+ Reads a CSV file and returns a DataFrame excluding specified columns.
8244
+
8245
+ Parameters:
8246
+ - x (str): File path of the input CSV file describing the blind QC output
8247
+ - cnms (list): List of column names to exclude from the DataFrame.
8248
+
8249
+ Returns:
8250
+ pd.DataFrame: DataFrame with specified columns excluded.
8251
+ """
8252
+ df = pd.read_csv(x)
8253
+ return df.loc[:, ~df.columns.isin(cnms)]
8254
+
8255
+ import warnings
8256
+ # Warning message for untested function
8257
+ warnings.warn("Warning: This function is not well tested. Use with caution.")
8258
+
8259
+ if valid_modalities is None:
8260
+ valid_modalities = get_valid_modalities('long')
8261
+
8262
+ # Read the input CSV file
8263
+ df = pd.read_csv(input_csv)
8264
+
8265
+ # Filter rows where modality is 'T1w'
8266
+ df = df[df['modality'] == 'T1w']
8267
+ badnames = get_names_from_data_frame( ['Unnamed'], df )
8268
+ df=df.drop(badnames, axis=1)
8269
+
8270
+ # Add new columns for subject ID, date, and image ID
8271
+ df[subject_col] = np.nan
8272
+ df[date_col] = date_column
8273
+ df[image_col] = np.nan
8274
+ df = df.astype({subject_col: str, date_col: str, image_col: str })
8275
+
8276
+ # if verbose:
8277
+ # print( df.shape )
8278
+ # print( df.dtypes )
8279
+
8280
+ # prefilter df for data that exists
8281
+ keep = np.tile( False, df.shape[0] )
8282
+ for x in range(df.shape[0]):
8283
+ temp = df['fn'].iloc[x].split("_")
8284
+ # Generalized search paths
8285
+ path_template = f"{base_path}{temp[0]}/{date_column}/*/*/*"
8286
+ hierfn = sorted(glob( path_template + "-" + hiervariable + "-*wide.csv" ) )
8287
+ if len( hierfn ) > 0:
8288
+ keep[x]=True
8289
+
8290
+
8291
+ df=df[keep]
8292
+
8293
+ if verbose:
8294
+ print( "original input had shape " + str( df.shape[0] ) + " (T1 only) and we find " + str( (keep).sum() ) + " with hierarchical output defined by variable: " + hiervariable )
8295
+ print( df.shape )
8296
+
8297
+ myct = 0
8298
+ for x in range( df.shape[0]):
8299
+ if verbose:
8300
+ print(f"{x}...")
8301
+ locind = df.index[x]
8302
+ temp = df['fn'].iloc[x].split("_")
8303
+ if verbose:
8304
+ print( temp )
8305
+ df[subject_col].iloc[x]=temp[0]
8306
+ df[date_col].iloc[x]=date_column
8307
+ df[image_col].iloc[x]=temp[1]
8308
+
8309
+ # Generalized search paths
8310
+ path_template = f"{base_path}{temp[0]}/{date_column}/*/*/*"
8311
+ if verbose:
8312
+ print(path_template)
8313
+ hierfn = sorted(glob( path_template + "-" + hiervariable + "-*wide.csv" ) )
8314
+ if len( hierfn ) > 0:
8315
+ hdf=t1df=dtdf=rsdf=perfdf=nmdf=flairdf=None
8316
+ if verbose:
8317
+ print(hierfn)
8318
+ hdf = pd.read_csv(hierfn[0])
8319
+ badnames = get_names_from_data_frame( ['Unnamed'], hdf )
8320
+ hdf=hdf.drop(badnames, axis=1)
8321
+ nums = [isinstance(hdf[col].iloc[0], (int, float)) for col in hdf.columns]
8322
+ corenames = list(np.array(hdf.columns)[nums])
8323
+ hdf.loc[:, nums] = hdf.loc[:, nums].add_prefix("T1Hier_")
8324
+ myct = myct + 1
8325
+ dflist = [hdf]
8326
+
8327
+ for mymod in valid_modalities:
8328
+ t1wfn = sorted(glob( path_template+ "-" + mymod + "-*wide.csv" ) )
8329
+ if len( t1wfn ) > 0 :
8330
+ if verbose:
8331
+ print(t1wfn)
8332
+ t1df = myread_csv(t1wfn[0], corenames)
8333
+ t1df = filter_df( t1df, mymod+'_')
8334
+ dflist = dflist + [t1df]
8335
+
8336
+ hdf = pd.concat( dflist, axis=1)
8337
+ if verbose:
8338
+ print( df.loc[locind,'fn'] )
8339
+ if myct == 1:
8340
+ subdf = df.iloc[[x]]
8341
+ hdf.index = subdf.index.copy()
8342
+ df = pd.concat( [df,hdf], axis=1)
8343
+ else:
8344
+ commcols = list(set(hdf.columns).intersection(df.columns))
8345
+ df.loc[locind, commcols] = hdf.loc[0, commcols]
8346
+ badnames = get_names_from_data_frame( ['Unnamed'], df )
8347
+ df=df.drop(badnames, axis=1)
8348
+ return( df )
8349
+
8350
+ def aggregate_antspymm_results_sdf(
8351
+ study_df,
8352
+ project_col='projectID',
8353
+ subject_col='subjectID',
8354
+ date_col='date',
8355
+ image_col='imageID',
8356
+ base_path="./",
8357
+ hiervariable='T1wHierarchical',
8358
+ splitsep='-',
8359
+ idsep='-',
8360
+ wild_card_modality_id=False,
8361
+ verbose=False ):
8362
+ """
8363
+ Aggregate ANTsPyMM results from the specified study data frame and store the aggregated results in a new data frame. This assumes data is organized on disk
8364
+ as follows: rootdir/projectID/subjectID/date/outputid/imageid/ where
8365
+ outputid is modality-specific and created by ANTsPyMM processing.
8366
+
8367
+ Parameters:
8368
+ - study_df (pandas df): pandas data frame, output of generate_mm_dataframe.
8369
+ - project_col (str): Name of the column that stores the project ID
8370
+ - subject_col (str): Name of the column to store subject IDs.
8371
+ - date_col (str): Name of the column to store date information.
8372
+ - image_col (str): Name of the column to store image IDs.
8373
+ - base_path (str): Base path for searching for processing outputs of ANTsPyMM.
8374
+ - hiervariable (str) : the string variable denoting the Hierarchical output
8375
+ - splitsep (str): the separator used to split the filename
8376
+ - idsep (str): the separator used to partition subjectid date and imageid
8377
+ for example, if idsep is - then we have subjectid-date-imageid
8378
+ - wild_card_modality_id (bool): keep if False for safer execution
8379
+ - verbose : boolean
8380
+
8381
+ Note:
8382
+ This function is tested under limited circumstances. Use with caution.
8383
+
8384
+ Example usage:
8385
+ agg_df = aggregate_antspymm_results_sdf( studydf, subject_col='subjectID', date_col='date', image_col='imageID', base_path="./Your/Custom/Path/")
8386
+
8387
+ Author:
8388
+ Avants and ChatGPT
8389
+ """
8390
+ import pandas as pd
8391
+ import numpy as np
8392
+ from glob import glob
8393
+
8394
+ def filter_df( indf, myprefix ):
8395
+ nums = [isinstance(indf[col].iloc[0], (int, float)) for col in indf.columns]
8396
+ indf = indf.loc[:, nums]
8397
+ indf=indf.loc[:, indf.dtypes != 'object' ]
8398
+ indf = indf.loc[:, ~indf.columns.str.contains('Unnamed*', na=False, regex=True)]
8399
+ indf = pd.DataFrame(indf.mean(axis=0, skipna=True)).T
8400
+ indf = indf.add_prefix( myprefix )
8401
+ return( indf )
8402
+
8403
+ def myread_csv(x, cnms):
8404
+ """
8405
+ Reads a CSV file and returns a DataFrame excluding specified columns.
8406
+
8407
+ Parameters:
8408
+ - x (str): File path of the input CSV file describing the blind QC output
8409
+ - cnms (list): List of column names to exclude from the DataFrame.
8410
+
8411
+ Returns:
8412
+ pd.DataFrame: DataFrame with specified columns excluded.
8413
+ """
8414
+ df = pd.read_csv(x)
8415
+ return df.loc[:, ~df.columns.isin(cnms)]
8416
+
8417
+ import warnings
8418
+ # Warning message for untested function
8419
+ warnings.warn("Warning: This function is not well tested. Use with caution.")
8420
+
8421
+ # if valid_modalities is None:
8422
+ valid_modalities = get_valid_modalities('long')
8423
+ vmoddict = {}
8424
+ # Add key-value pairs
8425
+ vmoddict['imageID'] = 'T1w'
8426
+ vmoddict['flairid'] = 'T2Flair'
8427
+ vmoddict['perfid'] = 'perf'
8428
+ vmoddict['rsfid1'] = 'rsfMRI'
8429
+ vmoddict['dtid1'] = 'DTI'
8430
+ vmoddict['nmid1'] = 'NM2DMT'
8431
+
8432
+ # Filter rows where modality is 'T1w'
8433
+ df = study_df[study_df['modality'] == 'T1w']
8434
+ badnames = get_names_from_data_frame( ['Unnamed'], df )
8435
+ df=df.drop(badnames, axis=1)
8436
+ # prefilter df for data that exists
8437
+ keep = np.tile( False, df.shape[0] )
8438
+ for x in range(df.shape[0]):
8439
+ myfn = os.path.basename( df['filename'].iloc[x] )
8440
+ temp = myfn.split( splitsep )
8441
+ # Generalized search paths
8442
+ sid0 = temp[0]
8443
+ sid = str(df[subject_col].iloc[x])
8444
+ if sid0 != sid:
8445
+ warnings.warn("the id derived from the filename " + sid + " does not match the id stored in the data frame " + sid )
8446
+ myproj = str(df[project_col].iloc[x])
8447
+ mydate = str(df[date_col].iloc[x])
8448
+ myid = str(df[image_col].iloc[x])
8449
+ path_template = base_path + "/" + myproj + "/" + sid + "/" + mydate + '/' + hiervariable + '/' + str(myid) + "/"
8450
+ hierfn = sorted(glob( path_template + "*" + hiervariable + "*wide.csv" ) )
8451
+ if len( hierfn ) > 0:
8452
+ keep[x]=True
8453
+
8454
+ df=df[keep]
8455
+
8456
+ if not df.index.is_unique:
8457
+ warnings.warn("data frame does not have unique indices. we therefore reset the index to allow the function to continue on." )
8458
+ df = df.reset_index()
8459
+
8460
+
8461
+ if verbose:
8462
+ print( "original input had shape " + str( df.shape[0] ) + " (T1 only) and we find " + str( (keep).sum() ) + " with hierarchical output defined by variable: " + hiervariable )
8463
+ print( df.shape )
8464
+
8465
+ myct = 0
8466
+ for x in range( df.shape[0]):
8467
+ print("\n\n-------------------------------------------------")
8468
+ if verbose:
8469
+ print(f"{x}...")
8470
+ locind = df.index[x]
8471
+ myfn = os.path.basename( df['filename'].iloc[x] )
8472
+ sid = df[subject_col].iloc[x]
8473
+ if sid0 != sid:
8474
+ warnings.warn("the id derived from the filename " + sid + " does not match the id stored in the data frame " + sid )
8475
+ myproj = str(df[project_col].iloc[x])
8476
+ mydate = str(df[date_col].iloc[x])
8477
+ myid = str(df[image_col].iloc[x])
8478
+ if verbose:
8479
+ print( myfn )
8480
+ print( temp )
8481
+ print( "id " + sid )
8482
+ path_template = base_path + "/" + myproj + "/" + sid + "/" + mydate + '/' + hiervariable + '/' + str(myid) + "/"
8483
+ searchhier = path_template + "*" + hiervariable + "*wide.csv"
8484
+ if verbose:
8485
+ print( searchhier )
8486
+ hierfn = sorted( glob( searchhier ) )
8487
+ if len( hierfn ) > 1:
8488
+ raise ValueError("there are " + str( len( hierfn ) ) + " number of hier fns with search path " + searchhier )
8489
+ if len( hierfn ) == 1:
8490
+ hdf=t1df=dtdf=rsdf=perfdf=nmdf=flairdf=None
8491
+ if verbose:
8492
+ print(hierfn)
8493
+ hdf = pd.read_csv(hierfn[0])
8494
+ badnames = get_names_from_data_frame( ['Unnamed'], hdf )
8495
+ hdf=hdf.drop(badnames, axis=1)
8496
+ nums = [isinstance(hdf[col].iloc[0], (int, float)) for col in hdf.columns]
8497
+ corenames = list(np.array(hdf.columns)[nums])
8498
+ hdf.loc[:, nums] = hdf.loc[:, nums].add_prefix("T1Hier_")
8499
+ myct = myct + 1
8500
+ hdf = hdf.add_prefix( "T1Hier_" )
8501
+ dflist = [hdf]
8502
+
8503
+ for mymod in vmoddict.keys():
8504
+ print("\n\n************************* " + mymod + " *************************")
8505
+ modalityclass = vmoddict[ mymod ]
8506
+ if wild_card_modality_id:
8507
+ mymodid = '*'
8508
+ else:
8509
+ mymodid = str( df[mymod].iloc[x] )
8510
+ if mymodid.lower() != "nan" and mymodid.lower() != "na":
8511
+ mymodid = os.path.basename( mymodid )
8512
+ mymodid = os.path.splitext( mymodid )[0]
8513
+ mymodid = os.path.splitext( mymodid )[0]
8514
+ temp = mymodid.split( idsep )
8515
+ mymodid = temp[ len( temp )-1 ]
8516
+ else:
8517
+ print("missing")
8518
+ continue
8519
+ if verbose:
8520
+ print( "modality id is " + mymodid + " for modality " + modalityclass )
8521
+ modalityclasssearch = modalityclass
8522
+ if modalityclass in ['rsfMRI','DTI']:
8523
+ modalityclasssearch=modalityclass+"*"
8524
+ path_template_m = base_path + "/" + myproj + "/" + sid + "/" + mydate + '/' + modalityclasssearch + '/' + mymodid + "/"
8525
+ modsearch = path_template_m + "*" + modalityclasssearch + "*wide.csv"
8526
+ if verbose:
8527
+ print( modsearch )
8528
+ t1wfn = sorted( glob( modsearch ) )
8529
+ if len( t1wfn ) > 1:
8530
+ raise ValueError("there are " + str( len( t1wfn ) ) + " number of wide fns with search path " + modsearch )
8531
+ if len( t1wfn ) == 1:
8532
+ if verbose:
8533
+ print(t1wfn)
8534
+ t1df = myread_csv(t1wfn[0], corenames)
8535
+ t1df = filter_df( t1df, modalityclass+'_')
8536
+ dflist = dflist + [t1df]
8537
+ else:
8538
+ if verbose:
8539
+ print( " cannot find " + modsearch )
8540
+
8541
+ hdf = pd.concat( dflist, axis=1)
8542
+ if verbose:
8543
+ print( "count: " + str( myct ) )
8544
+ if myct == 1:
8545
+ subdf = df.iloc[[x]]
8546
+ hdf.index = subdf.index.copy()
8547
+ print( hdf.index )
8548
+ print( df.index )
8549
+ df = pd.concat( [df,hdf], axis=1)
8550
+ else:
8551
+ commcols = list(set(hdf.columns).intersection(df.columns))
8552
+ df.loc[locind, commcols] = hdf.loc[0, commcols]
8553
+ badnames = get_names_from_data_frame( ['Unnamed'], df )
8554
+ df=df.drop(badnames, axis=1)
8555
+ return( df )
8556
+
8557
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: antspymm
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: multi-channel/time-series medical image processing with antspyx
5
5
  Home-page: https://github.com/stnava/ANTsPyMM
6
6
  Author: Avants, Gosselin, Tustison, Reardon
@@ -18,7 +18,7 @@ Requires-Dist: dipy
18
18
  Requires-Dist: nibabel
19
19
  Requires-Dist: scipy
20
20
  Requires-Dist: siq
21
- Requires-Dist: sklearn
21
+ Requires-Dist: scikit-learn
22
22
 
23
23
  # ANTsPyMM
24
24
 
@@ -246,6 +246,13 @@ studycsv = antspymm.generate_mm_dataframe(
246
246
  rsf_filenames=[fns[2]])
247
247
  studycsv2 = studycsv.dropna(axis=1)
248
248
  mmrun = antspymm.mm_csv( studycsv2, mysep='_' )
249
+
250
+ # aggregate the data after you've run on many subjects
251
+ # studycsv_all would be the vstacked studycsv2 data frames
252
+ zz=antspymm.aggregate_antspymm_results_sdf( studycsv_all,
253
+ subject_col='subjectID', date_col='date', image_col='imageID', base_path=bd,
254
+ splitsep='_', idsep='-', wild_card_modality_id=True, verbose=True)
255
+
249
256
  ```
250
257
 
251
258
  ## NRG example
@@ -429,6 +436,15 @@ for f in folders:
429
436
  pdoc -o ./docs antspymm --html
430
437
  ```
431
438
 
439
+ ## ssl error
440
+
441
+ if you get an odd certificate error when calling `force_download`, try:
442
+
443
+ ```python
444
+ import ssl
445
+ ssl._create_default_https_context = ssl._create_unverified_context
446
+ ``
447
+
432
448
  ## to publish a release
433
449
 
434
450
  ```
@@ -0,0 +1,7 @@
1
+ antspymm/__init__.py,sha256=wck10IhO1ZFxNqz4bTIHFP7cqYosRKBI8bKIsZzRwfs,3166
2
+ antspymm/mm.py,sha256=sYXNst5xri6vFDzvuqDB6avuwkKoq3GTEsvm0RLUuPU,347923
3
+ antspymm-1.1.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
4
+ antspymm-1.1.3.dist-info/METADATA,sha256=bFp7u3Bd85Sq4Nc_TvvzxfYDhyU9Uxj4T9yzDcDK26I,14153
5
+ antspymm-1.1.3.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
6
+ antspymm-1.1.3.dist-info/top_level.txt,sha256=iyD1sRhCKzfwKRJLq5ZUeV9xsv1cGQl8Ejp6QwXM1Zg,9
7
+ antspymm-1.1.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.3)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,7 +0,0 @@
1
- antspymm/__init__.py,sha256=F7Dha4OCqkM7QhDCKXpO1fBHTXpUqxpIfDAn-NJG0r4,3045
2
- antspymm/mm.py,sha256=bcB6EGrAiaqbfdccTv2dG449Q_qqPFx4D6Cjq05knUc,319706
3
- antspymm-1.1.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
4
- antspymm-1.1.2.dist-info/METADATA,sha256=hamN7KOuQ0A6hOpABVGdR93kHtw56LwFfe1NyRm9ABc,13639
5
- antspymm-1.1.2.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
6
- antspymm-1.1.2.dist-info/top_level.txt,sha256=iyD1sRhCKzfwKRJLq5ZUeV9xsv1cGQl8Ejp6QwXM1Zg,9
7
- antspymm-1.1.2.dist-info/RECORD,,