# HG changeset patch # User fsavard # Date 1268270060 18000 # Node ID cde71d24f2353f4e67615f23772e1ec66bf952ad # Parent 4c137f16b01381dbd55c5469e2c1b3ab30def232# Parent de3aef84714a335b916a33b82213a99e01459591 Merge diff -r de3aef84714a -r cde71d24f235 utils/seriestables/series.py --- a/utils/seriestables/series.py Wed Mar 10 17:08:50 2010 -0500 +++ b/utils/seriestables/series.py Wed Mar 10 20:14:20 2010 -0500 @@ -1,5 +1,6 @@ from tables import * import numpy +import time ''' The way these "IsDescription constructor" work is simple: write the @@ -11,7 +12,20 @@ otherwise. ''' -def get_beginning_description_n_ints(int_names, int_width=64): +def _get_description_timestamp_cpuclock_columns(store_timestamp, store_cpuclock, pos=0): + toexec = "" + + if store_timestamp: + toexec += "\ttimestamp = Time32Col(pos="+str(pos)+")\n" + pos += 1 + + if store_cpuclock: + toexec += "\tcpuclock = Float64Col(pos="+str(pos)+")\n" + pos += 1 + + return toexec, pos + +def _get_description_n_ints(int_names, int_width=64, pos=0): """ Begins construction of a class inheriting from IsDescription to construct an HDF5 table with index columns named with int_names. @@ -22,16 +36,17 @@ if int_width == 32: int_constructor = "Int32Col" - toexec = "class LocalDescription(IsDescription):\n" - - pos = 0 + toexec = "" for n in int_names: toexec += "\t" + n + " = " + int_constructor + "(pos=" + str(pos) + ")\n" + pos += 1 - return toexec + return toexec, pos -def get_description_with_n_ints_n_floats(int_names, float_names, int_width=64, float_width=32): +def _get_description_with_n_ints_n_floats(int_names, float_names, + int_width=64, float_width=32, + store_timestamp=True, store_cpuclock=True): """ Constructs a class to be used when constructing a table with PyTables. @@ -49,29 +64,40 @@ Type of ints. float_width : {'32', '64'} Type of floats. + store_timestamp : bool + See __init__ of Series + store_cpuclock : bool + See __init__ of Series Returns ------- A class object, to pass to createTable() """ - toexec = get_beginning_description_n_ints(int_names, int_width=int_width) + toexec = "class LocalDescription(IsDescription):\n" + + toexec_, pos = _get_description_timestamp_cpuclock_columns(store_timestamp, store_cpuclock) + toexec += toexec_ + + toexec_, pos = _get_description_n_ints(int_names, int_width=int_width, pos=pos) + toexec += toexec_ float_constructor = "Float32Col" if float_width == 64: float_constructor = "Float64Col" - - pos = len(int_names) for n in float_names: toexec += "\t" + n + " = " + float_constructor + "(pos=" + str(pos) + ")\n" + pos += 1 exec(toexec) return LocalDescription class Series(): - def __init__(self, table_name, hdf5_file, index_names=('epoch',), title="", hdf5_group='/'): + def __init__(self, table_name, hdf5_file, index_names=('epoch',), + title="", hdf5_group='/', + store_timestamp=True, store_cpuclock=True): """Basic arguments each Series must get. Parameters @@ -86,15 +112,26 @@ Title to attach to this table as metadata. Can contain spaces and be longer then the table_name. hdf5_group : str Path of the group (kind of a file) in the HDF5 file under which to create the table. + store_timestamp : bool + Whether to create a column for timestamps and store them with each record. + store_cpuclock : bool + Whether to create a column for cpu clock and store it with each record. """ self.table_name = table_name self.hdf5_file = hdf5_file self.index_names = index_names self.title = title + self.store_timestamp = store_timestamp + self.store_cpuclock = store_cpuclock + def append(self, index, element): raise NotImplementedError + def _timestamp_cpuclock(self, newrow): + newrow["timestamp"] = time.time() + newrow["cpuclock"] = time.clock() + # To put in a series dictionary instead of a real series, to do nothing # when we don't want a given series to be saved. class DummySeries(): @@ -102,8 +139,11 @@ pass class ErrorSeries(Series): - def __init__(self, error_name, table_name, hdf5_file, index_names=('epoch',), title="", hdf5_group='/'): - Series.__init__(self, table_name, hdf5_file, index_names, title) + def __init__(self, error_name, table_name, + hdf5_file, index_names=('epoch',), + title="", hdf5_group='/', + store_timestamp=True, store_cpuclock=True): + Series.__init__(self, table_name, hdf5_file, index_names, title, store_timestamp, store_cpuclock) self.error_name = error_name @@ -112,7 +152,7 @@ self._table = hdf5_file.createTable(hdf5_group, self.table_name, table_description, title=title) def _get_table_description(self): - return get_description_with_n_ints_n_floats(self.index_names, (self.error_name,)) + return _get_description_with_n_ints_n_floats(self.index_names, (self.error_name,)) def append(self, index, error): """ @@ -134,6 +174,8 @@ newrow[col_name] = value newrow[self.error_name] = error + self._timestamp_cpuclock(newrow) + newrow.append() self.hdf5_file.flush() @@ -187,15 +229,20 @@ # Outside of class to fix an issue with exec in Python 2.6. # My sorries to the God of pretty code. -def _BasicStatisticsSeries_construct_table_toexec(index_names): - toexec = get_beginning_description_n_ints(index_names) +def _BasicStatisticsSeries_construct_table_toexec(index_names, store_timestamp, store_cpuclock): + toexec = "class LocalDescription(IsDescription):\n" + + toexec_, pos = _get_description_timestamp_cpuclock_columns(store_timestamp, store_cpuclock) + toexec += toexec_ - bpos = len(index_names) - toexec += "\tmean = Float32Col(pos=" + str(bpos) + ")\n" - toexec += "\tmin = Float32Col(pos=" + str(bpos+1) + ")\n" - toexec += "\tmax = Float32Col(pos=" + str(bpos+2) + ")\n" - toexec += "\tstd = Float32Col(pos=" + str(bpos+3) + ")\n" - + toexec_, pos = _get_description_n_ints(index_names, pos=pos) + toexec += toexec_ + + toexec += "\tmean = Float32Col(pos=" + str(pos) + ")\n" + toexec += "\tmin = Float32Col(pos=" + str(pos+1) + ")\n" + toexec += "\tmax = Float32Col(pos=" + str(pos+2) + ")\n" + toexec += "\tstd = Float32Col(pos=" + str(pos+3) + ")\n" + # This creates "LocalDescription", which we may then use exec(toexec) @@ -215,8 +262,11 @@ stats_functions : dict, optional Dictionary with a function for each key "mean", "min", "max", "std". The function must take whatever is passed to append(...) and return a single number (float). """ - def __init__(self, table_name, hdf5_file, stats_functions=basic_stats_functions, index_names=('epoch',), title="", hdf5_group='/'): - Series.__init__(self, table_name, hdf5_file, index_names, title) + def __init__(self, table_name, hdf5_file, + stats_functions=basic_stats_functions, + index_names=('epoch',), title="", hdf5_group='/', + store_timestamp=True, store_cpuclock=True): + Series.__init__(self, table_name, hdf5_file, index_names, title, store_timestamp, store_cpuclock) self.hdf5_group = hdf5_group @@ -225,7 +275,7 @@ self._construct_table() def _construct_table(self): - table_description = _BasicStatisticsSeries_construct_table_toexec(self.index_names) + table_description = _BasicStatisticsSeries_construct_table_toexec(self.index_names, self.store_timestamp, self.store_cpuclock) self._table = self.hdf5_file.createTable(self.hdf5_group, self.table_name, table_description) @@ -252,6 +302,8 @@ newrow["max"] = self.stats_functions['max'](array) newrow["std"] = self.stats_functions['std'](array) + self._timestamp_cpuclock(newrow) + newrow.append() self.hdf5_file.flush() diff -r de3aef84714a -r cde71d24f235 utils/seriestables/test_series.py --- a/utils/seriestables/test_series.py Wed Mar 10 17:08:50 2010 -0500 +++ b/utils/seriestables/test_series.py Wed Mar 10 20:14:20 2010 -0500 @@ -7,6 +7,7 @@ from tables import * from series import * +import series def compare_floats(f1,f2): @@ -144,7 +145,7 @@ h5f_path = tempfile.NamedTemporaryFile().name h5f = openFile(h5f_path, "w") - desc = get_description_with_n_ints_n_floats(("col1","col2"), ("col3","col4")) + desc = series._get_description_with_n_ints_n_floats(("col1","col2"), ("col3","col4")) mytable = h5f.createTable('/', 'mytable', desc)