@@ -264,8 +264,8 @@ def parcels_to_vertices(data, *, lhannot, rhannot, drop=None):
264264 drop : list, optional
265265 Specifies regions in {lh,rh}annot that are not present in `data`. NaNs
266266 will be inserted in place of the these regions in the returned data. If
267- not specified, 'unknown' and 'corpuscallosum' are assumed to not be
268- present. Default: None
267+ not specified, parcels defined in `netneurotools.freesurfer.FSIGNORE`
268+ are assumed to not be present. Default: None
269269
270270 Reurns
271271 ------
@@ -274,10 +274,7 @@ def parcels_to_vertices(data, *, lhannot, rhannot, drop=None):
274274 """
275275
276276 if drop is None :
277- drop = [
278- 'unknown' , 'corpuscallosum' , # default FreeSurfer
279- 'Background+FreeSurfer_Defined_Medial_Wall' # common alternative
280- ]
277+ drop = FSIGNORE
281278 drop = _decode_list (drop )
282279
283280 data = np .vstack (data )
@@ -335,8 +332,9 @@ def vertices_to_parcels(data, *, lhannot, rhannot, drop=None):
335332 hemisphere
336333 drop : list, optional
337334 Specifies regions in {lh,rh}annot that should be removed from the
338- parcellated version of `data`. If not specified, 'unknown' and
339- 'corpuscallosum' will be removed. Default: None
335+ parcellated version of `data`. If not specified, vertices corresponding
336+ to parcels defined in `netneurotools.freesurfer.FSIGNORE` will be
337+ removed. Default: None
340338
341339 Reurns
342340 ------
@@ -345,10 +343,7 @@ def vertices_to_parcels(data, *, lhannot, rhannot, drop=None):
345343 """
346344
347345 if drop is None :
348- drop = [
349- 'unknown' , 'corpuscallosum' , # default FreeSurfer
350- 'Background+FreeSurfer_Defined_Medial_Wall' # common alternative
351- ]
346+ drop = FSIGNORE
352347 drop = _decode_list (drop )
353348
354349 data = np .vstack (data )
@@ -439,6 +434,60 @@ def _get_fsaverage_coords(version='fsaverage', surface='sphere'):
439434 return np .row_stack (coords ), np .hstack (hemi )
440435
441436
437+ def _get_fsaverage_spins (version = 'fsaverage' , spins = None , n_rotate = 1000 ,
438+ seed = None , verbose = False , return_cost = False ):
439+ """
440+ Generates spatial permutation resamples for fsaverage `version`
441+
442+ If `spins` are provided then performs checks to confirm they are valid
443+
444+ Parameters
445+ ----------
446+ version : str, optional
447+ Specifies which version of `fsaverage` for which to generate spins.
448+ Must be one of {'fsaverage', 'fsaverage3', 'fsaverage4', 'fsaverage5',
449+ 'fsaverage6'}. Default: 'fsaverage'
450+ spins : array_like, optional
451+ Pre-computed spins to use instead of generating them on the fly. If not
452+ provided will use other provided parameters to create them. Default:
453+ None
454+ n_rotate : int, optional
455+ Number of rotations to generate. Default: 1000
456+ seed : {int, np.random.RandomState instance, None}, optional
457+ Seed for random number generation. Default: None
458+ verbose : bool, optional
459+ Whether to print occasional status messages. Default: False
460+ return_cost : bool, optional
461+ Whether to return cost array (specified as Euclidean distance) for each
462+ coordinate for each rotation. Currently this option is not supported if
463+ pre-computed `spins` are provided. Default: True
464+
465+ Returns
466+ --------
467+ spins : (N, S) numpy.ndarray
468+ Resampling array
469+ """
470+
471+ if spins is None :
472+ coords , hemiid = _get_fsaverage_coords (version , 'sphere' )
473+ spins , cost = gen_spinsamples (coords , hemiid , n_rotate = n_rotate ,
474+ seed = seed , verbose = verbose )
475+ if return_cost :
476+ return spins , cost
477+
478+ spins = np .asarray (spins , dtype = 'int32' )
479+ if spins .shape [- 1 ] != n_rotate :
480+ warnings .warn ('Shape of provided `spins` array does not match '
481+ 'number of rotations requested with `n_rotate`. '
482+ 'Ignoring specified `n_rotate` parameter and using '
483+ 'all provided `spins`.' )
484+ n_rotate = spins .shape [- 1 ]
485+ if return_cost :
486+ raise ValueError ('Cannot `return_cost` when `spins` are provided.' )
487+
488+ return spins , None
489+
490+
442491def spin_data (data , * , lhannot , rhannot , version = 'fsaverage' , n_rotate = 1000 ,
443492 spins = None , drop = None , seed = None , verbose = False ,
444493 return_cost = False ):
@@ -473,8 +522,8 @@ def spin_data(data, *, lhannot, rhannot, version='fsaverage', n_rotate=1000,
473522 drop : list, optional
474523 Specifies regions in {lh,rh}annot that are not present in `data`. NaNs
475524 will be inserted in place of the these regions in the returned data. If
476- not specified, 'unknown' and 'corpuscallosum' are assumed to not be
477- present. Default: None
525+ not specified, parcels defined in `netneurotools.freesurfer.FSIGNORE`
526+ are assumed to not be present. Default: None
478527 seed : {int, np.random.RandomState instance, None}, optional
479528 Seed for random number generation. Default: None
480529 verbose : bool, optional
@@ -495,41 +544,23 @@ def spin_data(data, *, lhannot, rhannot, version='fsaverage', n_rotate=1000,
495544 """
496545
497546 if drop is None :
498- drop = [
499- 'unknown' , 'corpuscallosum' , # default FreeSurfer
500- 'Background+FreeSurfer_Defined_Medial_Wall' # common alternative
501- ]
547+ drop = FSIGNORE
502548
503549 # get coordinates and hemisphere designation for spin generation
504550 vertices = parcels_to_vertices (data , lhannot = lhannot , rhannot = rhannot ,
505551 drop = drop )
506552
507- if spins is None :
508- coords , hemiid = _get_fsaverage_coords (version , 'sphere' )
509- if len (vertices ) != len (coords ):
510- raise ValueError ('Provided annotation files have a different '
511- 'number of vertices than the specified fsaverage '
512- 'surface.\n ANNOTATION: {} vertices\n '
513- 'FSAVERAGE: {} vertices'
514- .format (len (vertices ), len (coords )))
515- spins , cost = gen_spinsamples (coords , hemiid , n_rotate = n_rotate ,
516- seed = seed , verbose = verbose )
517- else :
518- spins = np .asarray (spins , dtype = 'int32' )
519- if len (spins ) != len (vertices ):
520- raise ValueError ('Provided `spins` array has a different number '
521- 'of vertices than the provided annotation files.'
522- '\n ANNOTATION: {} vertices\n SPINS: '
523- '{} vertices\n '
524- .format (len (vertices ), len (spins )))
525- if spins .shape [- 1 ] != n_rotate :
526- warnings .warn ('Shape of provided `spins` array does not match '
527- 'number of rotations requested with `n_rotate`. '
528- 'Ignoring specified `n_rotate` parameter and using '
529- 'all provided `spins`.' )
530- n_rotate = spins .shape [- 1 ]
531- if return_cost :
532- raise ValueError ('Cannot `return_cost` when `spins` are provided.' )
553+ # get spins + cost (if requested)
554+ spins , cost = _get_fsaverage_spins (version = version , spins = spins ,
555+ n_rotate = n_rotate ,
556+ seed = seed , verbose = verbose ,
557+ return_cost = return_cost )
558+ if len (vertices ) != len (spins ):
559+ raise ValueError ('Provided annotation files have a different '
560+ 'number of vertices than the specified fsaverage '
561+ 'surface.\n ANNOTATION: {} vertices\n '
562+ 'FSAVERAGE: {} vertices'
563+ .format (len (vertices ), len (spins )))
533564
534565 spun = np .zeros (data .shape + (n_rotate ,))
535566 for n in range (n_rotate ):
@@ -550,7 +581,8 @@ def spin_data(data, *, lhannot, rhannot, version='fsaverage', n_rotate=1000,
550581
551582
552583def spin_parcels (* , lhannot , rhannot , version = 'fsaverage' , n_rotate = 1000 ,
553- drop = None , seed = None , return_cost = False , ** kwargs ):
584+ spins = None , drop = None , seed = None , verbose = False ,
585+ return_cost = False ):
554586 """
555587 Rotates parcels in `{lh,rh}annot` and re-assigns based on maximum overlap
556588
@@ -569,16 +601,22 @@ def spin_parcels(*, lhannot, rhannot, version='fsaverage', n_rotate=1000,
569601 'fsaverage5', 'fsaverage6'}. Default: 'fsaverage'
570602 n_rotate : int, optional
571603 Number of rotations to generate. Default: 1000
604+ spins : array_like, optional
605+ Pre-computed spins to use instead of generating them on the fly. If not
606+ provided will use other provided parameters to create them. Default:
607+ None
572608 drop : list, optional
573609 Specifies regions in {lh,rh}annot that are not present in `data`. NaNs
574610 will be inserted in place of the these regions in the returned data. If
575- not specified, 'unknown' and 'corpuscallosum' are assumed to not be
576- present. Default: None
611+ not specified, parcels defined in `netneurotools.freesurfer.FSIGNORE`
612+ are assumed to not be present. Default: None
613+ seed : {int, np.random.RandomState instance, None}, optional
614+ Seed for random number generation. Default: None
615+ verbose : bool, optional
616+ Whether to print occasional status messages. Default: False
577617 return_cost : bool, optional
578618 Whether to return cost array (specified as Euclidean distance) for each
579- coordinate for each rotation Default: True
580- kwargs : key-value, optional
581- Key-value pairs passed to :func:`netneurotools.stats.gen_spinsamples`
619+ coordinate for each rotation. Default: True
582620
583621 Returns
584622 -------
@@ -604,10 +642,7 @@ def overlap(vals):
604642 return - 1
605643
606644 if drop is None :
607- drop = [
608- 'unknown' , 'corpuscallosum' , # default FreeSurfer
609- 'Background+FreeSurfer_Defined_Medial_Wall' # common alternative
610- ]
645+ drop = FSIGNORE
611646 drop = _decode_list (drop )
612647
613648 # get vertex-level labels (set drop labels to - values)
@@ -625,19 +660,24 @@ def overlap(vals):
625660 labels = np .unique (vertices )
626661 mask = labels > - 1
627662
628- # get coordinates and hemisphere designation for spin generation
629- coords , hemiid = _get_fsaverage_coords (version , 'sphere' )
630- if len (vertices ) != len (coords ):
631- raise ValueError ('Provided annotation files have a different number '
632- 'of vertices than the specified fsaverage surface.\n '
633- ' ANNOTATION: {} vertices\n '
634- ' FSAVERAGE: {} vertices'
635- .format (len (vertices ), len (coords )))
663+ # get spins + cost (if requested)
664+ spins , cost = _get_fsaverage_spins (version = version , spins = spins ,
665+ n_rotate = n_rotate ,
666+ seed = seed , verbose = verbose ,
667+ return_cost = return_cost )
668+ if len (vertices ) != len (spins ):
669+ raise ValueError ('Provided annotation files have a different '
670+ 'number of vertices than the specified fsaverage '
671+ 'surface.\n ANNOTATION: {} vertices\n '
672+ 'FSAVERAGE: {} vertices'
673+ .format (len (vertices ), len (spins )))
636674
637675 # spin and assign regions based on max overlap
638- spins , cost = gen_spinsamples (coords , hemiid , n_rotate = n_rotate , ** kwargs )
639676 regions = np .zeros ((len (labels [mask ]), n_rotate ), dtype = 'int32' )
640677 for n in range (n_rotate ):
678+ if verbose :
679+ msg = f'Calculating parcel overlap: { n :>5} /{ n_rotate } '
680+ print (msg , end = '\b ' * len (msg ), flush = True )
641681 regions [:, n ] = labeled_comprehension (vertices [spins [:, n ]], vertices ,
642682 labels , overlap , int , - 1 )[mask ]
643683
0 commit comments