Save cifti after changing size, stuck generating new header

Hello!

I have a cifti file from a task-based fmri run that I then did a GLM analysis on and generated z-scores based on a contrast. The initial cifti file was (207,91k) in dimension (207 TRs), but my new z-score np array has dimensions of (91k,).

I want to save the z-score array as a cifti file so I can use it in a downstream human connectome workbench script (Connectome - Workbench Commands).

When I try to run the below, I get an expected error but I can’t get past it. The error is due to not having a header file of the right size, but I got stuck on how to generate a new header.

import nibabel as nib

#z is the z-scored value of each greyordinate, a np array of shape (91282,) 

img = nib.Cifti2Image(z) #this throws a warning since I don't have a header
img.to_filename('roi.dtseries.nii')

ValueError                                Traceback (most recent call last)
Cell In[182], line 2
      1 img = nib.Cifti2Image(z)
----> 2 img.to_filename('roi.dtseries.nii')

File /om2/user/rfbrito/miniconda/envs/imaging/lib/python3.11/site-packages/nibabel/filebasedimages.py:302, in FileBasedImage.to_filename(self, filename, **kwargs)
    286 r"""Write image to files implied by filename string
    287 
    288 Parameters
   (...)
    299 None
    300 """
    301 self.file_map = self.filespec_to_file_map(filename)
--> 302 self.to_file_map(**kwargs)

File /om2/user/rfbrito/miniconda/envs/imaging/lib/python3.11/site-packages/nibabel/cifti2/cifti2.py:1552, in Cifti2Image.to_file_map(self, file_map, dtype)
   1550 header.extensions.append(extension)
   1551 if self._dataobj.shape != self.header.matrix.get_data_shape():
-> 1552     raise ValueError(
   1553         f'Dataobj shape {self._dataobj.shape} does not match shape '
   1554         f'expected from CIFTI-2 header {self.header.matrix.get_data_shape()}'
   1555     )
   1556 # if intent code is not set, default to unknown CIFTI
   1557 if header.get_intent()[0] == 'none':

ValueError: Dataobj shape (91282,) does not match shape expected from CIFTI-2 header ()

My issue is I don’t know how to make/provide a header since z is a different shape than any cifti I have.

I realize from this reply by @effigies I can get the time and brain model axes out e.g. if I do so on my cifti file (if I try to modify its header):

# is a <nibabel.cifti2.cifti2.Cifti2Image at 0x2b3ae2173f10>

time_axis, brain_model_axis = [c.header.get_axis(i) for i in range(c.ndim)]

But I got stuck on how to propely modify time_axis.

Thanks for your help!

Rahul

You don’t want to modify a time axis, as you no longer have a time series. Now you want a scalar axis:

scalar_axis = nib.cifti2.ScalarAxis(['zscore'])  # Takes a list of names, one per row
new_header = nib.Cifti2Header.from_axes([scalar_axis, brain_model_axis])

Here’s an example recently written you might use as a model:

this fitlins function should help: fitlins/nistats.py at ec655c7ec71d92cc81141467bdb44e2bcc253425 · poldracklab/fitlins · GitHub

No, it has to be gratuitously long and include irrelevant bits to sift through.

1 Like

Thanks for the super quick reply Chris! That makes sense and worked!

Note for those who might read this later I had to reshape my (91K,) array into a (1,91K) array otherwise it complained the dimensions did not match

e.g.

z = np.reshape(z, (-1, z.shape[0]))

LOL, gotta teach a programmer how to fish right

1 Like

In one I used atleast_2d(), which will turn a vector into a row vector. In the other, it looks like I used header.matrix.get_data_shape(). Both should work.

1 Like