Skip to content

surface_genetic_expression is broken with current versions of  #351

Closed

Description

@araikes
  • What is the current behavior?
    Following the tutorial using BrainStat in a Jupyter Notebook running through Singularity. In the tutorial, you are supposed to be able to fetch gene expression using surface_genetic_expression. However, this is broken currently:
2024-06-04 10:34:06.077 (36615.547s) [        C8222740] vtkPythonAlgorithm.cxx:97     ERR| vtkPythonAlgorithm (0x56267e036dd0): Failure when calling method: "ProcessRequest":

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File /opt/conda/lib/python3.11/site-packages/vtk-9.3.20230807rc0-py3.11-linux-x86_64.egg/vtkmodules/util/vtkAlgorithm.py:152, in VTKPythonAlgorithmBase.InternalAlgorithm.ProcessRequest(self, vtkself, request, inInfo, outInfo)
    151 def ProcessRequest(self, vtkself, request, inInfo, outInfo):
--> 152     return vtkself.ProcessRequest(request, inInfo, outInfo)

File /opt/conda/lib/python3.11/site-packages/vtk-9.3.20230807rc0-py3.11-linux-x86_64.egg/vtkmodules/util/vtkAlgorithm.py:198, in VTKPythonAlgorithmBase.ProcessRequest(self, request, inInfo, outInfo)
    196     return self.RequestUpdateExtent(request, inInfo, outInfo)
    197 elif request.Has(vtkDemandDrivenPipeline.REQUEST_DATA()):
--> 198     return self.RequestData(request, inInfo, outInfo)
    200 return 1

File /opt/conda/lib/python3.11/site-packages/brainspace-0.1.10-py3.11.egg/brainspace/vtk_interface/io_support/gifti_support.py:123, in vtkGIFTIWriter.RequestData(self, request, inInfo, outInfo)
    122 def RequestData(self, request, inInfo, outInfo):
--> 123     _write_gifti(vtkPolyData.GetData(inInfo[0], 0), self.__FileName)
    124     return 1

File /opt/conda/lib/python3.11/site-packages/brainspace-0.1.10-py3.11.egg/brainspace/vtk_interface/decorators.py:41, in wrap_input.<locals>._wrapper_decorator.<locals>._wrapper_wrap(*args, **kwds)
     38 @functools.wraps(func)
     39 def _wrapper_wrap(*args, **kwds):
     40     args, kwds = _wrap_input_data(args, kwds, *xargs, skip=skip)
---> 41     data = func(*args, **kwds)
     42     return data

File /opt/conda/lib/python3.11/site-packages/brainspace-0.1.10-py3.11.egg/brainspace/vtk_interface/io_support/gifti_support.py:61, in _write_gifti(pd, opth)
     58 if not pd.has_only_triangle:
     59     raise ValueError('GIFTI writer only accepts triangles.')
---> 61 points = GiftiDataArray(data=pd.Points, intent=INTENT_POINTS)
     62 cells = GiftiDataArray(data=pd.GetCells2D(), intent=INTENT_CELLS)
     63 # if data is not None:
     64 #     data_array = GiftiDataArray(data=data, intent=INTENT_POINTDATA)
     65 #     gii = nb.gifti.GiftiImage(darrays=[points, cells, data_array])
     66 # else:

File /opt/conda/lib/python3.11/site-packages/nibabel-5.2.1-py3.11.egg/nibabel/gifti/gifti.py:476, in GiftiDataArray.__init__(self, data, intent, datatype, encoding, endian, coordsys, ordering, meta, ext_fname, ext_offset)
    474         datatype = self.data.dtype
    475     else:
--> 476         raise ValueError(
    477             f'Data array has type {self.data.dtype}. '
    478             'The GIFTI standard only supports uint8, int32 and float32 arrays.\n'
    479             'Explicitly cast the data array to a supported dtype or pass an '
    480             'explicit "datatype" parameter to GiftiDataArray().'
    481         )
    482 self.datatype = data_type_codes.code[datatype]
    483 self.encoding = gifti_encoding_codes.code[encoding]

ValueError: Data array has type float64. The GIFTI standard only supports uint8, int32 and float32 arrays.
Explicitly cast the data array to a supported dtype or pass an explicit "datatype" parameter to GiftiDataArray().

2024-06-04 10:34:06.107 (36615.577s) [        C8222740]       vtkExecutive.cxx:729    ERR| vtkCompositeDataPipeline (0x56267e246d40): Algorithm vtkPythonAlgorithm (0x56267e036dd0) returned failure for request: vtkInformation (0x56266eec1ac0)
  Debug: Off
  Modified Time: 80451
  Reference Count: 2
  Registered Events: (none)
  Request: REQUEST_DATA
  FROM_OUTPUT_PORT: -1
  ALGORITHM_AFTER_FORWARD: 1
  FORWARD_DIRECTION: 0


