Changeset 215

Show
Ignore:
Timestamp:
10/16/08 13:45:00 (3 months ago)
Author:
apdavison
Message:

Merged cleanup branch changes r198:214 into the trunk.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/INSTALL

    r148 r215  
    1 NeuroTools Insallation: 
     1NeuroTools Insallation 
     2====================== 
    23 
    34First download the package files: 
    4 svn co https://neuralensemble.kip.uni-heidelberg.de/svn/NeuroTools/trunk NeuroTools 
    5 cd NeuroTools 
    65 
    7 As root 
     6        svn co https://neuralensemble.org/svn/NeuroTools/trunk NeuroTools 
     7        cd NeuroTools 
    88 
    9 $ python setup.py install 
     9Install as root (if you want a global install) 
    1010 
    11 or for those without root access, to a local location, something like: 
     11        $ python setup.py install 
    1212 
    13 # python setup.py install --prefix=$HOME/opt/mystuff 
     13or for those without root access, install to a writable location, something like: 
     14 
     15        # python setup.py install --prefix=$HOME/opt/mystuff 
    1416 
    1517Then you need to add the location:  
    1618 
    17 $HOME/opt/mystuff/lib/python2.4/site-packages/  
     19$HOME/opt/mystuff/lib/python2.5/site-packages/  
    1820 
    1921to your PYTHON_PAH or (in python) your sys.path 
    2022 
    21 Note, here lib is replaced by lib64 on 64bit systems, and python2.4 is 
     23Note, here lib is replaced by lib64 on 64bit systems, and python2.5 is 
    2224(obviously) replaced by your python version. 
    2325 
    24 Developpers of NeuroTools may be intersted in having their lastly updated version from SVN used by python (and therefore on python's path). A solution is to symbolically link the src folder to a folder included in the path: 
    25 cd my_local_site-packages_folder 
    26 ln -s where_I_check-out_neuralensemble/NeuroTools/trunk/src NeuroTools 
     26Developpers of NeuroTools may be interested in having their lastly updated version from SVN used by python (and therefore on python's path). A solution is to symbolically link the src folder to a folder included in the path: 
     27 
     28        cd my_local_site-packages_folder 
     29        ln -s where_I_check-out_neuralensemble/NeuroTools/trunk/src NeuroTools 
     30 
    2731and voila! 
    2832 
  • trunk/README

    r6 r215  
    1 NeuroTools SVN : see https://facets-vm3.kip.uni-heidelberg.de/trac/NeuroTools/wiki 
     1For more info see the NeuroTools trac http://neuralensemble.org/trac/NeuroTools 
  • trunk/examples/single_neuron/simple_single_neuron.py

    r177 r215  
    2323import pyNN.nest2 as sim 
    2424# the link to read SpikeList files with NeuroTools 
    25 from NeuroTools.spikes import loadSpikeList 
     25from NeuroTools.signals import loadSpikeList 
    2626# using parameters utility 
    2727from NeuroTools.parameters import ParameterSet 
  • trunk/src/__init__.py

    r190 r215  
    1 __all__ = ['analysis', 'benchmark', 'parameters', 'plotting', 'sandbox', 'spikes', 'signals', 'stgen', 'utilities'] 
     1__all__ = ['analysis', 'benchmark', 'parameters', 'plotting', 'sandbox', 'signals', 'stgen', 'utilities', 'io'] 
    22 
    33"""from tables import __version__ 
  • trunk/src/analysis.py

    r188 r215  
    99import os, numpy 
    1010 
     11def ccf(x, y, axis=None): 
     12    """Computes the cross-correlation function of two series `x` and `y`. 
     13        Note that the computations are performed on anomalies (deviations from 
     14        average). 
     15        Returns the values of the cross-correlation at different lags. 
     16        Lags are given as [0,1,2,...,n,n-1,n-2,...,-2,-1] (not any more) 
     17         
     18        :Parameters: 
     19            `x` : 1D MaskedArray 
     20                Time series. 
     21            `y` : 1D MaskedArray 
     22                Time series. 
     23            `axis` : integer *[None]* 
     24                Axis along which to compute (0 for rows, 1 for cols). 
     25                If `None`, the array is flattened first. 
     26    """ 
     27    assert(x.ndim == y.ndim, "Inconsistent shape !") 
     28#    assert(x.shape == y.shape, "Inconsistent shape !") 
     29    if axis is None: 
     30        if x.ndim > 1: 
     31            x = x.ravel() 
     32            y = y.ravel() 
     33        npad = x.size + y.size 
     34        xanom = (x - x.mean(axis=None)) 
     35        yanom = (y - y.mean(axis=None)) 
     36        Fx = numpy.fft.fft(xanom, npad, ) 
     37        Fy = numpy.fft.fft(yanom, npad, ) 
     38        iFxy = numpy.fft.ifft(Fx.conj()*Fy).real 
     39        varxy = numpy.sqrt(numpy.inner(xanom,xanom) * numpy.inner(yanom,yanom)) 
     40    else: 
     41        npad = x.shape[axis] + y.shape[axis] 
     42        if axis == 1: 
     43            if x.shape[0] != y.shape[0]: 
     44                raise ValueError, "Arrays should have the same length!" 
     45            xanom = (x - x.mean(axis=1)[:,None]) 
     46            yanom = (y - y.mean(axis=1)[:,None]) 
     47            varxy = numpy.sqrt((xanom*xanom).sum(1) * (yanom*yanom).sum(1))[:,None] 
     48        else: 
     49            if x.shape[1] != y.shape[1]: 
     50                raise ValueError, "Arrays should have the same width!" 
     51            xanom = (x - x.mean(axis=0)) 
     52            yanom = (y - y.mean(axis=0)) 
     53            varxy = numpy.sqrt((xanom*xanom).sum(0) * (yanom*yanom).sum(0)) 
     54        Fx = numpy.fft.fft(xanom, npad, axis=axis) 
     55        Fy = numpy.fft.fft(yanom, npad, axis=axis) 
     56        iFxy = numpy.fft.ifft(Fx.conj()*Fy,n=npad,axis=axis).real 
     57    # We juste turn the lags into correct positions: 
     58    iFxy = numpy.concatenate((iFxy[len(iFxy)/2:len(iFxy)],iFxy[0:len(iFxy)/2])) 
     59    return iFxy/varxy 
    1160 
    12 def record(output, cfilename = 'SpikeTrainPlay.wav', fs=44100, enc = 'pcm26'): 
    13     """ record the 'sound' produced by a neuron. Takes a spike train as the 
    14     output. 
    15  
    16     >>> record(my_spike_train) 
    17  
    18     """ 
    19  
    20  
    21     # from the spike list 
    22     simtime_seconds = (output.t_stop - output.t_start)/1000. 
    23     #time = numpy.linspace(0, simtime_seconds , fs*simtime_seconds) 
    24     (trace,time) = numpy.histogram(output.spike_times*1000., fs*simtime_seconds) 
    25  
    26  
    27     # TODO convolve with proper spike... 
    28     spike = numpy.ones((fs/1000.,)) # one ms 
    29  
    30     trace = numpy.convolve(trace, spike, mode='same')#/2.0 
    31     trace /= numpy.abs(trace).max() * 1.1 
    32  
    33     from scikits.audiolab import wavwrite 
    34     wavwrite(trace, cfilename, fs = fs, enc = enc) 
    35  
    36 def play(output): 
    37     """ 
    38     plays a spike list to the audio output 
    39  
    40     play(spike_list) where spike_list is a spike_list object 
    41  
    42     see playing_with_simple_single_neuron.py for a sample use 
    43  
    44     >>> play(my_spike_train) 
    45  
    46     TODO: make it possible to play multiple spike trains in stereo 
    47     """ 
    48  
    49  
    50     from tempfile import mkstemp 
    51     fd, cfilename   = mkstemp('SpikeListPlay.wav') 
    52     try: 
    53         record(output, cfilename) 
    54         import pyaudio 
    55         import wave 
    56  
    57         chunk = 1024 
    58         wf = wave.open(cfilename, 'rb') 
    59         p = pyaudio.PyAudio() 
    60  
    61         # open stream 
    62         stream = p.open(format = 
    63                         p.get_format_from_width(wf.getsampwidth()), 
    64                         channels = wf.getnchannels(), 
    65                         rate = wf.getframerate(), 
    66                         output = True) 
    67  
    68         # read data 
    69         data = wf.readframes(chunk) 
    70  
    71         # play stream 
    72         while data != '': 
    73             stream.write(data) 
    74             data = wf.readframes(chunk) 
    75  
    76         stream.close() 
    77         p.terminate() 
    78     except: 
    79         print "Error playing the SpikeTrain " 
    80         # finally 
    81         os.remove(cfilename) 
    82  
    83     # Python 2.4 compatibility 
    84     # finally: 
    85     os.remove(cfilename) 
    8661 
    8762def _dict_max(D): 
  • trunk/src/plotting.py

    r186 r215  
    11 
    2 import numpy #, pylab 
    3 import sys 
     2import numpy, pylab, sys 
    43from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas 
    54from matplotlib.figure import Figure 
     
    3837 
    3938 
    40 def raster_plot(spike_list,output=None):# limits of the plot 
    41     import pylab 
    42     DATA=spike_list.as_list_id_list_time() 
    43     pylab.plot(DATA[1],DATA[0],'.') 
    44     pylab.ylabel('neuron ID') 
    45     pylab.xlabel('time (s)') 
    46     pylab.axis([spike_list.t_start, spike_list.t_stop, 0, spike_list.N]) 
    47     if not(output==None): 
    48         pylab.savefig(output) 
    49     #else: 
    50         #pylab.show() 
    51  
    52  
    53  
    54  
    5539def set_frame(ax,boollist,linewidth=2): 
    5640    assert len(boollist) == 4 
     
    6549            if draw: 
    6650                ax.add_line(side) 
     51 
     52 
    6753 
    6854class SimpleMultiplot(object): 
  • trunk/src/signals.py

    r205 r215  
    55""" 
    66 
    7 import numpy 
    8 import os 
     7import numpy, os, logging, re 
     8from NeuroTools import analysis 
     9from NeuroTools import io 
     10 
    911try : 
    1012    import pylab 
    11 except Exception: 
    12     print "Warning: pylab not present" 
    13 from NeuroTools import analysis 
    14 import logging 
     13    ENABLE_PLOTS     = True 
     14except ImportError: 
     15    ENABLE_PLOTS     = False 
     16    MATPLOTLIB_ERROR = """ 
     17    Matplotlib not detected so plots are disabled.  
     18    To turn on plots, please install the Matplotlib package 
     19    """ 
     20    print MATPLOTLIB_ERROR 
     21 
    1522 
    1623class SpikeTrain(object): 
    1724    """ 
    18     This class defines a spike train as a list of the events times. 
     25    SpikeTrain(spikes_times, dt=None, t_start=None, t_stop=None) 
     26    This class defines a spike train as a list of times events. 
    1927 
    2028    Event times are given in a list (sparse representation) in milliseconds. 
    2129 
    22     >>> s1 = SpikeTrain([0.0, 0.1, 0.2, 0.5]) 
    23     >>> s2 = SpikeTrain(numpy.array([0, 1, 2, 5]), dt=0.1) 
    24     >>> assert all(s1.spike_times == s2.spike_times) 
    25     >>> print s1.format(relative=True) 
    26     [ 0.   0.1  0.1  0.3] 
    27     >>> s1.isi() 
    28     array([ 0.1,  0.1,  0.3]) 
    29     >>> s1.mean_rate() 
    30     8.0 
    31     >>> s1.cv_isi() 
    32     0.565685424949 
     30    Inputs: 
     31        spike_times - a list/numpy array of spike times (in milliseconds) 
     32        t_start     - beginning of the SpikeTrain (if not, this is infered) 
     33        t_stop      - end of the SpikeTrain (if not, this is infered) 
     34        dt          - time precision of the spike events 
     35 
     36    Examples: 
     37        >>> s1 = SpikeTrain([0.0, 0.1, 0.2, 0.5]) 
     38        >>> s2 = SpikeTrain([0, 1, 2, 5], dt=0.1) 
     39        >>> assert all(s1.spike_times == s2.spike_times) 
     40        >>> s1.isi() 
     41        array([ 0.1,  0.1,  0.3]) 
     42        >>> s1.mean_rate() 
     43        8.0 
     44        >>> s1.cv_isi() 
     45        0.565685424949 
    3346    """ 
    34     # TODO in the definition, should a spike train be ordered? 
    35  
    36     # TODO : should we handle different population shapes differently? 
    3747 
    3848    ####################################################################### 
     
    4151    def __init__(self, spike_times, dt=None, t_start=None, t_stop=None): 
    4252        """ 
    43         `spike_times` is a list/numpy array of spike times (in milliseconds) 
    44  
    45         TODO: We proposed initially that : 
    46         If `dt` is specified, the time values should be ints, 
    47         and will be multiplied by `dt`, otherwise time values should be floats. 
    48         Markus suggested that the internal representation should be integer and 
    49         that analysis methods also. 
    50  
    51         If `t_start` and `t_stop` are not specified, they are inferred from the data. 
    52  
    53         the format adopted for the representation is relative=False, quantized=False 
     53        Constructor of the SpikeTrain object 
     54         
     55        See also 
     56            SpikeTrain 
    5457 
    5558        """ 
    5659 
    5760        if dt is not None: 
    58             if dt<=0: 
     61            if dt <= 0: 
    5962                raise ValueError("dt must be greater than zero") 
    6063            #self.spike_times *= dt 
    6164 
    62         self.dt = dt 
    63         self.t_start = t_start 
    64         self.t_stop  = t_stop 
    65  
     65        self.dt          = dt 
     66        self.t_start     = t_start 
     67        self.t_stop      = t_stop 
    6668        self.spike_times = numpy.array(spike_times, 'float') 
    6769 
     
    6971        # the spikes with t >= t_start 
    7072        if self.t_start is not None: 
    71             idx = numpy.where(self.spike_times >= self.t_start)[0] 
    72             self.spike_times = self.spike_times[idx] 
    73             assert numpy.all(self.spike_times >= self.t_start), \ 
    74               "Spike times out of range (t_start=%s, min(spike_times)=%s" % (self.t_start,self.spike_times.min()) 
     73            self.spike_times = numpy.extract((self.spike_times >= self.t_start), self.spike_times) 
    7574 
    7675        # If t_stop is not None, we resize the spike_train keeping only 
    7776        # the spikes with t <= t_stop 
    7877        if self.t_stop is not None: 
    79             idx = numpy.where(self.spike_times <= self.t_stop)[0] 
    80             self.spike_times = self.spike_times[idx] 
    81             assert numpy.all(self.spike_times <= self.t_stop), \ 
    82                 "Spike times out of range (t_stop=%s, max(spike_times)=%s" % (self.t_stop,self.spike_times.max()) 
    83  
     78            self.spike_times = numpy.extract((self.spike_times <= self.t_stop), self.spike_times) 
     79 
     80        # We sort the spike_times. May be slower, but is necessary by the way for quite a  
     81        # lot of methods... 
     82        self.spike_times = numpy.sort(self.spike_times) 
    8483        # Here we deal with the t_start and t_stop values if the SpikeTrain 
    8584        # is empty, with only one element or several elements, if we 
     
    9190        size = len(spike_times) 
    9291        if size == 0: 
    93             if self.t_start is None: 
     92            if self.t_start is None:  
    9493                self.t_start = 0 
    9594            if self.t_stop is None: 
     
    111110 
    112111        if self.t_start >= self.t_stop : 
    113             raise Exception("Incompatible time interval for the creation of the SpikeTrain. t_start=%s, t_stop=%s" % (self.t_start, self.t_stop)) 
     112            raise Exception("Incompatible time interval : t_start = %s, t_stop = %s" % (self.t_start, self.t_stop)) 
    114113        if self.t_start < 0: 
    115114            raise ValueError("t_start must not be negative") 
     
    122121    def __len__(self): 
    123122        return len(self.spike_times) 
     123     
     124    def __getslice__(self, i, j): 
     125        """ 
     126        Return a sublist of the spike_times vector of the SpikeTrain 
     127        """ 
     128        return self.spike_times[i:j] 
     129     
     130    def copy(self): 
     131        """ 
     132        Return a copy of the SpikeTrain object 
     133        """ 
     134        return SpikeTrain(self.spike_times, self.dt, self.t_start, self.t_stop) 
     135     
     136    def __getdisplay__(self,display): 
     137        """ 
     138        Return a pylab object with a plot() function to draw the plots. 
     139         
     140        Inputs: 
     141            display - if True, a new figure is created. Otherwise, if display is a 
     142                      subplot object, this object is returned. 
     143        """ 
     144        if display is False: 
     145            return None 
     146        elif display is True: 
     147            pylab.figure() 
     148            return pylab 
     149        else: 
     150            return display 
     151     
     152    def __labels__(self, subplot, xlabel, ylabel): 
     153        """ 
     154        Function to put some labels on a plot 
     155         
     156        Inputs: 
     157            subplot - the targeted plot 
     158            xlabel  - a string for the x label 
     159            ylabel  - a string for the y label 
     160        """ 
     161        if hasattr(subplot, 'xlabel'): 
     162            subplot.xlabel(xlabel) 
     163            subplot.ylabel(ylabel) 
     164        else: 
     165            subplot.set_xlabel(xlabel) 
     166            subplot.set_ylabel(ylabel) 
    124167 
    125168    def duration(self): 
     169        """ 
     170        Return the duration of the SpikeTrain 
     171        """ 
    126172        return self.t_stop - self.t_start 
     173     
     174    def merge(self, spiketrain): 
     175        """ 
     176        Add the spike times from a spiketrain to the current SpikeTrain 
     177         
     178        Inputs: 
     179            spiketrain - The SpikeTrain that should be added 
     180         
     181        Examples: 
     182            >> a = SpikeTrain(range(0,100,10),0.1,0,100) 
     183            >> b = SpikeTrain(range(400,500,10),0.1,400,500) 
     184            >> a.merge(b) 
     185            >> a.spike_times 
     186            >> [   0.,   10.,   20.,   30.,   40.,   50.,   60.,   70.,   80., 
     187                90.,  400.,  410.,  420.,  430.,  440.,  450.,  460.,  470., 
     188                480.,  490.] 
     189            >> a.t_stop 
     190            >> 500 
     191        """ 
     192        self.spike_times = numpy.concatenate((self.spike_times, spiketrain.spike_times)) 
     193        self.spike_times = numpy.sort(self.spike_times) 
     194        self.t_start     = min(self.t_start, spiketrain.t_start) 
     195        self.t_stop      = max(self.t_stop, spiketrain.t_stop) 
    127196 
    128197    def format(self, relative=False, quantized=False): 
    129198        """ 
    130         a function to format a spike train from one format to another. 
    131         outputs a numpy array 
    132  
     199        Return an array with a new representation of the spike times 
     200         
     201        Inputs: 
     202            relative  - if True, spike times are expressed in a relative 
     203                       time compared to the previsous one 
     204            quantized - a value to divide spike times with before rounding 
     205             
     206        Examples: 
     207            >>> st.spikes_times=[0, 2.1, 3.1, 4.4] 
     208            >>> st.format(relative=True) 
     209            >>> [0, 2.1, 1, 1.3] 
     210            >>> st.format(quantized=2) 
     211            >>> [0, 1, 2, 2] 
    133212        """ 
    134213        spike_times = self.spike_times.copy() 
    135         spike_times.sort() 
    136  
    137         if relative and len(spike_times)>0: 
     214 
     215        if relative and len(spike_times) > 0: 
    138216            spike_times[1:] = spike_times[1:] - spike_times[:-1] 
    139217 
     
    145223        return spike_times 
    146224 
     225 
     226 
    147227    ####################################################################### 
    148228    ## Analysis methods that can be applied to a SpikeTrain object       ## 
    149229    ####################################################################### 
     230     
    150231    def isi(self): 
    151         # TODO this needs some thinking to know how to handle the border, in particular the 
    152         # first spike and t_start 
    153         return self.format(relative=True, quantized=False)[1:] 
    154  
    155     # Returns the mean firing rate of the SpikeTrain 
     232        """ 
     233        Return an array with the inter-spike intervals of the SpikeTrain 
     234         
     235        Examples: 
     236            >>> st.spikes_times=[0, 2.1, 3.1, 4.4] 
     237            >>> st.isi() 
     238            >>> [2.1, 1., 1.3] 
     239         
     240        See also 
     241            cv_isi 
     242        """ 
     243        return numpy.diff(self.spike_times) 
     244 
    156245    def mean_rate(self, t_start=None, t_stop=None): 
    157         """ Mean firing rate between t_start and t_stop in Hz 
    158  
    159         NOTE: avoided calling where when defaults settings t_start=self.t_start, t_stop=self.t_stop 
    160         """ 
    161         if (t_start==None) & (t_stop==None): 
    162             t_start=self.t_start 
    163             t_stop=self.t_stop 
    164             idx = self.spike_times 
    165         else: 
    166             if t_start==None: t_start=self.t_start 
    167             if t_stop==None: t_stop=self.t_stop 
     246        """  
     247        Return the mean firing rate between t_start and t_stop, in Hz 
     248         
     249        Inputs: 
     250            t_start - in ms. If not defined, the one of the SpikeTrain object is used 
     251            t_stop  - in ms. If not defined, the one of the SpikeTrain object is used 
     252        """ 
     253        if (t_start == None) & (t_stop == None): 
     254            t_start = self.t_start 
     255            t_stop  = self.t_stop 
     256            idx     = self.spike_times 
     257        else: 
     258            if t_start == None: t_start=self.t_start 
     259            if t_stop == None: t_stop=self.t_stop 
    168260            idx = numpy.where((self.spike_times >= t_start) & (self.spike_times <= t_stop))[0] 
    169261        return 1000.*len(idx)/(t_stop-t_start) 
    170262 
    171     # Returns the cv of the isi of the SpikeTrain 
    172263    def cv_isi(self): 
    173264        """ 
     265        Return the coefficient of variation of the isis. 
     266         
    174267        cv_isi is the ratio between the standard deviation and the mean of the ISI 
    175  
    176268          The irregularity of individual spike trains is measured by the squared 
    177269        coefficient of variation of the corresponding inter-spike interval (ISI) 
     
    181273        Poisson-type behavior. As a measure for irregularity in the network one 
    182274        can use the average irregularity across all neurons. 
    183  
    184         TODO: is it useful to get the std of CV? 
     275         
     276        See also 
     277            isi 
     278 
    185279        """ 
    186280        isi = self.isi() 
    187         #TODO return an exception if mean.isi= 0 
    188281        if len(isi) > 0: 
    189282            return numpy.std(isi)/numpy.mean(isi) 
     
    192285 
    193286    def fano_factor_isi(self): 
    194         """ returns the fano factor of this spike trains ISI (see SpikeList.fano_factor)""" 
     287        """  
     288        Return the fano factor of this spike trains ISI. 
     289         
     290        The Fano Factor is defined as the variance of the isi divided by the mean of the isi 
     291         
     292        See also 
     293            isi, cv_isi 
     294        """ 
    195295        isi = self.isi() 
    196296        if len(isi) > 0: 
    197             #firing_rate = self.time_histogram(time_bin,False)       
    198297            fano = numpy.var(isi)/numpy.mean(isi) 
    199298            return fano 
     
    201300            raise Exception("No spikes in the SpikeTrain !") 
    202301 
    203     def time_axis(self, time_bin): 
     302    def time_axis(self, time_bin=10): 
    204303        """ 
    205304        Return a time axis between t_start and t_stop according to a time_bin 
     305         
     306        Inputs: 
     307            time_bin - the bin width 
     308             
     309        Examples: 
     310            >>> st = SpikeTrain(range(100),0.1,0,100) 
     311            >>> st.time_axis(10) 
     312            >>> [ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90] 
     313         
     314        See also 
     315            time_histogram 
     316 
    206317        """ 
    207318        return numpy.arange(self.t_start, self.t_stop, time_bin) 
    208319 
    209     def raster_plot(self, t_start=None, t_stop=None, color='b'): 
     320    def raster_plot(self, t_start=None, t_stop=None, display=True, kwargs={}): 
    210321        """ 
    211322        Generate a raster plot with the SpikeTrain in a subwindow of interest, 
    212         defined by t_start and t_stop. Those are not the global t_start and t_stop 
    213         of the SpikeTrain objects. If not defined, we use the one of the SpikeTrain 
    214         object 
    215         """ 
    216         if t_start is None: 
    217             t_start = self.t_start 
    218         if t_stop is None: 
    219             t_stop = self.t_stop 
    220         idx = numpy.where((self.spike_times >= t_start) & (self.spike_times <= t_stop))[0] 
    221         spikes = self.spike_times[idx] 
    222         if len(spikes) > 0: 
    223             pylab.figure() 
    224             pylab.scatter(spikes,numpy.ones(len(spikes)), c=color) 
    225             pylab.xlabel("Time (ms)", size="x-large") 
    226             pylab.ylabel("Neuron #", size="x-large") 
    227  
    228     # Method to sort a SpikeTrain 
    229     def sort_by_time(self): 
    230         self.spike_time = sort(self.spike_time) 
    231  
    232     def subSpikeTrain(self, t_start, t_stop): 
    233         """ Returns a spike train sliced between t_start and t_stop 
    234         t_start and t_stop may either be single values or sequences of start 
    235         and stop times. 
    236         """ 
    237         if hasattr(t_start, '__len__'): 
    238             assert len(t_start) == len(t_stop) 
    239             mask = False 
    240             for t0,t1 in zip(t_start, t_stop): 
    241                 mask = mask | (self.spike_times >= t0) & (self.spike_times <= t1) 
    242             t_start = t_start[0] 
    243             t_stop = t_stop[-1] 
    244         else: 
    245             mask = (self.spike_times >= t_start) & (self.spike_times <= t_stop) 
    246         idx = numpy.where(mask)[0] 
    247         spikes = self.spike_times[idx] 
     323        defined by t_start and t_stop.  
     324         
     325        Inputs: 
     326            t_start - in ms. If not defined, the one of the SpikeTrain object is used 
     327            t_stop  - in ms. If not defined, the one of the SpikeTrain object is used 
     328            display - if True, a new figure is created. Could also be a subplot 
     329            kwargs  - dictionary contening extra parameters that will be sent to the plot  
     330                      function 
     331         
     332        Examples: 
     333            >>> z = subplot(221) 
     334            >>> st.raster_plot(display=z, kwargs={'color':'r'}) 
     335         
     336        See also 
     337            SpikeList.raster_plot 
     338        """ 
     339        if t_start is None: t_start = self.t_start 
     340        if t_stop is None:  t_stop = self.t_stop 
     341        spikes = numpy.extract((self.spike_times >= t_start) & (self.spike_times <= t_stop), self.spike_times) 
     342        subplot = self.__getdisplay__(display) 
     343        if not subplot or not ENABLE_PLOTS: 
     344            print MATPLOTLIB_ERROR 
     345            return 
     346        else: 
     347            if len(spikes) > 0: 
     348                xlabel = "Time (ms)" 
     349                ylabel = "Neurons #" 
     350                self.__labels__(subplot, xlabel, ylabel) 
     351                subplot.scatter(spikes,numpy.ones(len(spikes)),**kwargs) 
     352                pylab.draw() 
     353 
     354    def add_offset(self, offset): 
     355        """ 
     356        Add an offset to the SpikeTrain object. t_start and t_stop are 
     357        shifted from offset, so does all the spike times. 
     358          
     359        Inputs: 
     360            offset - the time offset, in ms 
     361         
     362        Examples: 
     363            >>>spktrain = SpikeTrain(arange(0,100,10)) 
     364            >>>spktrain.add_offset(50) 
     365            >>>spklist.spike_times 
     366            >>>[  50.,   60.,   70.,   80.,   90.,  100.,  110.,   
     367                120.,  130.,  140.] 
     368        """ 
     369        self.t_start += offset 
     370        self.t_stop  += offset 
     371        self.spike_times += offset 
     372 
     373    def time_slice(self, t_start, t_stop): 
     374        """  
     375        Return a new SpikeTrain obtained by slicing between t_start and t_stop 
     376         
     377        Inputs: 
     378            t_start - begining of the new SpikeTrain, in ms. 
     379            t_stop  - end of the new SpikeTrain, in ms. 
     380 
     381        """ 
     382        spikes = numpy.extract((self.spike_times >= t_start) & (self.spike_times <= t_stop), self.spike_times) 
    248383        return SpikeTrain(spikes, self.dt, t_start, t_stop) 
    249384 
    250     def time_histogram(self, time_bin , normalized=True): 
     385 
     386    def time_histogram(self, time_bin=10, normalized=True): 
    251387        """ 
    252388        Bin the spikes with the specified bin width. The first and last bins 
    253389        are calculated from `self.t_start` and `self.t_stop`. 
    254         If `normalized` is True, the bin values are scaled so as to represent 
    255         firing rates in spikes/second, otherwise it's the number of spikes per bin. 
    256         """ 
    257         if hasattr(time_bin, '__len__'): 
    258             bins = time_bin 
    259         else: 
    260             bins = self.time_axis(time_bin) 
    261          
    262         (hist, lower_edges ) = numpy.histogram(self.spike_times, bins) 
     390         
     391        Inputs: 
     392            time_bin   - the bin width for gathering spikes_times 
     393            normalized - if True, the bin values are scaled to represent firing rates 
     394                         in spikes/second, otherwise otherwise it's the number of spikes  
     395                         per bin. 
     396         
     397        Examples: 
     398            >>> st=SpikeTrain(range(0,100,5),0.1,0,100) 
     399            >>> st.time_histogram(10) 
     400            >>> [200, 200, 200, 200, 200, 200, 200, 200, 200, 200] 
     401            >>> st.time_histogram(10, normalized=False) 
     402            >>> [2, 2, 2, 2, 2, 2, 2, 2, 2, 2] 
     403         
     404        See also 
     405            time_axis 
     406        """ 
     407        bins = self.time_axis(time_bin) 
     408        hist, lower_edges = numpy.histogram(self.spike_times, bins) 
    263409        if normalized and isinstance(time_bin, int): # what about normalization if time_bin is a sequence? 
    264410            hist *= 1000.0/time_bin 
     
    267413    def relative_times(self): 
    268414        """ 
    269         Rescale spike times to make them relative to t_start. 
    270         t_start becomes 0.""" 
     415        Rescale the spike times to make them relative to t_start. 
     416         
     417        Note that the SpikeTrain object itself is modified, t_start  
     418        is substracted to spike_times, t_start and t_stop 
     419        """ 
    271420        if self.t_start != 0: 
    272421            self.spike_times -= self.t_start 
    273             self.t_stop -= self.t_start 
    274             self.t_start = 0.0 
    275  
     422            self.t_stop      -= self.t_start 
     423            self.t_start      = 0.0 
     424 
     425    def VictorPurpuraDistance(self, spktrain, cost): 
     426        """ 
     427        Function to calculate the Victor-Purpura distance between two spike trains. 
     428        See J. D. Victor and K. P. Purpura, 
     429            Nature and precision of temporal coding in visual cortex: a metric-space 
     430            analysis., 
     431            J Neurophysiol,76(2):1310-1326, 1996 
     432         
     433        Inputs: 
     434            spktrain - the other SpikeTrain 
     435            cost     - The cost parameter. See the paper for more informations 
     436        """ 
     437        nspk_1      = len(self) 
     438        nspk_2      = len(spktrain) 
     439        if cost == 0:  
     440            return abs(nspk_1-nspk_2) 
     441        elif cost > 1e9 : 
     442            return nspk_1+nspk_2 
     443        scr = numpy.zeros((nspk_1+1,nspk_2+1)) 
     444        scr[:,0] = numpy.arange(0,nspk_1+1) 
     445        scr[0,:] = numpy.arange(0,nspk_2+1) 
     446             
     447        if nspk_1 > 0 and nspk_2 > 0: 
     448            for i in xrange(1,nspk_1+1): 
     449                for j in xrange(1,nspk_2+1): 
     450                    scr[i,j] = min(scr[i-1,j]+1,scr[i,j-1]+1) 
     451                    scr[i,j] = min(scr[i,j],scr[i-1,j-1]+cost*abs(self.spike_times[i-1]-spktrain.spike_times[j-1])) 
     452        return scr[nspk_1,nspk_2] 
     453 
     454 
     455    def KreuzDistance(self, spktrain): 
     456        """ 
     457        Function to calculate the Kreuz/Politi distance between two spike trains 
     458        See Kreuz, T.; Haas, J.S.; Morelli, A.; Abarbanel, H.D.I. & Politi,  
     459        A. Measuring spike train synchrony.  
     460        J Neurosci Methods, 2007, 165, 151-161 
     461 
     462        Inputs: 
     463            spktrain - the other SpikeTrain 
     464        """ 
     465        N              = (self.t_stop-self.t_start)/self.dt 
     466        vec_1          = numpy.zeros(N) 
     467        vec_2          = numpy.zeros(N) 
     468        result         = numpy.zeros(N) 
     469        idx_spikes     = self.spike_times/self.dt 
     470        previous_spike = 0 
     471        for spike in idx_spikes: 
     472            vec_1[previous_spike:spike] = (spike-previous_spike)*self.dt 
     473            previous_spike = spike 
     474        vec_1[previous_spike-1:N] = vec_1[previous_spike-2] 
     475        idx_spikes     = spktrain.spike_times/self.dt 
     476        previous_spike = 0 
     477        for spike in idx_spikes: 
     478            vec_2[previous_spike:spike] = (spike-previous_spike)*self.dt 
     479            previous_spike = spike 
     480        vec_2[previous_spike-1:N] = vec_2[previous_spike-2] 
     481        idx = numpy.where(vec_1 <= vec_2)[0] 
     482        result[idx] = vec_1[idx]/vec_2[idx] - 1 
     483        idx = numpy.where(vec_1 > vec_2)[0] 
     484        result[idx] = -(vec_2[idx]/vec_1[idx] -1) 
     485        return numpy.sum(numpy.abs(result)) 
     486     
     487     
     488    def record(self, filename = 'SpikeTrain.wav', fs=44100): 
     489        """  
     490        Fancy tool, not really useful but still... 
     491        Record the 'sound' produced by a SpikeTrain. Need the scikits.audiolab 
     492        package 
     493         
     494        Inputs: 
     495            filename - the name of the output file (should have .wav extension) 
     496            fs       - the sampling rate 
     497         
     498        Examples: 
     499            >>> spktrain.record("my_sound.wav") 
     500        """ 
     501        # from the spike list 
     502        simtime_seconds = (self.t_stop - self.t_start)/1000. 
     503        (trace,time) = numpy.histogram(self.spike_times*1000., fs*simtime_seconds) 
     504        # TODO convolve with proper spike... 
     505        spike = numpy.ones((fs/1000.,)) # one ms 
     506        trace = numpy.convolve(trace, spike, mode='same')#/2.0 
     507        trace /= numpy.abs(trace).max() * 1.1 
     508        try: 
     509            from scikits.audiolab import wavwrite 
     510        except ImportError: 
     511            print "You need the scikits.audiolab package to produce sounds !" 
     512        wavwrite(trace, filename, fs = fs, enc = 'pcm26') 
     513 
     514    #################################################################### 
     515    ### TOO SPECIFIC METHOD ? 
     516    ### Better documentation 
     517    #################################################################### 
    276518    def tuning_curve(self, var_array, normalized=False, method='sum'): 
    277519        """ 
     
    320562        return tuning_curve 
    321563 
     564 
     565    #################################################################### 
     566    ### TOO SPECIFIC METHOD ? 
     567    ### Better documentation 
     568    #################################################################### 
    322569    def frequency_spectrum(self, time_bin): 
    323570        """ 
     
    325572        frequency axis. 
    326573        """ 
    327         hist = self.time_histogram(time_bin, normalized=False) 
     574        hist       = self.time_histogram(time_bin, normalized=False) 
    328575        freq_spect = analysis.simple_frequency_spectrum(hist) 
    329         freq_bin = 1000.0/self.duration() # Hz 
    330         freq_axis = numpy.arange(len(freq_spect)) * freq_bin     
     576        freq_bin   = 1000.0/self.duration() # Hz 
     577        freq_axis = numpy.arange(len(freq_spect)) * freq_bin     
    331578        return freq_spect, freq_axis     
    332579 
     580 
     581    #################################################################### 
     582    ### TOO SPECIFIC METHOD ? 
     583    ### Better documentation 
     584    #################################################################### 
    333585    def f1f0_ratio(self, time_bin, f_stim): 
    334586        """ 
     
    346598            raise Exception(errmsg) 
    347599        return F1/F0 
    348      
    349     def merge(self, spiketrain, relative_times=False): 
    350         """ 
    351         Add the spike times from `spiketrain` to this `SpikeTrain`s spike list. 
    352         """ 
    353         if relative_times: 
    354             self.relative_times() 
    355             spiketrain.relative_times() 
    356         self.spike_times = numpy.concatenate((self.spike_times, spiketrain.spike_times)) 
    357         self.spike_times.sort() 
    358         self.t_start = min(self.t_start, spiketrain.t_start) 
    359         self.t_stop = max(self.t_stop, spiketrain.t_stop) 
    360          
     600         
     601 
    361602 
    362603class SpikeList(object): 
    363604    """ 
    364     A SpikeList object is a list of SpikeTrain objects. 
    365  
    366     >>> sl = SpikeList(3, [(0, 0.1), (1, 0.1), (0, 0.2)]) 
    367     >>> type( sl.spiketrains[0] ) 
    368     <type SpikeTrain> 
    369     >>> sl.spiketrains[0].spike_times 
    370     array([ 0.1,  0.2]
    371     >>> sl.as_ids_times() 
    372     (array([0,0,1]), array([0.1,0.2,0.1])) 
    373     >>> sl.as_ids_times(relative=True) 
    374     (array([0,1,0]), array([0.1,0.0,0.1])) 
    375     >>> sl.as_ids_times(quantized=0.01) 
    376     (array([0,0,1]), array([10,20,10])) 
    377     >>> sl.as_list_id_time() 
    378     [(0,0.1), (0,0.2), (1,0.1)] 
    379     >>> sl.as_id_list_times(
    380     [(0, array([0.1, 0.2])), (1, array([0.1]))] 
    381     >>> sl.as_time_list_ids() 
    382     [(0.1, [0,1]), (0.2, [0])] 
    383     >>> sl.as_2byN_array() 
    384     >>> array([[0,0,1],[0.1,0.2,0.1]]) 
     605    SpikeList(spikes, id_list, dt=None, t_start=None, t_stop=None, dims=None) 
     606     
     607    Return a SpikeList object which will be a list of SpikeTrain objects. 
     608 
     609    Inputs: 
     610        spikes  - a list of (id,time) tuples (id being in id_list) 
     611        is_list - the list of the ids of all recorded cells (needed for silent cells
     612        dt      - if dt is specified, time values should be floats 
     613        t_start - begining of the SpikeList, in ms. If None, will be infered from the data 
     614        t_stop  - end of the SpikeList, in ms. If None, will be infered from the data 
     615        dims    - dimensions of the recorded population, if not 1D population 
     616     
     617    dt, t_start and t_stop are shared for all SpikeTrains object within the SpikeList 
     618     
     619    Examples: 
     620        >>> sl = SpikeList(3, [(0, 0.1), (1, 0.1), (0, 0.2)]
     621        >>> type( sl[0] ) 
     622        >>> <type SpikeTrain> 
     623     
     624    See also 
     625        loadSpikeList 
    385626 
    386627    """ 
     
    388629    ## Constructor and key methods to manipulate the SpikeList objects   ## 
    389630    ####################################################################### 
    390     def __init__(self, spikes, id_list, dt=None, t_start=None, t_stop=None, label=''): 
    391         """ 
    392         `spikes` is a list/tuple of (id,t) tuples (id in id_list) 
    393         `id_list` is the list of ids of all recorded cells (needed for silent cells) 
    394         If `dt`is specified, the time values should be ints, 
    395         and will be multiplied by `dt`, otherwise time values should be floats. 
    396         If `t_start` and `t_stop` are not specified, they are inferred from the data. 
    397  
    398         dt, t_start and t_stop are shared for all SpikeTrains 
    399  
    400         """ 
    401         self.id_list = id_list 
    402         self.t_start = t_start 
    403         self.t_stop = t_stop 
    404   &n