Jonas T. Kaplan, Ph.D.
Custom Python Modules



DISCLAIMER: These files are intended for internal lab use. You're free to use them of course, I just don't intend to support them for the public.

pydoc module documentation

Introduction

The purpose of this module is to make it easier to parse experiment logfiles and output the information in various forms needed for fMRI analysis. Currently the code will output any of the following:

Download and Installation

You can download the latest version as zip file here:

fmri-1.0.tar.gz

To install it, just decompress the archive, then change director into the new folder, and do:

python setup.py install

That's it.

Usage

The code is set up to parse logfiles in the format that I usually create them, but its extensible so that you can write your own parser for your logfiles and still use the reformatting functions without having to rewrite those. First, let's cover using the module in its default setup.

From your python terminal (I recommend ipython), import the module:

import fmri

The main class you will be using is called an "experiment". This object will contain a whole bunch of metadata about the experiment for a given subject, including the logfiles, their location, and ultimately the trial and timing information from every trial. To create an experiment object, you need to provide at least the name of a subject, and the location of the subject's logfiles:

mysubject = fmri.experiment("touch03","/Users/jonask/fMRI/touch/data/logfiles")

Parsing logfile: touch03-1-sess01-soma-ns-log.txt Treating as session 01
Parsing logfile: touch03-1-sess02-soma-ns-log.txt Treating as session 02
Parsing logfile: touch03-1-sess03-soma-ns-log.txt Treating as session 03
Parsing logfile: touch03-1-sess04-soma-ns-log.txt Treating as session 04
Parsing logfile: touch03-1-sess05-soma-ns-log.txt Treating as session 05
Parsing logfile: touch03-1-sess06-soma-ns-log.txt Treating as session 06
Parsing logfile: touch03-1-sess07-soma-ns-log.txt Treating as session 07
Parsing logfile: touch03-1-sess08-soma-ns-log.txt Treating as session 08
Parsing logfile: touch03-1-sess09-soma-ns-log.txt Treating as session 09
Parsing logfile: touch03-1-sess10-soma-ns-log.txt Treating as session 10

So, this subject had 10 logfiles, each describing a separate scan. In the module's terminology, a scan is called a "session". The code has looked in the specified folder for any file containing the subject's name. If your logfiles are not set up this way, you'll have to alter the setLogfiles method or override it with a subclass (more on that later).

Let's take a look at some of the metadata stored in the object.

mysubject

{'trialduration': 5.0, 'name': None, 'TR': 2.0, 'logfiledir': '/Users/jonask/fMRI/touch/data/logfiles', 'numtimepoints': 174, 'subject': 'touch03'}


These tags are the default values. You can change them by passing new values when you create your experiment instance, or if you make your own experiment-specific subclass you can change the default values. You can also change them at any time, like:

mysubject["TR"] = 3.0

We can see the list of logfiles found like this:

mysubject.logfiles

['touch03-1-sess01-soma-ns-log.txt',
'touch03-1-sess02-soma-ns-log.txt',
'touch03-1-sess03-soma-ns-log.txt',
'touch03-1-sess04-soma-ns-log.txt',
'touch03-1-sess05-soma-ns-log.txt',
'touch03-1-sess06-soma-ns-log.txt',
'touch03-1-sess07-soma-ns-log.txt',
'touch03-1-sess08-soma-ns-log.txt',
'touch03-1-sess09-soma-ns-log.txt',
'touch03-1-sess10-soma-ns-log.txt']

If the parser worked, it has looked inside each file and converted each trial in the experiment into a trial object, a list of which is attached to our experiment:

mysubject.trials

[Session: 1 Trialnum 1 TrialType: tennis-ball Onset: 10.041623 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 2 TrialType: plant Onset: 26.220686 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 3 TrialType: plant Onset: 42.157300 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 4 TrialType: plant Onset: 58.151220 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 5 TrialType: tennis-ball Onset: 74.143699 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 6 TrialType: tennis-ball Onset: 90.138236 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 7 TrialType: yarn Onset: 106.397191 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 8 TrialType: keychain Onset: 122.515201 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 9 TrialType: plant Onset: 138.242894 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 10 TrialType: lightbulb Onset: 154.367350 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 11 TrialType: yarn Onset: 170.143228 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 12 TrialType: lightbulb Onset: 186.156421 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 13 TrialType: keychain Onset: 204.137828 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 14 TrialType: keychain Onset: 224.138507 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 15 TrialType: yarn Onset: 240.129531 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 16 TrialType: keychain Onset: 256.131525 Duration: 5.00 Height: 1.00,
Session: 1 Trialnum 17 TrialType: yarn Onset: 272.251141 Duration: 5.00 Height: 1.00,
Session: 2 Trialnum 1 TrialType: plant Onset: 10.011155 Duration: 5.00 Height: 1.00,
Session: 2 Trialnum 2 TrialType: keychain Onset: 26.209448 Duration: 5.00 Height: 1.00,
Session: 2 Trialnum 3 TrialType: yarn Onset: 42.114830 Duration: 5.00 Height: 1.00,
Session: 2 Trialnum 4 TrialType: keychain Onset: 58.113971 Duration: 5.00 Height: 1.00,
Session: 2 Trialnum 5 TrialType: keychain Onset: 74.192392 Duration: 5.00 Height: 1.00,
etc.

Now, outputting any of the various formats is just a matter of calling the appropriate output function:

toFSL(self,outputdir='.',bytrial=False)
This one is for outputting 3-column format FSL files. By default, you will get one file per stimulus type per session. To get one per trial, set bytrial to True. You can specify the output directory for the files or allow them to be written into the current directory. e.g.:

mysubject.toFSL('/Users/jonask/stimfile_folder')

toMVPA_sparse(self,outputdir='.',leadingzero=True)
This will create chunks and targets for PyMVPA, one per trial. Useful if you have already temporally compressed your data by averaging or parameter estimates, or if you have done sparse sampling. The leadingzero parameter will determine whether a blank rest trial is placed at the start of every chunk, as needed in our sparse paradigms.e.g:

mysubject.toMVPA_sparse(leadingzero=False)

In addition to creating a text file, this will also populate the object's chunks and targets attributes so that you can use them directly:

mysubject.targets

['tennis-ball',
'plant',
'plant',
'plant',
'tennis-ball',
'tennis-ball',
'yarn',
'keychain',
'plant',
'lightbulb',
'yarn',
'lightbulb',
etc.


If you want to see what the two columns look like together as they are in your text file, you can call showAttributes():

mysubject.showAttributes()

tennis-ball 1
plant 1
plant 1
plant 1
tennis-ball 1
tennis-ball 1
yarn 1
keychain 1
plant 1
lightbulb 1
yarn 1
lightbulb 1
keychain 1
keychain 1
yarn 1
keychain 1
yarn 1
plant 2
keychain 2
yarn 2
etc.


toMVPA_trialstarts(self,outputdir='.')
This one will create chunks and attributes but will label only the first volume of each trial. All the other trials will be labeled as "rest".


toMVPA_continuous(self,outputdir='.',hrfdelay=4.0)
This one will create chunks and attributes and will label every volume within the trial according to its duration and a specified hrfdelay, in seconds. All other trials will be labeled as "rest".


Customization

The easiest way to customize the code for your experiment is to create a subclass that has the defaults for your experiment, and possibly contains a specialized parsing method to handle the specifics of your logfiles. For example, I have created a new class that inherits from fmri.experiment, but passes new defaults to the init function, and specifies a new parser to deal with the layout of my specific logfiles. You can view this example code here. Now I can use this custom class instead, and since it already specifies the location of my logfiles, I only need to give it a subject name:

import touchexp
mysubj = touchexp.experiment('touch03')