---------------------------------------------------------------------------
ImageFileError                            Traceback (most recent call last)
Cell In[72], line 4
      2 schaefer_200_fs5 = fetch_parcellation("fsaverage5", "schaefer", 200)
      3 surfaces = fetch_template_surface("fsaverage5", join=False)
----> 4 expression = surface_genetic_expression(schaefer_200_fs5, surfaces, space="fsaverage5")

File /opt/conda/lib/python3.11/site-packages/brainstat/context/genetics.py:100, in surface_genetic_expression(labels, surfaces, space, atlas_info, ibf_threshold, probe_selection, donor_probes, lr_mirror, missing, tolerance, sample_norm, gene_norm, norm_matched, norm_structures, region_agg, agg_metric, corrected_mni, reannotated, return_counts, return_donors, return_report, donors, data_dir, verbose, n_proc)
     98         name = f.name
     99         write_surface(surface, name, otype="gii")
--> 100     surfaces_gii.append(nib.load(name))
    101 finally:
    102     Path(name).unlink()

File /opt/conda/lib/python3.11/site-packages/nibabel-5.2.1-py3.11.egg/nibabel/loadsave.py:104, in load(filename, **kwargs)
    102     raise FileNotFoundError(f"No such file or no access: '{filename}'")
    103 if stat_result.st_size <= 0:
--> 104     raise ImageFileError(f"Empty file: '{filename}'")
    106 sniff = None
    107 for image_klass in all_image_classes:

ImageFileError: Empty file: '/tmp/tmp6aalcznf.gii

