[Scipy-svn] r2611 - in trunk/Lib/sandbox/timeseries: . io
scipy-svn at scipy.org
scipy-svn at scipy.org
Thu Jan 25 13:13:41 EST 2007
Author: mattknox_ca
Date: 2007-01-25 12:13:36 -0600 (Thu, 25 Jan 2007)
New Revision: 2611
Added:
trunk/Lib/sandbox/timeseries/io/
trunk/Lib/sandbox/timeseries/io/__init__.py
trunk/Lib/sandbox/timeseries/io/report.py
Log:
Added io module
Added: trunk/Lib/sandbox/timeseries/io/__init__.py
===================================================================
Added: trunk/Lib/sandbox/timeseries/io/report.py
===================================================================
--- trunk/Lib/sandbox/timeseries/io/report.py 2007-01-25 11:59:47 UTC (rev 2610)
+++ trunk/Lib/sandbox/timeseries/io/report.py 2007-01-25 18:13:36 UTC (rev 2611)
@@ -0,0 +1,216 @@
+import cStringIO,operator, types
+import timeseries as ts
+
+def report(*tseries, **kwargs):
+ """generate a table report of *tseries with dates in the left column.
+
+:Parameters:
+ - `*tseries` : time series objects. Must all be at the same frequency, but
+ do not need to be aligned.
+ - `dates` (DateArray, *[None]*) : dates at which values of all the series
+ will be output. If not specified, data will be output from the minimum
+ start_date to the maximum end_date of all the time series objects
+ - `header_row` (list, *[None]*) : optional list of column headers. Length
+ must be equal to len(tseries) (no date column header specified) or
+ len(tseries)+1 (first header is assumed to be date column header)
+ - `header_char` : Character to be used for the row separator line
+ - `justify` (List or String, *[None]*) : Determines how are data justified in
+ their column. If not specified, the date column and string columns are left
+ justified, and everything else is right justified. If a string is specified,
+ it must be one of 'left', 'right', or 'center' and all columns will be
+ justified the same way. If a list is specified, each column will be justified
+ according to the specification for that column in the list (specifying the
+ justification for the date column is optional).
+ - `prefix` (string, *['']*) : A string prepended to each printed row.
+ - `postfix` (string, *['']*) : A string appended to each printed row.
+ - `mask_rep` (string, *['--']*): String used to represent masked values in
+ output
+ - `datefmt` (string, *[None]*) : Formatting string used for displaying the
+ dates in the date column. If None, str() is simply called on the dates
+ - `fmtfunc` (function, *[str]*): A function f(item) for formatting each data
+ type in the report into a string. If not specified, str() is simply called
+ on each item.
+ - `wrapfunc` (function, *[lambda x:x]*): A function f(text) for wrapping text;
+ each element in the table is first wrapped by this function. Useful functions
+ for this are wrap_onspace, wrap_onspace_strict, and wrap_always (which are
+ part of this module). Eg wrapfunc=lambda x: wrap_onspace(x, 10)
+
+:Examples:
+
+ import numpy as np
+ import timeseries as ts
+ from timeseries.io import report as r
+
+ series1 = ts.time_series(np.random.uniform(-100,100,15), start_date=ts.thisday('b')-15)
+ series2 = ts.time_series(np.random.uniform(-100,100,13), start_date=ts.thisday('b')-10)
+ series3 = ts.time_series(['string1', 'another string', 'yet another string']*3, start_date=ts.thisday('b')-10)
+
+ darray = ts.date_array(start_date=ts.thisday('b')-8, end_date=ts.thisday('b')-3)
+
+ # print all values of series1 and series2 and show 2 decimal places.
+ # show masked values as "N/A"
+ print r.report(series1, series2, fmtfunc=lambda x:'%.2f' % x, mask_rep='N/A')
+
+ # print an html table of the data over a specified range
+ print "<table>" + \
+ r.report(series1, series2, series3, dates=darray,
+ delim="</td><td>", prefix="<tr><td>", postfix="</td></tr>") + \
+ "</table>"
+
+ # print a table with columns 10 characters wide when possible, but don't break up a word
+ print r.report(series1, series3, series2, wrapfunc=lambda x: r.wrap_onspace(x, 10))"""
+
+ dates = kwargs.pop('dates', None)
+ header_row = kwargs.pop('header_row', None)
+ header_char = kwargs.pop('header_char', '-')
+ delim = kwargs.pop('delim', ' | ')
+ justify = kwargs.pop('justify', None)
+ prefix = kwargs.pop('prefix', '')
+ postfix = kwargs.pop('postfix', '')
+ mask_rep = kwargs.pop('mask_rep', '--')
+ datefmt = kwargs.pop('datefmt', None)
+ fmtfunc_temp = kwargs.pop('fmtfunc', str)
+
+ def fmtfunc(item):
+ if hasattr(item, "_mask") and item._mask:
+ return mask_rep
+ else:
+ return fmtfunc_temp(item)
+
+ wrapfunc = kwargs.pop('wrapfunc', lambda x:x)
+
+ if len(kwargs) > 0:
+ raise KeyError("Unrecognized keyword(s): %s" % (", ".join(kwargs.keys())))
+
+ if header_row is not None:
+ hasHeader=True
+ if len(header_row) == len(tseries)+1:
+ # label for date column included
+ rows = [header_row]
+ elif len(header_row) == len(tseries):
+ # label for date column not included
+ rows = [['']+header_row]
+ else:
+ hasHeader=False
+ rows=[]
+
+ if justify is not None:
+ if type(justify) == types.StringType:
+ # justify all columns the the same way
+ justify = [justify for x in range(len(tseries)+1)]
+ else: #assume it is a list or tuple, etc
+ if len(justify) == len(tseries):
+ # justification for date column not included, so set that
+ # to left by default
+ justify = ['left'] + justify
+ else:
+ # default column justification
+ justify = ['left']
+ for ser in tseries:
+ if str(ser.dtype)[:2] == '|S': justify.append('left')
+ else: justify.append('right')
+
+
+ if datefmt is None:
+ def datefmt_func(date): return str(date)
+ else:
+ def datefmt_func(date): return date.strfmt(datefmt)
+
+ tseries = ts.align_series(*tseries)
+
+ if dates is None:
+ dates = ts.date_array(start_date=tseries[0].start_date,
+ end_date=tseries[0].end_date)
+
+ for d in dates:
+ rows.append([datefmt_func(d)]+[fmtfunc(ser[d]) for ser in tseries])
+
+ return indent(rows, hasHeader=hasHeader, headerChar=header_char,
+ delim=delim, justify=justify, separateRows=False,
+ prefix=prefix, postfix=postfix, wrapfunc=wrapfunc)
+
+
+
+
+# written by George Sakkis
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/267662
+def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify=None,
+ separateRows=False, prefix='', postfix='', wrapfunc=lambda x:x):
+ """Indents a table by column.
+ - rows: A sequence of sequences of items, one sequence per row.
+ - hasHeader: True if the first row consists of the columns' names.
+ - headerChar: Character to be used for the row separator line
+ (if hasHeader==True or separateRows==True).
+ - delim: The column delimiter.
+ - justify: Determines how are data justified in their column.
+ Valid values are 'left','right' and 'center'.
+ - separateRows: True if rows are to be separated by a line
+ of 'headerChar's.
+ - prefix: A string prepended to each printed row.
+ - postfix: A string appended to each printed row.
+ - wrapfunc: A function f(text) for wrapping text; each element in
+ the table is first wrapped by this function."""
+
+
+ def rowWrapper(row):
+ newRows = [wrapfunc(item).split('\n') for item in row]
+ return [[substr or '' for substr in item] for item in map(None,*newRows)]
+ # break each logical row into one or more physical ones
+ logicalRows = [rowWrapper(row) for row in rows]
+ # columns of physical rows
+ columns = map(None,*reduce(operator.add,logicalRows))
+ numCols = len(columns)
+ colNums = list(range(numCols))
+
+ if justify is None:
+ justify = ['left' for x in range(numCols)]
+
+ # get the maximum of each column by the string length of its items
+ maxWidths = [max([len(str(item)) for item in column]) for column in columns]
+ rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
+ len(delim)*(len(maxWidths)-1))
+ # select the appropriate justify method
+ justify_funcs = {'center':str.center, 'right':str.rjust, 'left':str.ljust}
+
+
+ output=cStringIO.StringIO()
+ if separateRows: print >> output, rowSeparator
+ for physicalRows in logicalRows:
+ for row in physicalRows:
+ print >> output, \
+ prefix \
+ + delim.join([justify_funcs[justify[colNum].lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \
+ + postfix
+ if separateRows or hasHeader: print >> output, rowSeparator; hasHeader=False
+ return output.getvalue()
+
+# written by Mike Brown
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
+def wrap_onspace(text, width):
+ """
+ A word-wrap function that preserves existing line breaks
+ and most spaces in the text. Expects that existing line
+ breaks are posix newlines (\n).
+ """
+ return reduce(lambda line, word, width=width: '%s%s%s' %
+ (line,
+ ' \n'[(len(line[line.rfind('\n')+1:])
+ + len(word.split('\n',1)[0]
+ ) >= width)],
+ word),
+ text.split(' ')
+ )
+
+import re
+def wrap_onspace_strict(text, width):
+ """Similar to wrap_onspace, but enforces the width constraint:
+ words longer than width are split."""
+ wordRegex = re.compile(r'\S{'+str(width)+r',}')
+ return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width)
+
+import math
+def wrap_always(text, width):
+ """A simple word-wrap function that wraps text on exactly width characters.
+ It doesn't split the text in words."""
+ return '\n'.join([ text[width*i:width*(i+1)] \
+ for i in xrange(int(math.ceil(1.*len(text)/width))) ])
More information about the Scipy-svn
mailing list