Utils

Utility functions

class linescanning.utils.VertexInfo(infofile=None, subject=None, hemi='lh')[source]

This object reads a .csv file containing relevant information about the angles, vertex position, and normal vector.

Parameters:
  • infofile (str) – path to the information file containing best_vertices in the filename

  • subject (str) – subject ID as used in SUBJECTS_DIR

Returns:

sets attributes in the class

Return type:

attr

get(keyword, hemi='both')[source]

return values from dataframe given keyword. Can be any column name or ‘prf’ for pRF-parameters

linescanning.utils.ants_to_spm_moco(affine, deg=False, convention='SPM')[source]

SPM output = x [LR], y [AP], z [SI], rx, ry, rz. ANTs employs an LPS system, so y value should be switched

linescanning.utils.assemble_fmriprep_wf(bold_path, wf_only=False)[source]

Parses the bold file into a workflow name for fMRIPrep into its constituents to recreate a filename. Searches for the following keys: [‘ses’, ‘task’, ‘acq’, ‘run’].

Parameters:
  • bold_path (str) – Path to bold-file

  • wf_only (bool, optional) – If sub tag is found in bold_path, we can reconstruct the full workflow folder including preceding single_subject_<sub_id>_wf. If you do not want this, set wf_only to False.

Returns:

filename based on constituent file parts

Return type:

str

Example

>>> from linescanning.utils import disassemble_fmriprep_wf
>>> bold_file = "sub-008_ses-2_task-SRFi_acq-3DEPI_run-1_desc-preproc_bold.nii.gz"
>>> wf_name = assemble_fmriprep_wf(bold_file)
>>> wf_name
>>> 'single_subject_008_wf/func_preproc_ses_2_task_SRFi_run_1_acq_3DEPI_wf'
>>> # workflow name only
>>> wf_name = assemble_fmriprep_wf(bold_file, wf_only=True)
>>> wf_name
>>> 'func_preproc_ses_2_task_SRFi_run_1_acq_3DEPI_wf'
linescanning.utils.bids_fullfile(bids_image)[source]

get full path to a BIDS-image filename

linescanning.utils.convert2unit(v, method='np')[source]

convert vector to unit vector

linescanning.utils.copy_hdr(source_img, dest_img)[source]

Similar functionality as fslcpgeom but than more rigorious using Nibabel. Copies the ENTIRE header, including affine, quaternion rotations, and dimensions.

Parameters:
  • source_img (str, nibabel.Nifti1Image) – source image from which to derive the header information

  • dest_img (str, nibabel.Nifti1Image) – destination image to which to copy the header from <source image> to

Returns:

source_img with updated header information

Return type:

nibabel.Nifti1Image

Example

>>> new_img = copy_hdr(img1,img2)
linescanning.utils.create_sinewave(amplitude=1, frequency=1, phase=0, sampling_rate=100, duration=1)[source]
Parameters:
  • amplitude (int,float) – Amplitude of the wave. Default = 1

  • frequency (int,float) – Frequency of the wave in Hertz. Default = 1

  • phase (int,float) – Phase shift of the wave in radians

  • sampling_rate (int) – Number of samples per second

  • duration (int) – Duration of the wave in seconds

Return type:

A tuple with first element the numpy array containing the sine wave. The second element is the time axis

Example

>>> from linescanning import utils
>>> wave,time = utils.create_sinewave()
>>> from linescanning import utils
>>> wave,time = utils.create_sinewave(
>>>     frequency=4,
>>>     amplitude=0.2
>>> )
linescanning.utils.decode(obj)[source]

decode an object

linescanning.utils.disassemble_fmriprep_wf(wf_path, subj_ID, prefix='sub-')[source]

Parses the workflow-folder from fMRIPrep into its constituents to recreate a filename. Searches for the following keys: [‘ses’, ‘task’, ‘acq’, ‘run’].

Parameters:
  • wf_path (str) – Path to workflow-folder

  • subj_ID (str) – Subject ID to append to prefix

  • prefix (str, optional) – Forms together with subj_ID the beginning of the new filename. By default “sub-”

Returns:

filename based on constituent file parts

Return type:

str

Example

>>> from linescanning.utils import disassemble_fmriprep_wf
>>> wf_dir = "func_preproc_ses_2_task_pRF_run_1_acq_3DEPI_wf"
>>> fname = disassemble_fmriprep_wf(wf_dir, "001")
>>> fname
'sub-001_ses-2_task-pRF_acq-3DEPI_run-1'
linescanning.utils.filter_for_nans(array)[source]

filter out NaNs from an array

linescanning.utils.find_intersection(xx, curve1, curve2)[source]

Find the intersection coordinates given two functions using Shapely.

Parameters:
  • xx (numpy.ndarray) – array describing the x-axis values

  • curve1 (numpy.ndarray) – array describing the first curve

  • curve2 (numpy.ndarray) – array describing the first curve

Returns:

x,y coordinates where curve1 and curve2 intersect

Return type:

tuple

Raises:

ValueError – if no intersection coordinates could be found

Example

See [refer to linescanning.prf.SizeResponse.find_stim_sizes]

linescanning.utils.find_max_val(array)[source]

find the index of maximum value given an array

linescanning.utils.find_nearest(array, value, return_nr=1)[source]

Find the index and value in an array given a value. You can either choose to have 1 item (the closest) returned, or the 5 nearest items (return_nr=5), or everything you’re interested in (return_nr=”all”)

Parameters:
  • array (numpy.ndarray) – array to search in

  • value (float) – value to search for in array

  • return_nr (int, str, optional) – number of elements to return after searching for elements in array that are close to value. Can either be an integer or a string all

Returns:

  • int – integer representing the index of the element in array closest to value.

  • list – if return_nr > 1, a list of indices will be returned

  • numpy.ndarray – value in array at the index closest to value

linescanning.utils.get_file_from_substring(filt, path, return_msg='error', exclude=None)[source]

This function returns the file given a path and a substring. Avoids annoying stuff with glob. Now also allows multiple filters to be applied to the list of files in the directory. The idea here is to construct a binary matrix of shape (files_in_directory, nr_of_filters), and test for each filter if it exists in the filename. If all filters are present in a file, then the entire row should be 1. This is what we’ll be looking for. If multiple files are found in this manner, a list of paths is returned. If only 1 file was found, the string representing the filepath will be returned.

Parameters:
  • filt (str, list) – tag for files we need to select. Now also support a list of multiple filters.

  • path (str) – path to the directory from which we need to remove files

  • return_msg (str, optional) – whether to raise an error (return_msg=’error’) or return None (*return_msg=None). Default = ‘error’.

  • exclude (str, optional:) – Specify string to exclude from options. This criteria will be ensued after finding files that conform to filt as final filter.

Returns:

  • str – path to the files containing string. If no files could be found, None is returned

  • list – list of paths if multiple files were found

Raises:

FileNotFoundError – If no files usingn the specified filters could be found

Example

>>> get_file_from_substring("R2", "/path/to/prf")
'/path/to/prf/r2.npy'
>>> get_file_from_substring(['gauss', 'best_vertices'], "path/to/pycortex/sub-xxx")
'/path/to/pycortex/sub-xxx/sub-xxx_model-gauss_desc-best_vertices.csv'
>>> get_file_from_substring(['best_vertices'], "path/to/pycortex/sub-xxx")
['/path/to/pycortex/sub-xxx/sub-xxx_model-gauss_desc-best_vertices.csv',
'/path/to/pycortex/sub-xxx/sub-xxx_model-norm_desc-best_vertices.csv']
linescanning.utils.get_matrixfromants(mat, invert=False)[source]

This function greps the rotation and translation matrices from the matrix-file create by antsRegistration. It basically does the same as on of the ANTs functions, but still..

Parameters:
  • mat (str) – string pointing to a .mat-file containing the transformation.

  • invert (bool) – Boolean for inverting the matrix (invert=False) or not (invert=True)

Returns:

(4,4) array representing the transformation matrix

Return type:

numpy.ndarray

linescanning.utils.get_module_nr(key_word)[source]

Fetches the module number from the master script given an input string. It sends a command using sed and grep to the bash command line. Won’t work on windows! See call_bashhelper for more information (that version is actually more accurate as it allows additions to the master usage, while that’s hardcoded in this one..)

Parameters:

key_word (str) – search string of the module your interested in. Should at least match otherwise the function will not find anything. For instance, if we want to know which module the creation of the sinus mask is, we can do:

Example

>>> get_module_nr('sinus')
'12'
linescanning.utils.make_binary_cm(color)[source]

This function creates a custom binary colormap using matplotlib based on the RGB code specified. Especially useful if you want to overlay in imshow, for instance. These RGB values will be converted to range between 0-1 so make sure you’re specifying the actual RGB-values. I like https://htmlcolorcodes.com to look up RGB-values of my desired color. The snippet of code used here comes from https://kbkb-wx-python.blogspot.com/2015/12/python-transparent-colormap.html

Parameters:

<color> (tuple, str) –

either hex-code with (!!) ‘#’ or a tuple consisting of:

  • <R> int | red-channel (0-255)

  • <G> int | green-channel (0-255)

  • <B> int | blue-channel (0-255)

Returns:

colormap to be used with plt.imshow

Return type:

matplotlib.colors.LinearSegmentedColormap object

Example

>>> cm = make_binary_cm((232,255,0))
>>> cm
<matplotlib.colors.LinearSegmentedColormap at 0x7f35f7154a30>
>>> cm = make_binary_cm("#D01B47")
>>> cm
>>> <matplotlib.colors.LinearSegmentedColormap at 0x7f35f7154a30>
linescanning.utils.make_chicken_csv(coord, input='ras', output_file=None, vol=0.343)[source]