Activity

  1. RicardoRyn commented on Jul 29, 2024

    @RicardoRyn

    I encountered the same issue.

    • python 3.11
    • brainstat 0.4.2
    • brainspace 0.1.10
    • abagen 0.1.3
    • nibabel 5.2.0
    • vtk 9.3.1

    This is my code:

    from brainstat.context.genetics import surface_genetic_expression
    from nilearn import datasets
    import numpy as np
    
    
    destrieux = datasets.fetch_atlas_surf_destrieux()
    labels = np.hstack((destrieux['map_left'], destrieux['map_right']))
    fsaverage = datasets.fetch_surf_fsaverage()
    surfaces = (fsaverage['pial_left'], fsaverage['pial_right'])
    expression = surface_genetic_expression(labels, surfaces, space='fsaverage')

    And this is error:

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\images.py:352, in check_atlas(atlas, atlas_info, geometry, space, donor, data_dir)
        351 try:
    --> 352     atlas = check_img(atlas)
        353     coords = triangles = None
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\images.py:218, in check_img(img)
        217 elif not isinstance(img, nib.spatialimages.SpatialImage):
    --> 218     raise TypeError('Provided image must be an existing filepath or a '
        219                     'pre-loaded niimg-like object')
        221 # ensure 3D or squeezable to 3D
    
    TypeError: Provided image must be an existing filepath or a pre-loaded niimg-like object
    
    During handling of the above exception, another exception occurred:
    
    TypeError                                 Traceback (most recent call last)
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\images.py:419, in check_geometry(surface, space, donor, data_dir)
        418 try:
    --> 419     coords, triangles = map(list, zip(*[
        420         load_gifti(img).agg_data() for img in surface
        421     ]))
        422 except TypeError:
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\images.py:420, in <listcomp>(.0)
        418 try:
        419     coords, triangles = map(list, zip(*[
    --> 420         load_gifti(img).agg_data() for img in surface
        421     ]))
        422 except TypeError:
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\utils.py:220, in load_gifti(img)
        217     elif (isinstance(err, TypeError)
        218           and not str(err) == 'stat: path should be string, bytes, os.'
        219                               'PathLike or integer, not GiftiImage'):
    --> 220         raise err
        222 return img
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\utils.py:210, in load_gifti(img)
        209 try:
    --> 210     img = nib.load(img)
        211 except (ImageFileError, TypeError) as err:
        212     # it's gzipped, so read the gzip and pipe it in
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\nibabel\loadsave.py:96, in load(filename, **kwargs)
         82 r"""Load file given filename, guessing at file type
         83 
         84 Parameters
       (...)
         94    Image of guessed type
         95 """
    ---> 96 filename = _stringify_path(filename)
         98 # Check file exists and is not empty
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\nibabel\filename_parser.py:41, in _stringify_path(filepath_or_buffer)
         26 """Attempt to convert a path-like object to a string.
         27 
         28 Parameters
       (...)
         39 https://github.com/pandas-dev/pandas/blob/325dd68/pandas/io/common.py#L131-L160
         40 """
    ---> 41 return pathlib.Path(filepath_or_buffer).expanduser().as_posix()
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\pathlib.py:871, in Path.__new__(cls, *args, **kwargs)
        870     cls = WindowsPath if os.name == 'nt' else PosixPath
    --> 871 self = cls._from_parts(args)
        872 if not self._flavour.is_supported:
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\pathlib.py:509, in PurePath._from_parts(cls, args)
        508 self = object.__new__(cls)
    --> 509 drv, root, parts = self._parse_args(args)
        510 self._drv = drv
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\pathlib.py:493, in PurePath._parse_args(cls, args)
        492 else:
    --> 493     a = os.fspath(a)
        494     if isinstance(a, str):
        495         # Force-cast str subclasses to str (issue #21127)
    
    TypeError: expected str, bytes or os.PathLike object, not GiftiImage
    
    During handling of the above exception, another exception occurred:
    
    TypeError                                 Traceback (most recent call last)
    Cell In[8], line 10
          8 fsaverage = datasets.fetch_surf_fsaverage()
          9 surfaces = (fsaverage['pial_left'], fsaverage['pial_right'])
    ---> 10 expression = surface_genetic_expression(labels, surfaces, space='fsaverage')
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\brainstat\context\genetics.py:110, in surface_genetic_expression(labels, surfaces, space, atlas_info, ibf_threshold, probe_selection, donor_probes, lr_mirror, missing, tolerance, sample_norm, gene_norm, norm_matched, norm_structures, region_agg, agg_metric, corrected_mni, reannotated, return_counts, return_donors, return_report, donors, data_dir, verbose, n_proc)
        106 # Use abagen to grab expression data.
        107 logger.info(
        108     "If you use BrainStat's genetics functionality, please cite abagen (https://abagen.readthedocs.io/en/stable/citing.html)."
        109 )
    --> 110 atlas = check_atlas(labels, geometry=surfaces_gii, space=space)
        111 expression = get_expression_data(
        112     atlas,
        113     atlas_info=atlas_info,
       (...)
        134     n_proc=n_proc,
        135 )
        137 return expression
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\images.py:366, in check_atlas(atlas, atlas_info, geometry, space, donor, data_dir)
        363 elif geometry is not None and space is None:
        364     raise ValueError('If providing geometry files space parameter '
        365                      'must be specified')
    --> 366 coords, triangles = check_geometry(geometry, space, donor=donor,
        367                                    data_dir=data_dir)
        368 if atlas_info is None and info is not None:
        369     atlas_info = info
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\abagen\images.py:423, in check_geometry(surface, space, donor, data_dir)
        419     coords, triangles = map(list, zip(*[
        420         load_gifti(img).agg_data() for img in surface
        421     ]))
        422 except TypeError:
    --> 423     coords, triangles = map(list, zip(*[i for i in surface]))
        425 triangles[-1] += coords[0].shape[0]
        426 coords, triangles = np.row_stack(coords), np.row_stack(triangles)
    
    File e:\jupyter_notebook\jupyter_notebook_env\RJX\Lib\site-packages\nibabel\filebasedimages.py:206, in FileBasedImage.__getitem__(self, key)
        204 def __getitem__(self, key) -> None:
        205     """No slicing or dictionary interface for images"""
    --> 206     raise TypeError('Cannot slice image objects.')
    
    TypeError: Cannot slice image objects.
  2. daisyxyfamily commented on Sep 11, 2024

    @daisyxyfamily

    I got the same error of araikes's. Anybody solved this problem?

  3. self-assigned this
    on Oct 22, 2024
  4. zihuaihuai commented on Oct 22, 2024

    @zihuaihuai
    Collaborator

    this bug is because the new check atlas function in abagen has a little bug
    rmarkello/abagen#235

  5. added a commit that references this issue on Nov 25, 2025
    7a9c11c
  6. zihuaihuai commented on Nov 25, 2025

    @zihuaihuai
    Collaborator

    Summary

    Fixed the float64 to float32 dtype issue that was causing GIFTI writing to fail.

    Changes

    • Cast surface Points to float32 before writing to GIFTI format
    • Added dtype check to avoid unnecessary conversions
    • Make a copy of the surface to avoid modifying the original data

    Why

    The surface_genetic_expression function was failing because surface coordinate data was in float64 format, but the GIFTI standard only supports uint8, int32, and float32 datatypes. This caused a ValueError when writing surfaces to GIFTI format.

    Testing

    The fix ensures that surface data is properly converted to GIFTI-compatible format before writing.

    Branch

    351-surface_genetic_expression-is-broken-with-current-versions-of


  7. added a commit that references this issue on Nov 26, 2025
  8. zihuaihuai commented on Nov 26, 2025

    @zihuaihuai
    Collaborator

    Also fixed compatibility issues with abagen 0.1.3 running on Python 3.13 with pandas 2.x and nibabel 5.x.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions