Package mvpa :: Package measures :: Module searchlight

[hide private]
[frames] | no frames]

# Source Code for Module mvpa.measures.searchlight

      1  # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
      2  # vi: set ft=python sts=4 ts=4 sw=4 et:
      3  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
      4  #
      5  #   See COPYING file distributed along with the PyMVPA package for the
      6  #   copyright and license terms.
      7  #
      8  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
      9  """Implementation of the Searchlight algorithm"""
     10
     11  __docformat__ = 'restructuredtext'
     12
     13  if __debug__:
     14      from mvpa.base import debug
     15
     16  from mvpa.datasets.mapped import MappedDataset
     17  from mvpa.measures.base import DatasetMeasure
     18  from mvpa.misc.state import StateVariable
     19  from mvpa.base.dochelpers import enhancedDocString
     20
     21
     22 -class Searchlight(DatasetMeasure):
     23      """Runs a scalar `DatasetMeasure` on all possible spheres of a certain size
     24      within a dataset.
     25
     26      The idea for a searchlight algorithm stems from a paper by
     27      :ref:`Kriegeskorte et al. (2006) <KGB06>`.
     28      """
     29
     30      spheresizes = StateVariable(enabled=False,
     31          doc="Number of features in each sphere.")
     32
     33 -    def __init__(self, datameasure, radius=1.0, center_ids=None, **kwargs):
     34          """
     35          :Parameters:
     36            datameasure: callable
     37              Any object that takes a :class:`~mvpa.datasets.base.Dataset`
     38              and returns some measure when called.
     39            radius: float
     40              All features within the radius around the center will be part
     41              of a sphere. Provided dataset should have a metric assigned
     42              (for NiftiDataset, voxel size is used to provide such a metric,
     43              hence radius should be specified in mm).
     44            center_ids: list(int)
     45              List of feature ids (not coordinates) the shall serve as sphere
     46              centers. By default all features will be used.
     47            **kwargs
     48              In additions this class supports all keyword arguments of its
     49              base-class :class:`~mvpa.measures.base.DatasetMeasure`.
     50
     51          .. note::
     52
     53            If `Searchlight` is used as `SensitivityAnalyzer` one has to make
     54            sure that the specified scalar `DatasetMeasure` returns large
     55            (absolute) values for high sensitivities and small (absolute) values
     56            for low sensitivities. Especially when using error functions usually
     57            low values imply high performance and therefore high sensitivity.
     58            This would in turn result in sensitivity maps that have low
     59            (absolute) values indicating high sensitivites and this conflicts
     60            with the intended behavior of a `SensitivityAnalyzer`.
     61          """
     62          DatasetMeasure.__init__(self, **(kwargs))
     63
     64          self.__datameasure = datameasure
     65          self.__radius = radius
     66          self.__center_ids = center_ids
     67
     68
     69      __doc__ = enhancedDocString('Searchlight', locals(), DatasetMeasure)
     70
     71
     72 -    def _call(self, dataset):
     73          """Perform the spheresearch.
     74          """
     75          if not isinstance(dataset, MappedDataset) \
     76                 or dataset.mapper.metric is None:
     77              raise ValueError, "Searchlight only works with MappedDatasets " \
     78                                "that has metric assigned."
     79
     80          if self.states.isEnabled('spheresizes'):
     81              self.spheresizes = []
     82
     83          if __debug__:
     84              if not self.__center_ids == None:
     85                  nspheres = len(self.__center_ids)
     86              else:
     87                  nspheres = dataset.nfeatures
     88              sphere_count = 0
     89
     90          # collect the results in a list -- you never know what you get
     91          results = []
     92
     93          # decide whether to run on all possible center coords or just a provided
     94          # subset
     95          if not self.__center_ids == None:
     96              generator = self.__center_ids
     97          else:
     98              generator = xrange(dataset.nfeatures)
     99
    100          # put spheres around all features in the dataset and compute the
    101          # measure within them
    102          for f in generator:
    103              sphere = dataset.selectFeatures(
    104                  dataset.mapper.getNeighbors(f, self.__radius),
    105                  plain=True)
    106
    107              # compute the datameasure and store in results
    108              measure = self.__datameasure(sphere)
    109              results.append(measure)
    110
    111              # store the size of the sphere dataset
    112              if self.states.isEnabled('spheresizes'):
    113                  self.spheresizes.append(sphere.nfeatures)
    114
    115              if __debug__:
    116                  sphere_count += 1
    117                  debug('SLC', "Doing %i spheres: %i (%i features) [%i%%]" \
    118                      % (nspheres,
    119                         sphere_count,
    120                         sphere.nfeatures,
    121                         float(sphere_count)/nspheres*100,), cr=True)
    122
    123          if __debug__:
    124              debug('SLC', '')
    125
    126          # charge state
    127          self.raw_results = results
    128
    129          # return raw results, base-class will take care of transformations
    130          return results
    131
    132
    133
    134
    135  #class OptimalSearchlight( object ):
    136  #    def __init__( self,
    137  #                  searchlight,
    138  #                  test_radii,
    139  #                  verbose=False,
    140  #                  **kwargs ):
    141  #        """
    142  #        """
    143  #        # results will end up here
    144  #        self.__perfmeans = []
    145  #        self.__perfvars = []
    146  #        self.__chisquares = []
    147  #        self.__chanceprobs = []
    148  #        self.__spheresizes = []
    149  #
    150  #        # run searchligh for all radii in the list
    151  #        for radius in test_radii:
    152  #            if verbose:
    153  #                print 'Using searchlight with radius:', radius
    154  #            # compute the results
    155  #            searchlight( radius, **(kwargs) )
    156  #
    157  #            self.__perfmeans.append( searchlight.perfmean )
    158  #            self.__perfvars.append( searchlight.perfvar )
    159  #            self.__chisquares.append( searchlight.chisquare )
    160  #            self.__chanceprobs.append( searchlight.chanceprob )
    161  #            self.__spheresizes.append( searchlight.spheresize )
    162  #
    163  #
    164  #        # now determine the best classification accuracy
    165  #        best = N.array(self.__perfmeans).argmax( axis=0 )
    166  #
    167  #        # select the corresponding values of the best classification
    168  #        # in all data tables
    169  #        self.perfmean   = best.choose(*(self.__perfmeans))
    170  #        self.perfvar    = best.choose(*(self.__perfvars))
    171  #        self.chisquare  = best.choose(*(self.__chisquares))
    172  #        self.chanceprob = best.choose(*(self.__chanceprobs))
    173  #        self.spheresize = best.choose(*(self.__spheresizes))
    174  #
    175  #        # store the best performing radius
    176  #        self.bestradius = N.zeros( self.perfmean.shape, dtype='uint' )
    177  #        self.bestradius[searchlight.mask==True] = \
    178  #            best.choose( test_radii )[searchlight.mask==True]
    179  #
    180  #
    181  #
    182  #def makeSphericalROIMask( mask, radius, elementsize=None ):
    183  #    """
    184  #    """
    185  #    # use default elementsize if none is supplied
    186  #    if not elementsize:
    187  #        elementsize = [ 1 for i in range( len(mask.shape) ) ]
    188  #    else:
    189  #        if len( elementsize ) != len( mask.shape ):
    190  #            raise ValueError, 'elementsize does not match mask dimensions.'
    191  #
    192  #    # rois will be drawn into this mask
    193  #    roi_mask = N.zeros( mask.shape, dtype='int32' )
    194  #
    195  #    # while increase with every ROI
    196  #    roi_id_counter = 1
    197  #
    198  #    # build spheres around every non-zero value in the mask
    199  #    for center, spheremask in \
    200  #        algorithms.SpheresInMask( mask,
    201  #                                  radius,
    202  #                                  elementsize,
    203  #                                  forcesphere = True ):
    204  #
    205  #        # set all elements that match the current spheremask to the
    206  #        # current ROI index value
    207  #        roi_mask[spheremask] = roi_id_counter
    208  #
    209  #        # increase ROI counter
    210  #        roi_id_counter += 1
    211  #
    212  #    return roi_mask
    213