This function creates a .csv-file like the chicken.csv example from ANTs to warp a coordinate using a transformation file. ANTs assumes the input coordinate to be LPS, but this function can deal with RAS-coordinates too. (see https://github.com/stnava/chicken for the reason of this function’s name)

Parameters:
  • coord (np.ndarray) – numpy array containing the three coordinates in x,y,z direction

  • input (str) – specify whether your coordinates uses RAS or LPS convention (default is RAS, and will be converted to LPS to create the file)

  • output_file (str) – path-like string pointing to an output file (.csv!)

  • vol (float) – volume of voxels (pixdim_x*pixdim_y*pixdim_z). If you’re using the standard 0.7 MP2RAGE, the default vol will be ok

Returns:

path pointing to the csv-file containing the coordinate

Return type:

str

Example

>>> make_chicken_csv(np.array([-16.239,-67.23,-2.81]), output_file="sub-001_space-fs_desc-lpi.csv")
"sub-001_space-fs_desc-lpi.csv"
linescanning.utils.match_lists_on(ref_list, search_list, matcher='run')[source]

Match two list based on a BIDS-specifier such as ‘sub’, ‘run’, etc. Can be any key that is extracted using linescanning.utils.split_bids_components().

Parameters:
  • ref_list (list) – List to use as reference

  • search_list (list) – List to search for items in ref_list

  • matcher (str, optional) – BIDS-identifier, by default “run”

Returns:

new search_list filtered for items in ref_list

Return type:

list

Example

>>> # Let's say I have functional files for 3 runs
>>> func_file
>>> ['sub-003_ses-3_task-SR_run-3_bold.mat',
>>> 'sub-003_ses-3_task-SR_run-4_bold.mat',
>>> 'sub-003_ses-3_task-SR_run-6_bold.mat']
>>> # and anatomical slices for 5 runs
>>> anat_slices
>>> ['sub-003_ses-3_acq-1slice_run-2_T1w.nii.gz',
>>> 'sub-003_ses-3_acq-1slice_run-3_T1w.nii.gz',
>>> 'sub-003_ses-3_acq-1slice_run-4_T1w.nii.gz',
>>> 'sub-003_ses-3_acq-1slice_run-5_T1w.nii.gz',
>>> 'sub-003_ses-3_acq-1slice_run-6_T1w.nii.gz']
>>> # I can then use `match_list_on` to find the anatomical slices corresponding to the functional files
>>> from linescanning import utils
>>> utils.match_lists_on(func_file, anat_slices, matcher='run')
>>> ['sub-003_ses-3_acq-1slice_run-3_T1w.nii.gz',
>>> 'sub-003_ses-3_acq-1slice_run-4_T1w.nii.gz',
>>> 'sub-003_ses-3_acq-1slice_run-6_T1w.nii.gz']
linescanning.utils.percent_change(ts, ax, nilearn=False, baseline=20, prf=False, dm=None)[source]

Function to convert input data to percent signal change. Two options are current supported: the nilearn method (nilearn=True), where the mean of the entire timecourse if subtracted from the timecourse, and the baseline method (nilearn=False), where the median of baseline is subtracted from the timecourse.

Parameters:
  • ts (numpy.ndarray) – Array representing the data to be converted to percent signal change. Should be of shape (n_voxels, n_timepoints)

  • ax (int) – Axis over which to perform the conversion. If shape (n_voxels, n_timepoints), then ax=1. If shape (n_timepoints, n_voxels), then ax=0.

  • nilearn (bool, optional) – Use nilearn method, by default False

  • baseline (int, list, np.ndarray optional) – Use custom method where only the median of the baseline (instead of the full timecourse) is subtracted, by default 20. Length should be in volumes, not seconds. Can also be a list or numpy array (1d) of indices which are to be considered as baseline. The list of indices should be corrected for any deleted volumes at the beginning.

Returns:

Array with the same size as ts (voxels,time), but with percent signal change.

Return type:

numpy.ndarray

Raises:

ValueError – If ax > 2

linescanning.utils.random_timeseries(intercept, volatility, nr)[source]

Create a random timecourse by multiplying an intercept with a random Gaussian distribution.

Parameters:
  • intercept (float) – starting point of timecourse

  • volatility (float) – this factor is multiplied with the Gaussian distribution before multiplied with the intercept

  • nr (int) – length of timecourse

Returns:

array of length nr

Return type:

numpy.ndarray

Example

>>> from linescanning import utils
>>> ts = utils.random_timeseries(1.2, 0.5, 100)

Notes

Source: https://stackoverflow.com/questions/67977231/how-to-generate-random-time-series-data-with-noise-in-python-3

linescanning.utils.read_chicken_csv(chicken_file, return_type='lps')[source]

Function to get at least the coordinates from a csv file used with antsApplyTransformsToPoints. (see https://github.com/stnava/chicken for the reason of this function’s name)

Parameters:
  • chicken_file (str) – path-like string pointing to an input file (.csv!)

  • return_type (str) – specify the coordinate system that the output should be in

Returns:

(3,) array containing the coordinate in chicken_file

Return type:

numpy.ndarray

Example

>>> read_chicken_csv("sub-001_space-fs_desc-lpi.csv")
array([-16.239,-67.23,-2.81])
linescanning.utils.read_fs_reg(dat_file)[source]

Read a .dat-formatted registration file from FreeSurfer

Parameters:

dat_file (str) – path pointing to the registration file

Returns:

(4,4) numpy array containing the transformation

Return type:

nump.ndarray

linescanning.utils.remove_files(path, string, ext=False)[source]

Remove files from a given path that containg a string as extension (ext=True), or at the start of the file (ext=False)

Parameters:
  • path (str) – path to the directory from which we need to remove files

  • string (str) – tag for files we need to remove

  • ext (str, optional) – only remove files containing string that end with ext

linescanning.utils.replace_string(fn, str1, str2, fn_sep='_')[source]

Replace a string with another string given a filename

Parameters:
  • fn (str) – filename in which we need to replace something

  • str1 (str) – string-to-be-replaced

  • str2 (str) – string-to-replace-str1-with

  • fn_sep (str) – what type of element can we use to split the filename into chunks that we can replace

Returns:

filename with replaced substring

Return type:

str

linescanning.utils.resample2d(array: ndarray, new_size: int, kind='linear')[source]

Resamples a 2D (or 3D) array with scipy.interpolate.interp2d() to new_size. If input is 2D, we’ll loop over the final axis.

Parameters:
  • array (np.ndarray) – Array to be interpolated. Ideally axis have the same size.

  • new_size (int) – New size of array

  • kind (str, optional) – Interpolation method, by default ‘linear’

Returns:

If 2D: resampled array of shape (new_size,new_size) If 3D: resampled array of shape (new_size,new_size, array.shape[-1])

Return type:

np.ndarray

linescanning.utils.reverse_sign(x)[source]

Inverts the sign given set of values. Can be either one value or an array of values that need to be inverted

Parameters:

x (int,float,list,numpy.ndarray) – input that needs inverting, either one value or a list

Return type:

the inverse of whatever the input x was

Example

>>> # input is integer
>>> x = 5
>>> reverse_sign(x)
-5
>>> # input is array
>>> x = np.array([2, -2340, 2345,123342, 123])
>>> In [6]: reverse_sign(x)
array([-2.00000e+00,  2.34000e+03, -2.34500e+03, -1.23342e+05,-1.23000e+02])
>>> # input is float
>>> x = 5.0
>>> reverse_sign(x)
-5.0
linescanning.utils.round_decimals_down(number: float, decimals: int = 2)[source]

Returns a value rounded down to a specific number of decimal places. see: https://kodify.net/python/math/round-decimals/#round-decimal-places-up-and-down-round

linescanning.utils.round_decimals_up(number: float, decimals: int = 2)[source]

Returns a value rounded up to a specific number of decimal places. see: https://kodify.net/python/math/round-decimals/#round-decimal-places-up-and-down-round

linescanning.utils.select_from_df(df, expression=None, index=True, indices=None, match_exact=True)[source]

Select a subset of a dataframe based on an expression. Dataframe should be indexed by the variable you want to select on or have the variable specified in the expression argument as column name. If index is True, the dataframe will be indexed by the selected variable. If indices is specified, the dataframe will be indexed by the indices specified through a list (only select the elements in the list) or a range-object (select within range).

Parameters:
  • df (pandas.DataFrame) – input dataframe

  • expression (str, optional) – what subject of the dataframe to select, by default None. The expression must consist of a variable name and an operator. The operator can be any of the following: ‘=’, ‘>’, ‘<’, ‘>=’, ‘<=’, ‘!=’, separated by spaces. You can also change 2 operations by specifying the &-operator between the two expressions. If you want to use indices, specify expression=”ribbon”.

  • index (bool, optional) – return output dataframe with the same indexing as df, by default True

  • indices (list, range, numpy.ndarray, optional) – List, range, or numpy array of indices to select from df, by default None

  • match_exact (bool, optional:) – When you insert a list of strings with indices to be filtered from the dataframe, you can either request that the items of indices should match exactly (match_exact=True, default) the column names of df, or whether the columns of df should contain the items of indices (match_exact=False).

Returns:

new dataframe where expression or indices were selected from df

Return type:

pandas.DataFrame

Raises:

TypeError – If indices is not a tuple, list, or array

Notes

See https://linescanning.readthedocs.io/en/latest/examples/nideconv.html for an example of how to use this function (do ctrl+F and enter “select_from_df”).

linescanning.utils.squeeze_generic(a, axes_to_keep)[source]

Numpy squeeze implementation keeping <axes_to_keep> dimensions.

Parameters:
  • a (numpy.ndarray) – array to be squeezed

  • axes_to_keep (tuple, range) – tuple of axes to keep from original input

Returns:

axes_to_keep from a

Return type:

numpy.ndarray

Example

>>> a = np.random.rand(3,5,1)
>>> squeeze_generic(a, axes_to_keep=range(2)).shape
(3, 5)

Notes

From: https://stackoverflow.com/questions/57472104/is-it-possible-to-squeeze-all-but-n-dimensions-using-numpy

linescanning.utils.string2float(string_array)[source]

This function converts a array in string representation to a regular float array. This can happen, for instance, when you’ve stored a numpy array in a pandas dataframe (such is the case with the ‘normal’ vector). It starts by splitting based on empty spaces, filter these, and convert any remaining elements to floats and returns these in an array.

Parameters:

string_array (str) – string to be converted to a valid numpy array with float values

Returns:

array containing elements in float rather than in string representation

Return type:

numpy.ndarray

Example

>>> string2float('[ -7.42 -92.97 -15.28]')
array([ -7.42, -92.97, -15.28])
linescanning.utils.string2list(string_array, make_float=False)[source]

This function converts a array in string representation to a list of string. This can happen, for instance, when you use bash to give a list of strings to python, where ast.literal_eval fails.

Parameters:

string_array (str) – string to be converted to a valid numpy array with float values

Returns:

array containing elements in float rather than in string representation

Return type:

numpy.ndarray

Example

>>> string2list('[tc,bgfs]')
['tc', 'bgfs']
linescanning.utils.unique_combinations(elements, l=2)[source]

Precondition: elements does not contain duplicates. Postcondition: Returns unique combinations of length 2 from elements.

>>> unique_combinations(["apple", "orange", "banana"])
[("apple", "orange"), ("apple", "banana"), ("orange", "banana")]