root/trunk/src/io.py

Revision 320, 13.5 kB (checked in by pierre, 2 months ago)

Fix some wrongs examples in doc/io.txt, and in the meanwhile consolidate the internal methods of StandardTextFile?.

Line 
1 """
2 NeuroTools.io
3 ==================
4
5 A collection of functions to handle all the inputs/outputs of the NeuroTools.signals
6 file, used by the loaders.
7
8 Classes
9 -------
10
11 FileHandler        - abstract class which should be overriden, managing how a file will load/write
12                      its data
13 StandardTextFile   - object used to manipulate text representation of NeuroTools objects (spikes or
14                      analog signals)
15 StandardPickleFile - object used to manipulate pickle representation of NeuroTools objects (spikes or
16                      analog signals)
17 DataHandler        - object to establish the interface between NeuroTools.signals and NeuroTools.io
18
19 All those objects can be used with NeuroTools.signals
20
21     >> data = StandardTextFile("my_data.dat")
22     >> spikes = load(data,'s')
23 """
24
25
26 from NeuroTools import check_dependency
27
28 import os, logging, cPickle, numpy
29 DEFAULT_BUFFER_SIZE = 10000
30
31
32 class FileHandler(object):
33     """
34     Class to handle all the file read/write methods for the key objects of the
35     signals class, i.e SpikeList and AnalogSignalList. Could be extented
36     
37     This is an abstract class that will be implemented for each format (txt, pickle, hdf5)
38     The key methods of the class are:
39         write(object)              - Write an object to a file
40         read_spikes(params)        - Read a SpikeList file with some params
41         read_analogs(type, params) - Read an AnalogSignalList of type `type` with some params
42     
43     Inputs:
44         filename - the file name for reading/writing data
45     
46     If you want to implement your own file format, you just have to create an object that will
47     inherit from this FileHandler class and implement the previous functions. See io.py for more
48     details
49     """
50    
51     def __init__(self, filename):
52         self.filename = filename
53    
54     def write(self, object):
55         """
56         Write the object to the file.
57         
58         Examples:
59             >> handler.write(SpikeListObject)
60             >> handler.write(VmListObject)
61         """
62         return _abstract_method(self)
63    
64     def read_spikes(self, params):
65         """
66         Read a SpikeList object from a file and return the SpikeList object, created from the File and
67         from the additional params that may have been provided
68         
69         Examples:
70             >> params = {'id_list' : range(100), 't_stop' : 1000}
71             >> handler.read_spikes(params)
72                 SpikeList Object (with params taken into account)
73         """
74         return _abstract_method(self)
75    
76     def read_analogs(self, type, params):
77         """
78         Read an AnalogSignalList object from a file and return the AnalogSignalList object of type
79         `type`, created from the File and from the additional params that may have been provided
80         
81         `type` can be in ["vm", "current", "conductance"]
82         
83         Examples:
84             >> params = {'id_list' : range(100), 't_stop' : 1000}
85             >> handler.read_analogs("vm", params)
86                 VmList Object (with params taken into account)
87             >> handler.read_analogs("current", params)
88                 CurrentList Object (with params taken into account)
89         """
90         if not type in ["vm", "current", "conductance"]:
91             raise Exception("The type %s is not available for the Analogs Signals" %type)
92         return _abstract_method(self)
93
94
95
96 class StandardTextFile(FileHandler):
97    
98     def __init__(self, filename):
99         FileHandler.__init__(self, filename)
100         self.metadata = {}
101
102     def __read_metadata(self):
103         """
104         Read the informations that may be contained in the header of
105         the NeuroTools object, if saved in a text file
106         """
107         cmd = ''
108         f = open(self.filename, 'r')
109         for line in f.readlines():
110             if line[0] == '#':
111                 cmd += line[1:].strip() + ';'
112             else:
113                 break
114         f.close()
115         exec cmd in None, self.metadata
116
117     def __fill_metadata(self, object):
118         """
119         Fill the metadata from those of a NeuroTools object before writing the object
120         """
121         self.metadata['dimensions'] = str(object.dimensions)
122         if len(object.id_list() > 0):
123             self.metadata['first_id'] = numpy.min(object.id_list())
124             self.metadata['last_id']  = numpy.max(object.id_list())
125         if hasattr(object, "dt"):
126             self.metadata['dt']     = object.dt
127
128     def __check_params(self, params):
129         """
130         Establish a control/completion/correction of the params to create an object by
131         using comparison and data extracted from the metadata.
132         """
133         if 'dt' in params:
134             if params['dt'] is None and 'dt' in self.metadata:
135                 logging.debug("dt is infered from the file header")
136                 params['dt'] = self.metadata['dt']
137         if not ('id_list' in params) or (params['id_list'] is None):
138             if ('first_id' in self.metadata) and ('last_id' in self.metadata):
139                 logging.debug("id_list is infered from the file header")
140                 params['id_list'] = range(int(self.metadata['first_id']), int(self.metadata['last_id'])+1)
141             else:
142                 raise Exception("id_list can not be infered while reading %s" %self.filename)
143         elif isinstance(params['id_list'], int): # allows to just specify the number of neurons
144             params['id_list'] = range(params['id_list'])
145         elif not isinstance(params['id_list'], list):
146             raise Exception("id_list should be an int or a list !")
147         if not ('dims' in params) or (params['dims'] is None):
148             if 'dimensions' in self.metadata:
149                 params['dims'] = self.metadata['dimensions']
150             else:
151                 raise Exception("dims can not be infered while reading %s" %self.filename)
152         return params
153            
154     def get_data(self, sepchar = "\t", skipchar = "#"):
155         """
156         Load data from a text file and returns a list of data
157         """
158         myfile = open(self.filename, "r", DEFAULT_BUFFER_SIZE)
159         contents = myfile.readlines()
160         myfile.close()
161         data = []
162         for line in contents:
163             line = line.strip()
164             if (len(line) != 0):
165                 if (line[0] != skipchar):
166                     line = line.split(sepchar)
167                     id    = [int(float(line[-1]))]
168                     id.extend(map(float, line[0:-1]))
169                     data.append(id)
170         return data
171
172     def write(self, object):
173         # can we write to the file more than once? In this case, should use seek, tell
174         # to always put the header information at the top?
175         # write header
176         self.__fill_metadata(object)
177         fileobj = open(self.filename, 'w', DEFAULT_BUFFER_SIZE)
178         header_lines = ["# %s = %s" % item for item in self.metadata.items()]
179         fileobj.write("\n".join(header_lines) + '\n')
180         numpy.savetxt(fileobj, object.raw_data(), fmt = '%g', delimiter='\t')
181         fileobj.close()
182
183     def read_spikes(self, params):
184         fileobj = open(self.filename, 'r', DEFAULT_BUFFER_SIZE)
185         self.__read_metadata()
186         p = self.__check_params(params)
187         from NeuroTools import signals
188         return signals.SpikeList(self.get_data(), p['id_list'], p['t_start'], p['t_stop'], p['dims'])
189
190     def read_analogs(self, type, params):
191         fileobj = open(self.filename, 'r', DEFAULT_BUFFER_SIZE)
192         self.__read_metadata()
193         p = self.__check_params(params)
194         from NeuroTools import signals
195         if type == "vm":
196             return signals.VmList(self.get_data(), p['id_list'], p['dt'], p['t_start'], p['t_stop'], p['dims'])
197         elif type == "current":
198             return signals.CurrentList(self.get_data(), p['id_list'], p['dt'], p['t_start'], p['t_stop'], p['dims'])
199         elif type == "conductance":
200             data  = numpy.array(self.get_data())
201             if len(data[0,:]) > 2:
202                 g_exc = signals.ConductanceList(data[:,[0,1]] , p['id_list'], p['dt'], p['t_start'], p['t_stop'], p['dims'])
203                 g_inh = signals.ConductanceList(data[:,[0,2]] , p['id_list'], p['dt'], p['t_start'], p['t_stop'], p['dims'])
204                 return [g_exc, g_inh]
205             else:
206                 return signals.ConductanceList(data, p['id_list'], p['dt'], p['t_start'], p['t_stop'], p['dims'])
207
208
209 class StandardPickleFile(FileHandler):
210    
211     def __init__(self, filename):
212         FileHandler.__init__(self, filename)
213         self.metadata = {}
214
215     def __fill_metadata(self, object):
216         """
217         Fill the metadata from those of a NeuroTools object before writing the object
218         """
219         self.metadata['dimensions'] = str(object.dimensions)
220         self.metadata['first_id']   = numpy.min(object.id_list())
221         self.metadata['last_id']    = numpy.max(object.id_list())
222         if hasattr(object, 'dt'):
223             self.metadata['dt']     = object.dt
224
225     def __reformat(self, params, object):
226         self.__fill_metadata(object)
227         if 'id_list' in params and params['id_list'] != None:
228             if isinstance(params['id_list'], int): # allows to just specify the number of neurons
229                 params['id_list'] = range(params['id_list'])
230             if params['id_list'] != range(int(self.metadata['first_id']), int(self.metadata['last_id'])+1):
231                 object = object.id_slice(params['id_list'])
232         do_slice = False
233         t_start = object.t_start
234         t_stop  = object.t_stop
235         if 't_start' in params and params['t_start'] is not None and params['t_start'] != object.t_start:
236             t_start = params['t_start']
237             do_slice = True
238         if 't_stop' in params and params['t_stop'] is not None and params['t_stop'] != object.t_stop:
239             t_stop = params['t_stop']
240             do_slice = True
241         if do_slice:
242             object = object.time_slice(t_start, t_stop)
243         return object
244    
245     def write(self, object):
246         fileobj = file(self.filename,"w")
247         return cPickle.dump(object, fileobj)
248
249     def read_spikes(self, params):
250         fileobj = file(self.filename,"r")
251         object = cPickle.load(fileobj)
252         object = self.__reformat(params, object)
253         return object
254        
255     def read_analogs(self, type, params):
256         return self.read_spikes(params)
257
258
259 class DataHandler(object):
260     """
261     Class to establish the interface for loading/saving objects in NeuroTools
262     
263     Inputs:
264         filename - the user file for reading/writing data. By default, if this is
265                    string, a StandardTextFile is created
266         object   - the object to be saved. Could be a SpikeList or an AnalogSignalList
267         
268     Examples:
269         >> txtfile = StandardTextFile("results.dat")
270         >> DataHandler(txtfile)
271         >> picklefile = StandardPickelFile("results.dat")
272         >> DataHandler(picklefile)
273         
274     """
275     def __init__(self, user_file, object = None):
276         if type(user_file) == str:
277             user_file = StandardTextFile(user_file)
278         elif not isinstance(user_file, FileHandler):
279             raise Exception ("The user_file object should be a string or herits from FileHandler !")
280         self.user_file     = user_file
281         self.object        = object
282
283     def load_spikes(self, **params):
284         """
285         Function to load a SpikeList object from a file. The data type is automatically
286         infered. Return a SpikeList object
287         
288         Inputs:
289             params - a dictionnary with all the parameters used by the SpikeList constructor
290         
291         Examples:
292             >> params = {'id_list' : range(100), 't_stop' : 1000}
293             >> handler.load_spikes(params)
294                 SpikeList object
295         
296         See also
297             SpikeList, load_analogs
298         """
299        
300         ### Here we should have an automatic selection of the correct manager
301         ### acccording to the file format.
302         ### For the moment, we try the pickle format, and if not working
303         ### we assume this is a text file
304         return self.user_file.read_spikes(params)
305
306
307     def load_analogs(self, type, **params):
308         """
309         Read an AnalogSignalList object from a file and return the AnalogSignalList object of type
310         `type`, created from the File and from the additional params that may have been provided
311         
312         `type` can be in ["vm", "current", "conductance"]
313         
314         Examples:
315             >> params = {'id_list' : range(100), 't_stop' : 1000}
316             >> handler.load_analogs("vm", params)
317                 VmList Object (with params taken into account)
318             >> handler.load_analogs("current", params)
319                 CurrentList Object (with params taken into account)
320         
321         See also
322             AnalogSignalList, load_spikes
323         """
324         ### Here we should have an automatic selection of the correct manager
325         ### acccording to the file format.
326         ### For the moment, we try the pickle format, and if not working
327         ### we assume this is a text file
328         return self.user_file.read_analogs(type, params)
329
330
331     def save(self):
332         """
333         Save the object defined in self.object with the method os self.user_file
334         
335         Note that you can add your own format for I/O of such NeuroTools objects
336         """
337         ### Here, you could add your own format if you have created the appropriate
338         ### manager.
339         ### The methods of the manager are quite simple: should just inherits from the FileHandler
340         ### class and have read() / write() methods
341         if self.object == None:
342             raise Exception("No object has been defined to be saved !")
343         else:
344             self.user_file.write(self.object)
Note: See TracBrowser for help on using the browser.