reconnect moved files to git repo

This commit is contained in:
root
2025-08-01 04:33:03 -04:00
commit 5d3c35492d
23190 changed files with 4750716 additions and 0 deletions

View File

@ -0,0 +1,10 @@
from .foreign import savetxt
from .table import SimpleTable, csv2st
from .smpickle import save_pickle, load_pickle
from statsmodels.tools._test_runner import PytestTester
__all__ = ['test', 'csv2st', 'SimpleTable', 'savetxt',
'save_pickle', 'load_pickle']
test = PytestTester()

View File

@ -0,0 +1,7 @@
__all__ = [
"SimpleTable", "savetxt", "csv2st",
"save_pickle", "load_pickle"
]
from .foreign import savetxt
from .table import SimpleTable, csv2st
from .smpickle import save_pickle, load_pickle

View File

@ -0,0 +1,140 @@
"""
Input/Output tools for working with binary data.
See Also
--------
numpy.lib.io
"""
import numpy as np
from statsmodels.iolib.openfile import get_file_obj
def savetxt(fname, X, names=None, fmt='%.18e', delimiter=' '):
"""
Save an array to a text file.
This is just a copy of numpy.savetxt patched to support structured arrays
or a header of names. Does not include py3 support now in savetxt.
Parameters
----------
fname : filename or file handle
If the filename ends in ``.gz``, the file is automatically saved in
compressed gzip format. `loadtxt` understands gzipped files
transparently.
X : array_like
Data to be saved to a text file.
names : list, optional
If given names will be the column header in the text file.
fmt : str or sequence of strs
A single format (%10.5f), a sequence of formats, or a
multi-format string, e.g. 'Iteration %d -- %10.5f', in which
case `delimiter` is ignored.
delimiter : str
Character separating columns.
See Also
--------
save : Save an array to a binary file in NumPy ``.npy`` format
savez : Save several arrays into a ``.npz`` compressed archive
Notes
-----
Further explanation of the `fmt` parameter
(``%[flag]width[.precision]specifier``):
flags:
``-`` : left justify
``+`` : Forces to preceed result with + or -.
``0`` : Left pad the number with zeros instead of space (see width).
width:
Minimum number of characters to be printed. The value is not truncated
if it has more characters.
precision:
- For integer specifiers (eg. ``d,i,o,x``), the minimum number of
digits.
- For ``e, E`` and ``f`` specifiers, the number of digits to print
after the decimal point.
- For ``g`` and ``G``, the maximum number of significant digits.
- For ``s``, the maximum number of characters.
specifiers:
``c`` : character
``d`` or ``i`` : signed decimal integer
``e`` or ``E`` : scientific notation with ``e`` or ``E``.
``f`` : decimal floating point
``g,G`` : use the shorter of ``e,E`` or ``f``
``o`` : signed octal
``s`` : str of characters
``u`` : unsigned decimal integer
``x,X`` : unsigned hexadecimal integer
This explanation of ``fmt`` is not complete, for an exhaustive
specification see [1]_.
References
----------
.. [1] `Format Specification Mini-Language
<http://docs.python.org/library/string.html#
format-specification-mini-language>`_, Python Documentation.
Examples
--------
>>> savetxt('test.out', x, delimiter=',') # x is an array
>>> savetxt('test.out', (x,y,z)) # x,y,z equal sized 1D arrays
>>> savetxt('test.out', x, fmt='%1.4e') # use exponential notation
"""
with get_file_obj(fname, 'w') as fh:
X = np.asarray(X)
# Handle 1-dimensional arrays
if X.ndim == 1:
# Common case -- 1d array of numbers
if X.dtype.names is None:
X = np.atleast_2d(X).T
ncol = 1
# Complex dtype -- each field indicates a separate column
else:
ncol = len(X.dtype.descr)
else:
ncol = X.shape[1]
# `fmt` can be a string with multiple insertion points or a list of formats.
# E.g. '%10.5f\t%10d' or ('%10.5f', '$10d')
if isinstance(fmt, (list, tuple)):
if len(fmt) != ncol:
raise AttributeError('fmt has wrong shape. %s' % str(fmt))
format = delimiter.join(fmt)
elif isinstance(fmt, str):
if fmt.count('%') == 1:
fmt = [fmt, ]*ncol
format = delimiter.join(fmt)
elif fmt.count('%') != ncol:
raise AttributeError('fmt has wrong number of %% formats. %s'
% fmt)
else:
format = fmt
# handle names
if names is None and X.dtype.names:
names = X.dtype.names
if names is not None:
fh.write(delimiter.join(names) + '\n')
for row in X:
fh.write(format % tuple(row) + '\n')

View File

@ -0,0 +1,79 @@
"""
Handle file opening for read/write
"""
from pathlib import Path
from numpy.lib._iotools import _is_string_like
class EmptyContextManager:
"""
This class is needed to allow file-like object to be used as
context manager, but without getting closed.
"""
def __init__(self, obj):
self._obj = obj
def __enter__(self):
"""When entering, return the embedded object"""
return self._obj
def __exit__(self, *args):
"""Do not hide anything"""
return False
def __getattr__(self, name):
return getattr(self._obj, name)
def _open(fname, mode, encoding):
if fname.endswith(".gz"):
import gzip
return gzip.open(fname, mode, encoding=encoding)
else:
return open(fname, mode, encoding=encoding)
def get_file_obj(fname, mode="r", encoding=None):
"""
Light wrapper to handle strings, path objects and let files (anything else)
pass through.
It also handle '.gz' files.
Parameters
----------
fname : str, path object or file-like object
File to open / forward
mode : str
Argument passed to the 'open' or 'gzip.open' function
encoding : str
For Python 3 only, specify the encoding of the file
Returns
-------
A file-like object that is always a context-manager. If the `fname` was
already a file-like object, the returned context manager *will not
close the file*.
"""
if _is_string_like(fname):
fname = Path(fname)
if isinstance(fname, Path):
return fname.open(mode=mode, encoding=encoding)
elif hasattr(fname, "open"):
return fname.open(mode=mode, encoding=encoding)
try:
return open(fname, mode, encoding=encoding)
except TypeError:
try:
# Make sure the object has the write methods
if "r" in mode:
fname.read
if "w" in mode or "a" in mode:
fname.write
except AttributeError:
raise ValueError("fname must be a string or a file-like object")
return EmptyContextManager(fname)

View File

@ -0,0 +1,42 @@
"""Helper files for pickling"""
from statsmodels.iolib.openfile import get_file_obj
def save_pickle(obj, fname):
"""
Save the object to file via pickling.
Parameters
----------
fname : {str, pathlib.Path}
Filename to pickle to
"""
import pickle
with get_file_obj(fname, "wb") as fout:
pickle.dump(obj, fout, protocol=-1)
def load_pickle(fname):
"""
Load a previously saved object
.. warning::
Loading pickled models is not secure against erroneous or maliciously
constructed data. Never unpickle data received from an untrusted or
unauthenticated source.
Parameters
----------
fname : {str, pathlib.Path}
Filename to unpickle
Notes
-----
This method can be used to load *both* models and results.
"""
import pickle
with get_file_obj(fname, "rb") as fin:
return pickle.load(fin)

View File

@ -0,0 +1,83 @@
""". regress totemp gnpdefl gnp unemp armed pop year
Source | SS df MS Number of obs = 16
-------------+------------------------------ F( 6, 9) = 330.29
Model | 184172402 6 30695400.3 Prob > F = 0.0000
Residual | 836424.129 9 92936.0144 R-squared = 0.9955
-------------+------------------------------ Adj R-squared = 0.9925
Total | 185008826 15 12333921.7 Root MSE = 304.85
------------------------------------------------------------------------------
totemp | Coef. Std. Err. t P>|t| [95% Conf. Interval]
-------------+----------------------------------------------------------------
gnpdefl | 15.06167 84.91486 0.18 0.863 -177.0291 207.1524
gnp | -.0358191 .033491 -1.07 0.313 -.111581 .0399428
unemp | -2.020229 .4883995 -4.14 0.003 -3.125065 -.9153928
armed | -1.033227 .2142741 -4.82 0.001 -1.517948 -.5485049
pop | -.0511045 .2260731 -0.23 0.826 -.5625173 .4603083
year | 1829.151 455.4785 4.02 0.003 798.7873 2859.515
_cons | -3482258 890420.3 -3.91 0.004 -5496529 -1467987
------------------------------------------------------------------------------
"""
#From Stata using Longley dataset as in the test and example for GLM
"""
. glm totemp gnpdefl gnp unemp armed pop year
Iteration 0: log likelihood = -109.61744
Generalized linear models No. of obs = 16
Optimization : ML Residual df = 9
Scale parameter = 92936.01
Deviance = 836424.1293 (1/df) Deviance = 92936.01
Pearson = 836424.1293 (1/df) Pearson = 92936.01
Variance function: V(u) = 1 [Gaussian]
Link function : g(u) = u [Identity]
AIC = 14.57718
Log likelihood = -109.6174355 BIC = 836399.2
------------------------------------------------------------------------------
| OIM
totemp | Coef. Std. Err. z P>|z| [95% Conf. Interval]
-------------+----------------------------------------------------------------
gnpdefl | 15.06167 84.91486 0.18 0.859 -151.3684 181.4917
gnp | -.0358191 .033491 -1.07 0.285 -.1014603 .029822
unemp | -2.020229 .4883995 -4.14 0.000 -2.977475 -1.062984
armed | -1.033227 .2142741 -4.82 0.000 -1.453196 -.6132571
pop | -.0511045 .2260731 -0.23 0.821 -.4941996 .3919906
year | 1829.151 455.4785 4.02 0.000 936.4298 2721.873
_cons | -3482258 890420.3 -3.91 0.000 -5227450 -1737066
------------------------------------------------------------------------------
"""
#RLM Example
"""
. rreg stackloss airflow watertemp acidconc
Huber iteration 1: maximum difference in weights = .48402478
Huber iteration 2: maximum difference in weights = .07083248
Huber iteration 3: maximum difference in weights = .03630349
Biweight iteration 4: maximum difference in weights = .2114744
Biweight iteration 5: maximum difference in weights = .04709559
Biweight iteration 6: maximum difference in weights = .01648123
Biweight iteration 7: maximum difference in weights = .01050023
Biweight iteration 8: maximum difference in weights = .0027233
Robust regression Number of obs = 21
F( 3, 17) = 74.15
Prob > F = 0.0000
------------------------------------------------------------------------------
stackloss | Coef. Std. Err. t P>|t| [95% Conf. Interval]
-------------+----------------------------------------------------------------
airflow | .8526511 .1223835 6.97 0.000 .5944446 1.110858
watertemp | .8733594 .3339811 2.61 0.018 .1687209 1.577998
acidconc | -.1224349 .1418364 -0.86 0.400 -.4216836 .1768139
_cons | -41.6703 10.79559 -3.86 0.001 -64.447 -18.89361
------------------------------------------------------------------------------
"""

View File

@ -0,0 +1,911 @@
from statsmodels.compat.python import lmap, lrange, lzip
import copy
from itertools import zip_longest
import time
import numpy as np
from statsmodels.iolib.table import SimpleTable
from statsmodels.iolib.tableformatting import (
fmt_2,
fmt_2cols,
fmt_params,
gen_fmt,
)
from .summary2 import _model_types
def forg(x, prec=3):
x = np.squeeze(x)
if prec == 3:
# for 3 decimals
if (abs(x) >= 1e4) or (abs(x) < 1e-4):
return '%9.3g' % x
else:
return '%9.3f' % x
elif prec == 4:
if (abs(x) >= 1e4) or (abs(x) < 1e-4):
return '%10.4g' % x
else:
return '%10.4f' % x
else:
raise ValueError("`prec` argument must be either 3 or 4, not {prec}"
.format(prec=prec))
def d_or_f(x, width=6):
"""convert number to string with either integer of float formatting
This is used internally for nobs and degrees of freedom which are usually
integers but can be float in some cases.
Parameters
----------
x : int or float
width : int
only used if x is nan
Returns
-------
str : str
number as formatted string
"""
if np.isnan(x):
return (width - 3) * ' ' + 'NaN'
if x // 1 == x:
return "%#6d" % x
else:
return "%#8.2f" % x
def summary(self, yname=None, xname=None, title=0, alpha=.05,
returns='text', model_info=None):
"""
Parameters
----------
yname : str
optional, Default is `Y`
xname : list[str]
optional, Default is `X.#` for # in p the number of regressors
Confidance interval : (0,1) not implimented
title : str
optional, Defualt is 'Generalized linear model'
returns : str
'text', 'table', 'csv', 'latex', 'html'
Returns
-------
Default :
returns='print'
Prints the summarirized results
Option :
returns='text'
Prints the summarirized results
Option :
returns='table'
SimpleTable instance : summarizing the fit of a linear model.
Option :
returns='csv'
returns a string of csv of the results, to import into a spreadsheet
Option :
returns='latex'
Not implimented yet
Option :
returns='HTML'
Not implimented yet
Examples (needs updating)
--------
>>> import statsmodels as sm
>>> data = sm.datasets.longley.load()
>>> data.exog = sm.add_constant(data.exog)
>>> ols_results = sm.OLS(data.endog, data.exog).results
>>> print ols_results.summary()
...
Notes
-----
conf_int calculated from normal dist.
"""
if title == 0:
title = _model_types[self.model.__class__.__name__]
if xname is not None and len(xname) != len(self.params):
# GH 2298
raise ValueError('User supplied xnames must have the same number of '
'entries as the number of model parameters '
'({})'.format(len(self.params)))
yname, xname = _getnames(self, yname, xname)
time_now = time.localtime()
time_of_day = [time.strftime("%H:%M:%S", time_now)]
date = time.strftime("%a, %d %b %Y", time_now)
modeltype = self.model.__class__.__name__
nobs = self.nobs
df_model = self.df_model
df_resid = self.df_resid
#General part of the summary table, Applicable to all? models
#------------------------------------------------------------
# TODO: define this generically, overwrite in model classes
#replace definition of stubs data by single list
#e.g.
gen_left = [('Model type:', [modeltype]),
('Date:', [date]),
('Dependent Variable:', yname), # TODO: What happens with multiple names?
('df model', [df_model])
]
gen_stubs_left, gen_data_left = zip_longest(*gen_left) #transpose row col
gen_title = title
gen_header = None
gen_table_left = SimpleTable(gen_data_left,
gen_header,
gen_stubs_left,
title=gen_title,
txt_fmt=gen_fmt
)
gen_stubs_right = ('Method:',
'Time:',
'Number of Obs:',
'df resid')
gen_data_right = ([modeltype], #was dist family need to look at more
time_of_day,
[nobs],
[df_resid]
)
gen_table_right = SimpleTable(gen_data_right,
gen_header,
gen_stubs_right,
title=gen_title,
txt_fmt=gen_fmt
)
gen_table_left.extend_right(gen_table_right)
general_table = gen_table_left
# Parameters part of the summary table
# ------------------------------------
# Note: this is not necessary since we standardized names,
# only t versus normal
tstats = {'OLS': self.t(),
'GLS': self.t(),
'GLSAR': self.t(),
'WLS': self.t(),
'RLM': self.t(),
'GLM': self.t()}
prob_stats = {'OLS': self.pvalues,
'GLS': self.pvalues,
'GLSAR': self.pvalues,
'WLS': self.pvalues,
'RLM': self.pvalues,
'GLM': self.pvalues
}
# Dictionary to store the header names for the parameter part of the
# summary table. look up by modeltype
alp = str((1-alpha)*100)+'%'
param_header = {
'OLS' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'],
'GLS' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'],
'GLSAR' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'],
'WLS' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'],
'GLM' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'], #glm uses t-distribution
'RLM' : ['coef', 'std err', 'z', 'P>|z|', alp + ' Conf. Interval'] #checke z
}
params_stubs = xname
params = self.params
conf_int = self.conf_int(alpha)
std_err = self.bse
exog_len = lrange(len(xname))
tstat = tstats[modeltype]
prob_stat = prob_stats[modeltype]
# Simpletable should be able to handle the formating
params_data = lzip(["%#6.4g" % (params[i]) for i in exog_len],
["%#6.4f" % (std_err[i]) for i in exog_len],
["%#6.4f" % (tstat[i]) for i in exog_len],
["%#6.4f" % (prob_stat[i]) for i in exog_len],
["(%#5g, %#5g)" % tuple(conf_int[i]) for i in exog_len])
parameter_table = SimpleTable(params_data,
param_header[modeltype],
params_stubs,
title=None,
txt_fmt=fmt_2
)
#special table
#-------------
#TODO: exists in linear_model, what about other models
#residual diagnostics
#output options
#--------------
#TODO: JP the rest needs to be fixed, similar to summary in linear_model
def ols_printer():
"""
print summary table for ols models
"""
table = str(general_table)+'\n'+str(parameter_table)
return table
def glm_printer():
table = str(general_table)+'\n'+str(parameter_table)
return table
printers = {'OLS': ols_printer, 'GLM': glm_printer}
if returns == 'print':
try:
return printers[modeltype]()
except KeyError:
return printers['OLS']()
def _getnames(self, yname=None, xname=None):
'''extract names from model or construct names
'''
if yname is None:
if getattr(self.model, 'endog_names', None) is not None:
yname = self.model.endog_names
else:
yname = 'y'
if xname is None:
if getattr(self.model, 'exog_names', None) is not None:
xname = self.model.exog_names
else:
xname = ['var_%d' % i for i in range(len(self.params))]
return yname, xname
def summary_top(results, title=None, gleft=None, gright=None, yname=None, xname=None):
'''generate top table(s)
TODO: this still uses predefined model_methods
? allow gleft, gright to be 1 element tuples instead of filling with None?
'''
#change of names ?
gen_left, gen_right = gleft, gright
# time and names are always included
time_now = time.localtime()
time_of_day = [time.strftime("%H:%M:%S", time_now)]
date = time.strftime("%a, %d %b %Y", time_now)
yname, xname = _getnames(results, yname=yname, xname=xname)
# create dictionary with default
# use lambdas because some values raise exception if they are not available
default_items = dict([
('Dependent Variable:', lambda: [yname]),
('Dep. Variable:', lambda: [yname]),
('Model:', lambda: [results.model.__class__.__name__]),
('Date:', lambda: [date]),
('Time:', lambda: time_of_day),
('Number of Obs:', lambda: [results.nobs]),
('No. Observations:', lambda: [d_or_f(results.nobs)]),
('Df Model:', lambda: [d_or_f(results.df_model)]),
('Df Residuals:', lambda: [d_or_f(results.df_resid)]),
('Log-Likelihood:', lambda: ["%#8.5g" % results.llf]) # does not exist for RLM - exception
])
if title is None:
title = results.model.__class__.__name__ + 'Regression Results'
if gen_left is None:
# default: General part of the summary table, Applicable to all? models
gen_left = [('Dep. Variable:', None),
('Model type:', None),
('Date:', None),
('No. Observations:', None),
('Df model:', None),
('Df resid:', None)]
try:
llf = results.llf # noqa: F841
gen_left.append(('Log-Likelihood', None))
except: # AttributeError, NotImplementedError
pass
gen_right = []
gen_title = title
gen_header = None
# replace missing (None) values with default values
gen_left_ = []
for item, value in gen_left:
if value is None:
value = default_items[item]() # let KeyErrors raise exception
gen_left_.append((item, value))
gen_left = gen_left_
if gen_right:
gen_right_ = []
for item, value in gen_right:
if value is None:
value = default_items[item]() # let KeyErrors raise exception
gen_right_.append((item, value))
gen_right = gen_right_
# check nothing was missed
missing_values = [k for k,v in gen_left + gen_right if v is None]
assert missing_values == [], missing_values
# pad both tables to equal number of rows
if gen_right:
if len(gen_right) < len(gen_left):
# fill up with blank lines to same length
gen_right += [(' ', ' ')] * (len(gen_left) - len(gen_right))
elif len(gen_right) > len(gen_left):
# fill up with blank lines to same length, just to keep it symmetric
gen_left += [(' ', ' ')] * (len(gen_right) - len(gen_left))
# padding in SimpleTable does not work like I want
#force extra spacing and exact string length in right table
gen_right = [('%-21s' % (' '+k), v) for k,v in gen_right]
gen_stubs_right, gen_data_right = zip_longest(*gen_right) #transpose row col
gen_table_right = SimpleTable(gen_data_right,
gen_header,
gen_stubs_right,
title=gen_title,
txt_fmt=fmt_2cols
)
else:
gen_table_right = [] #because .extend_right seems works with []
#moved below so that we can pad if needed to match length of gen_right
#transpose rows and columns, `unzip`
gen_stubs_left, gen_data_left = zip_longest(*gen_left) #transpose row col
gen_table_left = SimpleTable(gen_data_left,
gen_header,
gen_stubs_left,
title=gen_title,
txt_fmt=fmt_2cols
)
gen_table_left.extend_right(gen_table_right)
general_table = gen_table_left
return general_table
def summary_params(results, yname=None, xname=None, alpha=.05, use_t=True,
skip_header=False, title=None):
'''create a summary table for the parameters
Parameters
----------
res : results instance
some required information is directly taken from the result
instance
yname : {str, None}
optional name for the endogenous variable, default is "y"
xname : {list[str], None}
optional names for the exogenous variables, default is "var_xx"
alpha : float
significance level for the confidence intervals
use_t : bool
indicator whether the p-values are based on the Student-t
distribution (if True) or on the normal distribution (if False)
skip_headers : bool
If false (default), then the header row is added. If true, then no
header row is added.
Returns
-------
params_table : SimpleTable instance
'''
# Parameters part of the summary table
# ------------------------------------
# Note: this is not necessary since we standardized names,
# only t versus normal
if isinstance(results, tuple):
# for multivariate endog
# TODO: check whether I do not want to refactor this
#we need to give parameter alpha to conf_int
results, params, std_err, tvalues, pvalues, conf_int = results
else:
params = np.asarray(results.params)
std_err = np.asarray(results.bse)
tvalues = np.asarray(results.tvalues) # is this sometimes called zvalues
pvalues = np.asarray(results.pvalues)
conf_int = np.asarray(results.conf_int(alpha))
if params.size == 0:
return SimpleTable([['No Model Parameters']])
# Dictionary to store the header names for the parameter part of the
# summary table. look up by modeltype
if use_t:
param_header = ['coef', 'std err', 't', 'P>|t|',
'[' + str(alpha/2), str(1-alpha/2) + ']']
else:
param_header = ['coef', 'std err', 'z', 'P>|z|',
'[' + str(alpha/2), str(1-alpha/2) + ']']
if skip_header:
param_header = None
_, xname = _getnames(results, yname=yname, xname=xname)
if len(xname) != len(params):
raise ValueError('xnames and params do not have the same length')
params_stubs = xname
exog_idx = lrange(len(xname))
params = np.asarray(params)
std_err = np.asarray(std_err)
tvalues = np.asarray(tvalues)
pvalues = np.asarray(pvalues)
conf_int = np.asarray(conf_int)
params_data = lzip([forg(params[i], prec=4) for i in exog_idx],
[forg(std_err[i]) for i in exog_idx],
[forg(tvalues[i]) for i in exog_idx],
["%#6.3f" % (pvalues[i]) for i in exog_idx],
[forg(conf_int[i,0]) for i in exog_idx],
[forg(conf_int[i,1]) for i in exog_idx])
parameter_table = SimpleTable(params_data,
param_header,
params_stubs,
title=title,
txt_fmt=fmt_params
)
return parameter_table
def summary_params_frame(results, yname=None, xname=None, alpha=.05,
use_t=True):
"""
Create a summary table for the parameters
Parameters
----------
res : results instance
some required information is directly taken from the result
instance
yname : {str, None}
optional name for the endogenous variable, default is "y"
xname : {list[str], None}
optional names for the exogenous variables, default is "var_xx"
alpha : float
significance level for the confidence intervals
use_t : bool
indicator whether the p-values are based on the Student-t
distribution (if True) or on the normal distribution (if False)
skip_headers : bool
If false (default), then the header row is added. If true, then no
header row is added.
Returns
-------
params_table : SimpleTable instance
"""
# Parameters part of the summary table
# ------------------------------------
# Note: this is not necessary since we standardized names,
# only t versus normal
if isinstance(results, tuple):
# for multivariate endog
# TODO: check whether I do not want to refactor this
#we need to give parameter alpha to conf_int
results, params, std_err, tvalues, pvalues, conf_int = results
else:
params = results.params
std_err = results.bse
tvalues = results.tvalues #is this sometimes called zvalues
pvalues = results.pvalues
conf_int = results.conf_int(alpha)
# Dictionary to store the header names for the parameter part of the
# summary table. look up by modeltype
if use_t:
param_header = ['coef', 'std err', 't', 'P>|t|',
'Conf. Int. Low', 'Conf. Int. Upp.']
else:
param_header = ['coef', 'std err', 'z', 'P>|z|',
'Conf. Int. Low', 'Conf. Int. Upp.']
_, xname = _getnames(results, yname=yname, xname=xname)
from pandas import DataFrame
table = np.column_stack((params, std_err, tvalues, pvalues, conf_int))
return DataFrame(table, columns=param_header, index=xname)
def summary_params_2d(result, extras=None, endog_names=None, exog_names=None,
title=None):
"""create summary table of regression parameters with several equations
This allows interleaving of parameters with bse and/or tvalues
Parameters
----------
result : result instance
the result instance with params and attributes in extras
extras : list[str]
additional attributes to add below a parameter row, e.g. bse or tvalues
endog_names : {list[str], None}
names for rows of the parameter array (multivariate endog)
exog_names : {list[str], None}
names for columns of the parameter array (exog)
alpha : float
level for confidence intervals, default 0.95
title : None or string
Returns
-------
tables : list of SimpleTable
this contains a list of all seperate Subtables
table_all : SimpleTable
the merged table with results concatenated for each row of the parameter
array
"""
if endog_names is None:
# TODO: note the [1:] is specific to current MNLogit
endog_names = ['endog_%d' % i for i in
np.unique(result.model.endog)[1:]]
if exog_names is None:
exog_names = ['var%d' % i for i in range(len(result.params))]
# TODO: check formatting options with different values
res_params = [[forg(item, prec=4) for item in row] for row in result.params]
if extras:
extras_list = [[['%10s' % ('(' + forg(v, prec=3).strip() + ')')
for v in col]
for col in getattr(result, what)]
for what in extras
]
data = lzip(res_params, *extras_list)
data = [i for j in data for i in j] #flatten
stubs = lzip(endog_names, *[['']*len(endog_names)]*len(extras))
stubs = [i for j in stubs for i in j] #flatten
else:
data = res_params
stubs = endog_names
txt_fmt = copy.deepcopy(fmt_params)
txt_fmt["data_fmts"] = ["%s"]*result.params.shape[1]
return SimpleTable(data, headers=exog_names,
stubs=stubs,
title=title,
txt_fmt=txt_fmt)
def summary_params_2dflat(result, endog_names=None, exog_names=None, alpha=0.05,
use_t=True, keep_headers=True, endog_cols=False):
"""summary table for parameters that are 2d, e.g. multi-equation models
Parameters
----------
result : result instance
the result instance with params, bse, tvalues and conf_int
endog_names : {list[str], None}
names for rows of the parameter array (multivariate endog)
exog_names : {list[str], None}
names for columns of the parameter array (exog)
alpha : float
level for confidence intervals, default 0.95
use_t : bool
indicator whether the p-values are based on the Student-t
distribution (if True) or on the normal distribution (if False)
keep_headers : bool
If true (default), then sub-tables keep their headers. If false, then
only the first headers are kept, the other headerse are blanked out
endog_cols : bool
If false (default) then params and other result statistics have
equations by rows. If true, then equations are assumed to be in columns.
Not implemented yet.
Returns
-------
tables : list of SimpleTable
this contains a list of all seperate Subtables
table_all : SimpleTable
the merged table with results concatenated for each row of the parameter
array
"""
res = result
params = res.params
if params.ndim == 2: # we've got multiple equations
n_equ = params.shape[1]
if len(endog_names) != params.shape[1]:
raise ValueError('endog_names has wrong length')
else:
if len(endog_names) != len(params):
raise ValueError('endog_names has wrong length')
n_equ = 1
#VAR does not have conf_int
#params = res.params.T # this is a convention for multi-eq models
# check that we have the right length of names
if not isinstance(endog_names, list):
# TODO: this might be specific to multinomial logit type, move?
if endog_names is None:
endog_basename = 'endog'
else:
endog_basename = endog_names
# TODO: note, the [1:] is specific to current MNLogit
endog_names = res.model.endog_names[1:]
tables = []
for eq in range(n_equ):
restup = (res, res.params[:,eq], res.bse[:,eq], res.tvalues[:,eq],
res.pvalues[:,eq], res.conf_int(alpha)[eq])
skiph = False
tble = summary_params(restup, yname=endog_names[eq],
xname=exog_names, alpha=alpha, use_t=use_t,
skip_header=skiph)
tables.append(tble)
# add titles, they will be moved to header lines in table_extend
for i in range(len(endog_names)):
tables[i].title = endog_names[i]
table_all = table_extend(tables, keep_headers=keep_headers)
return tables, table_all
def table_extend(tables, keep_headers=True):
"""extend a list of SimpleTables, adding titles to header of subtables
This function returns the merged table as a deepcopy, in contrast to the
SimpleTable extend method.
Parameters
----------
tables : list of SimpleTable instances
keep_headers : bool
If true, then all headers are kept. If falls, then the headers of
subtables are blanked out.
Returns
-------
table_all : SimpleTable
merged tables as a single SimpleTable instance
"""
from copy import deepcopy
for ii, t in enumerate(tables[:]): #[1:]:
t = deepcopy(t)
#move title to first cell of header
# TODO: check if we have multiline headers
if t[0].datatype == 'header':
t[0][0].data = t.title
t[0][0]._datatype = None
t[0][0].row = t[0][1].row
if not keep_headers and (ii > 0):
for c in t[0][1:]:
c.data = ''
# add separating line and extend tables
if ii == 0:
table_all = t
else:
r1 = table_all[-1]
r1.add_format('txt', row_dec_below='-')
table_all.extend(t)
table_all.title = None
return table_all
def summary_return(tables, return_fmt='text'):
# join table parts then print
if return_fmt == 'text':
strdrop = lambda x: str(x).rsplit('\n',1)[0]
# convert to string drop last line
return '\n'.join(lmap(strdrop, tables[:-1]) + [str(tables[-1])])
elif return_fmt == 'tables':
return tables
elif return_fmt == 'csv':
return '\n'.join(x.as_csv() for x in tables)
elif return_fmt == 'latex':
# TODO: insert \hline after updating SimpleTable
table = copy.deepcopy(tables[0])
for part in tables[1:]:
table.extend(part)
return table.as_latex_tabular()
elif return_fmt == 'html':
return "\n".join(table.as_html() for table in tables)
else:
raise ValueError('available output formats are text, csv, latex, html')
class Summary:
"""
Result summary
Construction does not take any parameters. Tables and text can be added
with the `add_` methods.
Attributes
----------
tables : list of tables
Contains the list of SimpleTable instances, horizontally concatenated
tables are not saved separately.
extra_txt : str
extra lines that are added to the text output, used for warnings
and explanations.
"""
def __init__(self):
self.tables = []
self.extra_txt = None
def __str__(self):
return self.as_text()
def __repr__(self):
return str(type(self)) + '\n"""\n' + self.__str__() + '\n"""'
def _repr_html_(self):
"""Display as HTML in IPython notebook."""
return self.as_html()
def _repr_latex_(self):
"""Display as LaTeX when converting IPython notebook to PDF."""
return self.as_latex()
def add_table_2cols(self, res, title=None, gleft=None, gright=None,
yname=None, xname=None):
"""
Add a double table, 2 tables with one column merged horizontally
Parameters
----------
res : results instance
some required information is directly taken from the result
instance
title : str, optional
if None, then a default title is used.
gleft : list[tuple], optional
elements for the left table, tuples are (name, value) pairs
If gleft is None, then a default table is created
gright : list[tuple], optional
elements for the right table, tuples are (name, value) pairs
yname : str, optional
optional name for the endogenous variable, default is "y"
xname : list[str], optional
optional names for the exogenous variables, default is "var_xx".
Must match the number of parameters in the model.
"""
table = summary_top(res, title=title, gleft=gleft, gright=gright,
yname=yname, xname=xname)
self.tables.append(table)
def add_table_params(self, res, yname=None, xname=None, alpha=.05,
use_t=True):
"""create and add a table for the parameter estimates
Parameters
----------
res : results instance
some required information is directly taken from the result
instance
yname : {str, None}
optional name for the endogenous variable, default is "y"
xname : {list[str], None}
optional names for the exogenous variables, default is "var_xx"
alpha : float
significance level for the confidence intervals
use_t : bool
indicator whether the p-values are based on the Student-t
distribution (if True) or on the normal distribution (if False)
Returns
-------
None : table is attached
"""
if res.params.ndim == 1:
table = summary_params(res, yname=yname, xname=xname, alpha=alpha,
use_t=use_t)
elif res.params.ndim == 2:
_, table = summary_params_2dflat(res, endog_names=yname,
exog_names=xname,
alpha=alpha, use_t=use_t)
else:
raise ValueError('params has to be 1d or 2d')
self.tables.append(table)
def add_extra_txt(self, etext):
"""add additional text that will be added at the end in text format
Parameters
----------
etext : list[str]
string with lines that are added to the text output.
"""
self.extra_txt = '\n'.join(etext)
def as_text(self):
"""return tables as string
Returns
-------
txt : str
summary tables and extra text as one string
"""
txt = summary_return(self.tables, return_fmt='text')
if self.extra_txt is not None:
txt = txt + '\n\n' + self.extra_txt
return txt
def as_latex(self):
"""return tables as string
Returns
-------
latex : str
summary tables and extra text as string of Latex
Notes
-----
This currently merges tables with different number of columns.
It is recommended to use `as_latex_tabular` directly on the individual
tables.
"""
latex = summary_return(self.tables, return_fmt='latex')
if self.extra_txt is not None:
latex = latex + '\n\n' + self.extra_txt.replace('\n', ' \\newline\n ')
return latex
def as_csv(self):
"""return tables as string
Returns
-------
csv : str
concatenated summary tables in comma delimited format
"""
csv = summary_return(self.tables, return_fmt='csv')
if self.extra_txt is not None:
csv = csv + '\n\n' + self.extra_txt
return csv
def as_html(self):
"""return tables as string
Returns
-------
html : str
concatenated summary tables in HTML format
"""
html = summary_return(self.tables, return_fmt='html')
if self.extra_txt is not None:
html = html + '<br/><br/>' + self.extra_txt.replace('\n', '<br/>')
return html

View File

@ -0,0 +1,648 @@
from statsmodels.compat.pandas import FUTURE_STACK
from statsmodels.compat.python import lzip
import datetime
from functools import reduce
import re
import textwrap
import numpy as np
import pandas as pd
from .table import SimpleTable
from .tableformatting import fmt_latex, fmt_txt
class Summary:
def __init__(self):
self.tables = []
self.settings = []
self.extra_txt = []
self.title = None
self._merge_latex = False
def __str__(self):
return self.as_text()
def __repr__(self):
return str(type(self)) + '\n"""\n' + self.__str__() + '\n"""'
def _repr_html_(self):
"""Display as HTML in IPython notebook."""
return self.as_html()
def _repr_latex_(self):
'''Display as LaTeX when converting IPython notebook to PDF.'''
return self.as_latex()
def add_df(self, df, index=True, header=True, float_format='%.4f',
align='r'):
"""
Add the contents of a DataFrame to summary table
Parameters
----------
df : DataFrame
header : bool
Reproduce the DataFrame column labels in summary table
index : bool
Reproduce the DataFrame row labels in summary table
float_format : str
Formatting to float data columns
align : str
Data alignment (l/c/r)
"""
settings = {'index': index, 'header': header,
'float_format': float_format, 'align': align}
self.tables.append(df)
self.settings.append(settings)
def add_array(self, array, align='r', float_format="%.4f"):
"""Add the contents of a Numpy array to summary table
Parameters
----------
array : numpy array (2D)
float_format : str
Formatting to array if type is float
align : str
Data alignment (l/c/r)
"""
table = pd.DataFrame(array)
self.add_df(table, index=False, header=False,
float_format=float_format, align=align)
def add_dict(self, d, ncols=2, align='l', float_format="%.4f"):
"""Add the contents of a Dict to summary table
Parameters
----------
d : dict
Keys and values are automatically coerced to strings with str().
Users are encouraged to format them before using add_dict.
ncols : int
Number of columns of the output table
align : str
Data alignment (l/c/r)
float_format : str
Formatting to float data columns
"""
keys = [_formatter(x, float_format) for x in d.keys()]
vals = [_formatter(x, float_format) for x in d.values()]
data = np.array(lzip(keys, vals))
if data.shape[0] % ncols != 0:
pad = ncols - (data.shape[0] % ncols)
data = np.vstack([data, np.array(pad * [['', '']])])
data = np.split(data, ncols)
data = reduce(lambda x, y: np.hstack([x, y]), data)
self.add_array(data, align=align)
def add_text(self, string):
"""Append a note to the bottom of the summary table. In ASCII tables,
the note will be wrapped to table width. Notes are not indented.
"""
self.extra_txt.append(string)
def add_title(self, title=None, results=None):
"""Insert a title on top of the summary table. If a string is provided
in the title argument, that string is printed. If no title string is
provided but a results instance is provided, statsmodels attempts
to construct a useful title automatically.
"""
if isinstance(title, str):
self.title = title
else:
if results is not None:
model = results.model.__class__.__name__
if model in _model_types:
model = _model_types[model]
self.title = 'Results: ' + model
else:
self.title = ''
def add_base(self, results, alpha=0.05, float_format="%.4f", title=None,
xname=None, yname=None):
"""Try to construct a basic summary instance.
Parameters
----------
results : Model results instance
alpha : float
significance level for the confidence intervals (optional)
float_format: str
Float formatting for summary of parameters (optional)
title : str
Title of the summary table (optional)
xname : list[str] of length equal to the number of parameters
Names of the independent variables (optional)
yname : str
Name of the dependent variable (optional)
"""
param = summary_params(results, alpha=alpha, use_t=results.use_t)
info = summary_model(results)
if xname is not None:
param.index = xname
if yname is not None:
info['Dependent Variable:'] = yname
self.add_dict(info, align='l')
self.add_df(param, float_format=float_format)
self.add_title(title=title, results=results)
def as_text(self):
"""Generate ASCII Summary Table
"""
tables = self.tables
settings = self.settings
title = self.title
extra_txt = self.extra_txt
pad_col, pad_index, widest = _measure_tables(tables, settings)
rule_equal = widest * '='
simple_tables = _simple_tables(tables, settings, pad_col, pad_index)
tab = [x.as_text() for x in simple_tables]
tab = '\n'.join(tab)
tab = tab.split('\n')
tab[0] = rule_equal
tab.append(rule_equal)
tab = '\n'.join(tab)
if title is not None:
title = title
if len(title) < widest:
title = ' ' * int(widest / 2 - len(title) / 2) + title
else:
title = ''
txt = [textwrap.wrap(x, widest) for x in extra_txt]
txt = ['\n'.join(x) for x in txt]
txt = '\n'.join(txt)
out = '\n'.join([title, tab, txt])
return out
def as_html(self):
"""Generate HTML Summary Table
"""
tables = self.tables
settings = self.settings
simple_tables = _simple_tables(tables, settings)
tab = [x.as_html() for x in simple_tables]
tab = '\n'.join(tab)
temp_txt = [st.replace('\n', '<br/>\n')for st in self.extra_txt]
txt = '<br/>\n'.join(temp_txt)
out = '<br/>\n'.join([tab, txt])
return out
def as_latex(self, label=''):
"""Generate LaTeX Summary Table
Parameters
----------
label : str
Label of the summary table that can be referenced
in a latex document (optional)
"""
tables = self.tables
settings = self.settings
title = self.title
if title is not None:
title = '\\caption{' + title + '}'
else:
title = '\\caption{}'
label = '\\label{' + label + '}'
simple_tables = _simple_tables(tables, settings)
tab = [x.as_latex_tabular() for x in simple_tables]
tab = '\n\n'.join(tab)
to_replace = ('\\\\hline\\n\\\\hline\\n\\\\'
'end{tabular}\\n\\\\begin{tabular}{.*}\\n')
if self._merge_latex:
# create single tabular object for summary_col
tab = re.sub(to_replace, r'\\midrule\n', tab)
non_captioned = '\\begin{table}', title, label, tab, '\\end{table}'
non_captioned = '\n'.join(non_captioned)
txt = ' \\newline \n'.join(self.extra_txt)
out = non_captioned + '\n\\bigskip\n' + txt
return out
def _measure_tables(tables, settings):
"""Compare width of ascii tables in a list and calculate padding values.
We add space to each col_sep to get us as close as possible to the
width of the largest table. Then, we add a few spaces to the first
column to pad the rest.
"""
simple_tables = _simple_tables(tables, settings)
tab = [x.as_text() for x in simple_tables]
length = [len(x.splitlines()[0]) for x in tab]
len_max = max(length)
pad_sep = []
pad_index = []
for i in range(len(tab)):
nsep = max(tables[i].shape[1] - 1, 1)
pad = int((len_max - length[i]) / nsep)
pad_sep.append(pad)
len_new = length[i] + nsep * pad
pad_index.append(len_max - len_new)
return pad_sep, pad_index, max(length)
# Useful stuff # TODO: be more specific
_model_types = {'OLS': 'Ordinary least squares',
'GLS': 'Generalized least squares',
'GLSAR': 'Generalized least squares with AR(p)',
'WLS': 'Weighted least squares',
'RLM': 'Robust linear model',
'NBin': 'Negative binomial model',
'GLM': 'Generalized linear model'
}
def summary_model(results):
"""
Create a dict with information about the model
"""
def time_now(*args, **kwds):
now = datetime.datetime.now()
return now.strftime('%Y-%m-%d %H:%M')
info = {}
info['Model:'] = lambda x: x.model.__class__.__name__
info['Model Family:'] = lambda x: x.family.__class.__name__
info['Link Function:'] = lambda x: x.family.link.__class__.__name__
info['Dependent Variable:'] = lambda x: x.model.endog_names
info['Date:'] = time_now
info['No. Observations:'] = lambda x: "%#6d" % x.nobs
info['Df Model:'] = lambda x: "%#6d" % x.df_model
info['Df Residuals:'] = lambda x: "%#6d" % x.df_resid
info['Converged:'] = lambda x: x.mle_retvals['converged']
info['No. Iterations:'] = lambda x: x.mle_retvals['iterations']
info['Method:'] = lambda x: x.method
info['Norm:'] = lambda x: x.fit_options['norm']
info['Scale Est.:'] = lambda x: x.fit_options['scale_est']
info['Cov. Type:'] = lambda x: x.fit_options['cov']
rsquared_type = '' if results.k_constant else ' (uncentered)'
info['R-squared' + rsquared_type + ':'] = lambda x: "%#8.3f" % x.rsquared
info['Adj. R-squared' + rsquared_type + ':'] = lambda x: "%#8.3f" % x.rsquared_adj # noqa:E501
info['Pseudo R-squared:'] = lambda x: "%#8.3f" % x.prsquared
info['AIC:'] = lambda x: "%8.4f" % x.aic
info['BIC:'] = lambda x: "%8.4f" % x.bic
info['Log-Likelihood:'] = lambda x: "%#8.5g" % x.llf
info['LL-Null:'] = lambda x: "%#8.5g" % x.llnull
info['LLR p-value:'] = lambda x: "%#8.5g" % x.llr_pvalue
info['Deviance:'] = lambda x: "%#8.5g" % x.deviance
info['Pearson chi2:'] = lambda x: "%#6.3g" % x.pearson_chi2
info['F-statistic:'] = lambda x: "%#8.4g" % x.fvalue
info['Prob (F-statistic):'] = lambda x: "%#6.3g" % x.f_pvalue
info['Scale:'] = lambda x: "%#8.5g" % x.scale
out = {}
for key, func in info.items():
try:
out[key] = func(results)
except (AttributeError, KeyError, NotImplementedError):
# NOTE: some models do not have loglike defined (RLM),
# so raise NotImplementedError
pass
return out
def summary_params(results, yname=None, xname=None, alpha=.05, use_t=True,
skip_header=False, float_format="%.4f"):
"""create a summary table of parameters from results instance
Parameters
----------
res : results instance
some required information is directly taken from the result
instance
yname : {str, None}
optional name for the endogenous variable, default is "y"
xname : {list[str], None}
optional names for the exogenous variables, default is "var_xx"
alpha : float
significance level for the confidence intervals
use_t : bool
indicator whether the p-values are based on the Student-t
distribution (if True) or on the normal distribution (if False)
skip_header : bool
If false (default), then the header row is added. If true, then no
header row is added.
float_format : str
float formatting options (e.g. ".3g")
Returns
-------
params_table : SimpleTable instance
"""
if isinstance(results, tuple):
results, params, bse, tvalues, pvalues, conf_int = results
else:
params = results.params
bse = results.bse
tvalues = results.tvalues
pvalues = results.pvalues
conf_int = results.conf_int(alpha)
data = np.array([params, bse, tvalues, pvalues]).T
data = np.hstack([data, conf_int])
data = pd.DataFrame(data)
if use_t:
data.columns = ['Coef.', 'Std.Err.', 't', 'P>|t|',
'[' + str(alpha / 2), str(1 - alpha / 2) + ']']
else:
data.columns = ['Coef.', 'Std.Err.', 'z', 'P>|z|',
'[' + str(alpha / 2), str(1 - alpha / 2) + ']']
if not xname:
try:
data.index = results.model.data.param_names
except AttributeError:
data.index = results.model.exog_names
else:
data.index = xname
return data
# Vertical summary instance for multiple models
def _col_params(result, float_format='%.4f', stars=True, include_r2=False):
"""Stack coefficients and standard errors in single column
"""
# Extract parameters
res = summary_params(result)
# Format float
for col in res.columns[:2]:
res[col] = res[col].apply(lambda x: float_format % x)
# Std.Errors in parentheses
res.iloc[:, 1] = '(' + res.iloc[:, 1] + ')'
# Significance stars
if stars:
idx = res.iloc[:, 3] < .1
res.loc[idx, res.columns[0]] = res.loc[idx, res.columns[0]] + '*'
idx = res.iloc[:, 3] < .05
res.loc[idx, res.columns[0]] = res.loc[idx, res.columns[0]] + '*'
idx = res.iloc[:, 3] < .01
res.loc[idx, res.columns[0]] = res.loc[idx, res.columns[0]] + '*'
# Stack Coefs and Std.Errors
res = res.iloc[:, :2]
res = res.stack(**FUTURE_STACK)
# Add R-squared
if include_r2:
rsquared = getattr(result, 'rsquared', np.nan)
rsquared_adj = getattr(result, 'rsquared_adj', np.nan)
r2 = pd.Series({('R-squared', ""): rsquared,
('R-squared Adj.', ""): rsquared_adj})
if r2.notnull().any():
r2 = r2.apply(lambda x: float_format % x)
res = pd.concat([res, r2], axis=0)
res = pd.DataFrame(res)
res.columns = [str(result.model.endog_names)]
return res
def _col_info(result, info_dict=None):
"""Stack model info in a column
"""
if info_dict is None:
info_dict = {}
out = []
index = []
for i in info_dict:
if isinstance(info_dict[i], dict):
# this is a specific model info_dict, but not for this result...
continue
try:
out.append(info_dict[i](result))
except AttributeError:
out.append('')
index.append(i)
out = pd.DataFrame({str(result.model.endog_names): out}, index=index)
return out
def _make_unique(list_of_names):
if len(set(list_of_names)) == len(list_of_names):
return list_of_names
# pandas does not like it if multiple columns have the same names
from collections import defaultdict
name_counter = defaultdict(str)
header = []
for _name in list_of_names:
name_counter[_name] += "I"
header.append(_name + " " + name_counter[_name])
return header
def summary_col(results, float_format='%.4f', model_names=(), stars=False,
info_dict=None, regressor_order=(), drop_omitted=False,
include_r2=True):
"""
Summarize multiple results instances side-by-side (coefs and SEs)
Parameters
----------
results : statsmodels results instance or list of result instances
float_format : str, optional
float format for coefficients and standard errors
Default : '%.4f'
model_names : list[str], optional
Must have same length as the number of results. If the names are not
unique, a roman number will be appended to all model names
stars : bool
print significance stars
info_dict : dict, default None
dict of functions to be applied to results instances to retrieve
model info. To use specific information for different models, add a
(nested) info_dict with model name as the key.
Example: `info_dict = {"N":lambda x:(x.nobs), "R2": ..., "OLS":{
"R2":...}}` would only show `R2` for OLS regression models, but
additionally `N` for all other results.
Default : None (use the info_dict specified in
result.default_model_infos, if this property exists)
regressor_order : list[str], optional
list of names of the regressors in the desired order. All regressors
not specified will be appended to the end of the list.
drop_omitted : bool, optional
Includes regressors that are not specified in regressor_order. If
False, regressors not specified will be appended to end of the list.
If True, only regressors in regressor_order will be included.
include_r2 : bool, optional
Includes R2 and adjusted R2 in the summary table.
"""
if not isinstance(results, list):
results = [results]
cols = [_col_params(x, stars=stars, float_format=float_format,
include_r2=include_r2) for x in results]
# Unique column names (pandas has problems merging otherwise)
if model_names:
colnames = _make_unique(model_names)
else:
colnames = _make_unique([x.columns[0] for x in cols])
for i in range(len(cols)):
cols[i].columns = [colnames[i]]
def merg(x, y):
return x.merge(y, how='outer', right_index=True, left_index=True)
# Changes due to how pandas 2.2.0 handles merge
index = list(cols[0].index)
for col in cols[1:]:
for key in col.index:
if key not in index:
index.append(key)
for special in (('R-squared', ''), ('R-squared Adj.', '')):
if special in index:
index.remove(special)
index.insert(len(index), special)
summ = reduce(merg, cols)
summ = summ.reindex(index)
if regressor_order:
varnames = summ.index.get_level_values(0).tolist()
vc = pd.Series(varnames).value_counts()
varnames = vc.loc[vc == 2].index.tolist()
ordered = [x for x in regressor_order if x in varnames]
unordered = [x for x in varnames if x not in regressor_order]
new_order = ordered + unordered
other = [x for x in summ.index.get_level_values(0)
if x not in new_order]
new_order += other
if drop_omitted:
for uo in unordered:
new_order.remove(uo)
summ = summ.reindex(new_order, level=0)
idx = []
index = summ.index.get_level_values(0)
for i in range(0, index.shape[0], 2):
idx.append(index[i])
if (i + 1) < index.shape[0] and (index[i] == index[i + 1]):
idx.append("")
else:
idx.append(index[i + 1])
summ.index = idx
# add infos about the models.
if info_dict:
cols = [_col_info(x, info_dict.get(x.model.__class__.__name__,
info_dict)) for x in results]
else:
cols = [_col_info(x, getattr(x, "default_model_infos", None)) for x in
results]
# use unique column names, otherwise the merge will not succeed
for df, name in zip(cols, _make_unique([df.columns[0] for df in cols])):
df.columns = [name]
info = reduce(merg, cols)
dat = pd.DataFrame(np.vstack([summ, info])) # pd.concat better, but error
dat.columns = summ.columns
dat.index = pd.Index(summ.index.tolist() + info.index.tolist())
summ = dat
summ = summ.fillna('')
smry = Summary()
smry._merge_latex = True
smry.add_df(summ, header=True, align='l')
smry.add_text('Standard errors in parentheses.')
if stars:
smry.add_text('* p<.1, ** p<.05, ***p<.01')
return smry
def _formatter(element, float_format='%.4f'):
try:
out = float_format % element
except (ValueError, TypeError):
out = str(element)
return out.strip()
def _df_to_simpletable(df, align='r', float_format="%.4f", header=True,
index=True, table_dec_above='-', table_dec_below=None,
header_dec_below='-', pad_col=0, pad_index=0):
dat = df.copy()
try:
dat = dat.map(lambda x: _formatter(x, float_format))
except AttributeError:
dat = dat.applymap(lambda x: _formatter(x, float_format))
if header:
headers = [str(x) for x in dat.columns.tolist()]
else:
headers = None
if index:
stubs = [str(x) + int(pad_index) * ' ' for x in dat.index.tolist()]
else:
dat.iloc[:, 0] = [str(x) + int(pad_index) * ' '
for x in dat.iloc[:, 0]]
stubs = None
st = SimpleTable(np.array(dat), headers=headers, stubs=stubs,
ltx_fmt=fmt_latex, txt_fmt=fmt_txt)
st.output_formats['latex']['data_aligns'] = align
st.output_formats['latex']['header_align'] = align
st.output_formats['txt']['data_aligns'] = align
st.output_formats['txt']['table_dec_above'] = table_dec_above
st.output_formats['txt']['table_dec_below'] = table_dec_below
st.output_formats['txt']['header_dec_below'] = header_dec_below
st.output_formats['txt']['colsep'] = ' ' * int(pad_col + 1)
return st
def _simple_tables(tables, settings, pad_col=None, pad_index=None):
simple_tables = []
float_format = settings[0]['float_format'] if settings else '%.4f'
if pad_col is None:
pad_col = [0] * len(tables)
if pad_index is None:
pad_index = [0] * len(tables)
for i, v in enumerate(tables):
index = settings[i]['index']
header = settings[i]['header']
align = settings[i]['align']
simple_tables.append(_df_to_simpletable(v, align=align,
float_format=float_format,
header=header, index=index,
pad_col=pad_col[i],
pad_index=pad_index[i]))
return simple_tables

View File

@ -0,0 +1,927 @@
"""
Provides a simple table class. A SimpleTable is essentially
a list of lists plus some formatting functionality.
Dependencies: the Python 2.5+ standard library.
Installation: just copy this module into your working directory (or
anywhere in your pythonpath).
Basic use::
mydata = [[11,12],[21,22]] # data MUST be 2-dimensional
myheaders = [ "Column 1", "Column 2" ]
mystubs = [ "Row 1", "Row 2" ]
tbl = SimpleTable(mydata, myheaders, mystubs, title="Title")
print( tbl )
print( tbl.as_csv() )
A SimpleTable is inherently (but not rigidly) rectangular.
You should create it from a *rectangular* (2d!) iterable of data.
Each item in your rectangular iterable will become the data
of a single Cell. In principle, items can be any object,
not just numbers and strings. However, default conversion
during table production is by simple string interpolation.
(So you cannot have a tuple as a data item *and* rely on
the default conversion.)
A SimpleTable allows only one column (the first) of stubs at
initilization, concatenation of tables allows you to produce tables
with interior stubs. (You can also assign the datatype 'stub' to the
cells in any column, or use ``insert_stubs``.) A SimpleTable can be
concatenated with another SimpleTable or extended by another
SimpleTable. ::
table1.extend_right(table2)
table1.extend(table2)
A SimpleTable can be initialized with `datatypes`: a list of ints that
provide indexes into `data_fmts` and `data_aligns`. Each data cell is
assigned a datatype, which will control formatting. If you do not
specify the `datatypes` list, it will be set to ``range(ncols)`` where
`ncols` is the number of columns in the data. (I.e., cells in a
column have their own datatype.) This means that you can just specify
`data_fmts` without bothering to provide a `datatypes` list. If
``len(datatypes)<ncols`` then datatype assignment will cycle across a
row. E.g., if you provide 10 columns of data with ``datatypes=[0,1]``
then you will have 5 columns of datatype 0 and 5 columns of datatype
1, alternating. Corresponding to this specification, you should provide
a list of two ``data_fmts`` and a list of two ``data_aligns``.
Cells can be assigned labels as their `datatype` attribute.
You can then provide a format for that lable.
Us the SimpleTable's `label_cells` method to do this. ::
def mylabeller(cell):
if cell.data is np.nan:
return 'missing'
mytable.label_cells(mylabeller)
print(mytable.as_text(missing='-'))
Potential problems for Python 3
-------------------------------
- Calls ``next`` instead of ``__next__``.
The 2to3 tool should handle that no problem.
(We will switch to the `next` function if 2.5 support is ever dropped.)
- Let me know if you find other problems.
:contact: alan dot isaac at gmail dot com
:requires: Python 2.5.1+
:note: current version
:note: HTML data format currently specifies tags
:todo: support a bit more of http://www.oasis-open.org/specs/tr9503.html
:todo: add labels2formatters method, that associates a cell formatter with a
datatype
:todo: add colspan support to Cell
:since: 2008-12-21
:change: 2010-05-02 eliminate newlines that came before and after table
:change: 2010-05-06 add `label_cells` to `SimpleTable`
"""
from statsmodels.compat.python import lmap, lrange
from itertools import cycle, zip_longest
import csv
def csv2st(csvfile, headers=False, stubs=False, title=None):
"""Return SimpleTable instance,
created from the data in `csvfile`,
which is in comma separated values format.
The first row may contain headers: set headers=True.
The first column may contain stubs: set stubs=True.
Can also supply headers and stubs as tuples of strings.
"""
rows = list()
with open(csvfile, encoding="utf-8") as fh:
reader = csv.reader(fh)
if headers is True:
headers = next(reader)
elif headers is False:
headers = ()
if stubs is True:
stubs = list()
for row in reader:
if row:
stubs.append(row[0])
rows.append(row[1:])
else: # no stubs, or stubs provided
for row in reader:
if row:
rows.append(row)
if stubs is False:
stubs = ()
ncols = len(rows[0])
if any(len(row) != ncols for row in rows):
raise OSError('All rows of CSV file must have same length.')
return SimpleTable(data=rows, headers=headers, stubs=stubs)
class SimpleTable(list):
"""Produce a simple ASCII, CSV, HTML, or LaTeX table from a
*rectangular* (2d!) array of data, not necessarily numerical.
Directly supports at most one header row,
which should be the length of data[0].
Directly supports at most one stubs column,
which must be the length of data.
(But see `insert_stubs` method.)
See globals `default_txt_fmt`, `default_csv_fmt`, `default_html_fmt`,
and `default_latex_fmt` for formatting options.
Sample uses::
mydata = [[11,12],[21,22]] # data MUST be 2-dimensional
myheaders = [ "Column 1", "Column 2" ]
mystubs = [ "Row 1", "Row 2" ]
tbl = text.SimpleTable(mydata, myheaders, mystubs, title="Title")
print( tbl )
print( tbl.as_html() )
# set column specific data formatting
tbl = text.SimpleTable(mydata, myheaders, mystubs,
data_fmts=["%3.2f","%d"])
print( tbl.as_csv() )
with open('c:/temp/temp.tex','w') as fh:
fh.write( tbl.as_latex_tabular() )
"""
def __init__(self, data, headers=None, stubs=None, title='',
datatypes=None, csv_fmt=None, txt_fmt=None, ltx_fmt=None,
html_fmt=None, celltype=None, rowtype=None, **fmt_dict):
"""
Parameters
----------
data : list of lists or 2d array (not matrix!)
R rows by K columns of table elements
headers : list (or tuple) of str
sequence of K strings, one per header
stubs : list (or tuple) of str
sequence of R strings, one per stub
title : str
title of the table
datatypes : list of int
indexes to `data_fmts`
txt_fmt : dict
text formatting options
ltx_fmt : dict
latex formatting options
csv_fmt : dict
csv formatting options
hmtl_fmt : dict
hmtl formatting options
celltype : class
the cell class for the table (default: Cell)
rowtype : class
the row class for the table (default: Row)
fmt_dict : dict
general formatting options
"""
self.title = title
self._datatypes = datatypes
if self._datatypes is None:
self._datatypes = [] if len(data) == 0 else lrange(len(data[0]))
# start with default formatting
self._txt_fmt = default_txt_fmt.copy()
self._latex_fmt = default_latex_fmt.copy()
self._csv_fmt = default_csv_fmt.copy()
self._html_fmt = default_html_fmt.copy()
# substitute any general user specified formatting
# :note: these will be overridden by output specific arguments
self._csv_fmt.update(fmt_dict)
self._txt_fmt.update(fmt_dict)
self._latex_fmt.update(fmt_dict)
self._html_fmt.update(fmt_dict)
# substitute any output-type specific formatting
self._csv_fmt.update(csv_fmt or dict())
self._txt_fmt.update(txt_fmt or dict())
self._latex_fmt.update(ltx_fmt or dict())
self._html_fmt.update(html_fmt or dict())
self.output_formats = dict(
txt=self._txt_fmt,
csv=self._csv_fmt,
html=self._html_fmt,
latex=self._latex_fmt
)
self._Cell = celltype or Cell
self._Row = rowtype or Row
rows = self._data2rows(data) # a list of Row instances
list.__init__(self, rows)
self._add_headers_stubs(headers, stubs)
self._colwidths = dict()
def __str__(self):
return self.as_text()
def __repr__(self):
return str(type(self))
def _repr_html_(self, **fmt_dict):
return self.as_html(**fmt_dict)
def _repr_latex_(self, center=True, **fmt_dict):
return self.as_latex_tabular(center, **fmt_dict)
def _add_headers_stubs(self, headers, stubs):
"""Return None. Adds headers and stubs to table,
if these were provided at initialization.
Parameters
----------
headers : list[str]
K strings, where K is number of columns
stubs : list[str]
R strings, where R is number of non-header rows
:note: a header row does not receive a stub!
"""
if headers:
self.insert_header_row(0, headers, dec_below='header_dec_below')
if stubs:
self.insert_stubs(0, stubs)
def insert(self, idx, row, datatype=None):
"""Return None. Insert a row into a table.
"""
if datatype is None:
try:
datatype = row.datatype
except AttributeError:
pass
row = self._Row(row, datatype=datatype, table=self)
list.insert(self, idx, row)
def insert_header_row(self, rownum, headers, dec_below='header_dec_below'):
"""Return None. Insert a row of headers,
where ``headers`` is a sequence of strings.
(The strings may contain newlines, to indicated multiline headers.)
"""
header_rows = [header.split('\n') for header in headers]
# rows in reverse order
rows = list(zip_longest(*header_rows, **dict(fillvalue='')))
rows.reverse()
for i, row in enumerate(rows):
self.insert(rownum, row, datatype='header')
if i == 0:
self[rownum].dec_below = dec_below
else:
self[rownum].dec_below = None
def insert_stubs(self, loc, stubs):
"""Return None. Insert column of stubs at column `loc`.
If there is a header row, it gets an empty cell.
So ``len(stubs)`` should equal the number of non-header rows.
"""
_Cell = self._Cell
stubs = iter(stubs)
for row in self:
if row.datatype == 'header':
empty_cell = _Cell('', datatype='empty')
row.insert(loc, empty_cell)
else:
try:
row.insert_stub(loc, next(stubs))
except StopIteration:
raise ValueError('length of stubs must match table length')
def _data2rows(self, raw_data):
"""Return list of Row,
the raw data as rows of cells.
"""
_Cell = self._Cell
_Row = self._Row
rows = []
for datarow in raw_data:
dtypes = cycle(self._datatypes)
newrow = _Row(datarow, datatype='data', table=self, celltype=_Cell)
for cell in newrow:
cell.datatype = next(dtypes)
cell.row = newrow # a cell knows its row
rows.append(newrow)
return rows
def pad(self, s, width, align):
"""DEPRECATED: just use the pad function"""
return pad(s, width, align)
def _get_colwidths(self, output_format, **fmt_dict):
"""Return list, the calculated widths of each column."""
output_format = get_output_format(output_format)
fmt = self.output_formats[output_format].copy()
fmt.update(fmt_dict)
ncols = max(len(row) for row in self)
request = fmt.get('colwidths')
if request == 0: # assume no extra space desired (e.g, CSV)
return [0] * ncols
elif request is None: # assume no extra space desired (e.g, CSV)
request = [0] * ncols
elif isinstance(request, int):
request = [request] * ncols
elif len(request) < ncols:
request = [request[i % len(request)] for i in range(ncols)]
min_widths = []
for col in zip(*self):
maxwidth = max(len(c.format(0, output_format, **fmt)) for c in col)
min_widths.append(maxwidth)
result = lmap(max, min_widths, request)
return result
def get_colwidths(self, output_format, **fmt_dict):
"""Return list, the widths of each column."""
call_args = [output_format]
for k, v in sorted(fmt_dict.items()):
if isinstance(v, list):
call_args.append((k, tuple(v)))
elif isinstance(v, dict):
call_args.append((k, tuple(sorted(v.items()))))
else:
call_args.append((k, v))
key = tuple(call_args)
try:
return self._colwidths[key]
except KeyError:
self._colwidths[key] = self._get_colwidths(output_format,
**fmt_dict)
return self._colwidths[key]
def _get_fmt(self, output_format, **fmt_dict):
"""Return dict, the formatting options.
"""
output_format = get_output_format(output_format)
# first get the default formatting
try:
fmt = self.output_formats[output_format].copy()
except KeyError:
raise ValueError('Unknown format: %s' % output_format)
# then, add formatting specific to this call
fmt.update(fmt_dict)
return fmt
def as_csv(self, **fmt_dict):
"""Return string, the table in CSV format.
Currently only supports comma separator."""
# fetch the format, which may just be default_csv_format
fmt = self._get_fmt('csv', **fmt_dict)
return self.as_text(**fmt)
def as_text(self, **fmt_dict):
"""Return string, the table as text."""
# fetch the text format, override with fmt_dict
fmt = self._get_fmt('txt', **fmt_dict)
# get rows formatted as strings
formatted_rows = [row.as_string('text', **fmt) for row in self]
rowlen = len(formatted_rows[-1]) # do not use header row
# place decoration above the table body, if desired
table_dec_above = fmt.get('table_dec_above', '=')
if table_dec_above:
formatted_rows.insert(0, table_dec_above * rowlen)
# next place a title at the very top, if desired
# :note: user can include a newlines at end of title if desired
title = self.title
if title:
title = pad(self.title, rowlen, fmt.get('title_align', 'c'))
formatted_rows.insert(0, title)
# add decoration below the table, if desired
table_dec_below = fmt.get('table_dec_below', '-')
if table_dec_below:
formatted_rows.append(table_dec_below * rowlen)
return '\n'.join(formatted_rows)
def as_html(self, **fmt_dict):
"""Return string.
This is the default formatter for HTML tables.
An HTML table formatter must accept as arguments
a table and a format dictionary.
"""
# fetch the text format, override with fmt_dict
fmt = self._get_fmt('html', **fmt_dict)
formatted_rows = ['<table class="simpletable">']
if self.title:
title = '<caption>%s</caption>' % self.title
formatted_rows.append(title)
formatted_rows.extend(row.as_string('html', **fmt) for row in self)
formatted_rows.append('</table>')
return '\n'.join(formatted_rows)
def as_latex_tabular(self, center=True, **fmt_dict):
'''Return string, the table as a LaTeX tabular environment.
Note: will require the booktabs package.'''
# fetch the text format, override with fmt_dict
fmt = self._get_fmt('latex', **fmt_dict)
formatted_rows = []
if center:
formatted_rows.append(r'\begin{center}')
table_dec_above = fmt['table_dec_above'] or ''
table_dec_below = fmt['table_dec_below'] or ''
prev_aligns = None
last = None
for row in self + [last]:
if row == last:
aligns = None
else:
aligns = row.get_aligns('latex', **fmt)
if aligns != prev_aligns:
# When the number/type of columns changes...
if prev_aligns:
# ... if there is a tabular to close, close it...
formatted_rows.append(table_dec_below)
formatted_rows.append(r'\end{tabular}')
if aligns:
# ... and if there are more lines, open a new one:
formatted_rows.append(r'\begin{tabular}{%s}' % aligns)
if not prev_aligns:
# (with a nice line if it's the top of the whole table)
formatted_rows.append(table_dec_above)
if row != last:
formatted_rows.append(
row.as_string(output_format='latex', **fmt))
prev_aligns = aligns
# tabular does not support caption, but make it available for
# figure environment
if self.title:
title = r'%%\caption{%s}' % self.title
formatted_rows.append(title)
if center:
formatted_rows.append(r'\end{center}')
# Replace $$ due to bug in GH 5444
return '\n'.join(formatted_rows).replace('$$', ' ')
def extend_right(self, table):
"""Return None.
Extend each row of `self` with corresponding row of `table`.
Does **not** import formatting from ``table``.
This generally makes sense only if the two tables have
the same number of rows, but that is not enforced.
:note: To extend append a table below, just use `extend`,
which is the ordinary list method. This generally makes sense
only if the two tables have the same number of columns,
but that is not enforced.
"""
for row1, row2 in zip(self, table):
row1.extend(row2)
def label_cells(self, func):
"""Return None. Labels cells based on `func`.
If ``func(cell) is None`` then its datatype is
not changed; otherwise it is set to ``func(cell)``.
"""
for row in self:
for cell in row:
label = func(cell)
if label is not None:
cell.datatype = label
@property
def data(self):
return [row.data for row in self]
def pad(s, width, align):
"""Return string padded with spaces,
based on alignment parameter."""
if align == 'l':
s = s.ljust(width)
elif align == 'r':
s = s.rjust(width)
else:
s = s.center(width)
return s
class Row(list):
"""Provides a table row as a list of cells.
A row can belong to a SimpleTable, but does not have to.
"""
def __init__(self, seq, datatype='data', table=None, celltype=None,
dec_below='row_dec_below', **fmt_dict):
"""
Parameters
----------
seq : sequence of data or cells
table : SimpleTable
datatype : str ('data' or 'header')
dec_below : str
(e.g., 'header_dec_below' or 'row_dec_below')
decoration tag, identifies the decoration to go below the row.
(Decoration is repeated as needed for text formats.)
"""
self.datatype = datatype
self.table = table
if celltype is None:
if table is None:
celltype = Cell
else:
celltype = table._Cell
self._Cell = celltype
self._fmt = fmt_dict
self.special_fmts = dict() # special formatting for any output format
self.dec_below = dec_below
list.__init__(self, (celltype(cell, row=self) for cell in seq))
def add_format(self, output_format, **fmt_dict):
"""
Return None. Adds row-instance specific formatting
for the specified output format.
Example: myrow.add_format('txt', row_dec_below='+-')
"""
output_format = get_output_format(output_format)
if output_format not in self.special_fmts:
self.special_fmts[output_format] = dict()
self.special_fmts[output_format].update(fmt_dict)
def insert_stub(self, loc, stub):
"""Return None. Inserts a stub cell
in the row at `loc`.
"""
_Cell = self._Cell
if not isinstance(stub, _Cell):
stub = stub
stub = _Cell(stub, datatype='stub', row=self)
self.insert(loc, stub)
def _get_fmt(self, output_format, **fmt_dict):
"""Return dict, the formatting options.
"""
output_format = get_output_format(output_format)
# first get the default formatting
try:
fmt = default_fmts[output_format].copy()
except KeyError:
raise ValueError('Unknown format: %s' % output_format)
# second get table specific formatting (if possible)
try:
fmt.update(self.table.output_formats[output_format])
except AttributeError:
pass
# finally, add formatting for this row and this call
fmt.update(self._fmt)
fmt.update(fmt_dict)
special_fmt = self.special_fmts.get(output_format, None)
if special_fmt is not None:
fmt.update(special_fmt)
return fmt
def get_aligns(self, output_format, **fmt_dict):
"""Return string, sequence of column alignments.
Ensure comformable data_aligns in `fmt_dict`."""
fmt = self._get_fmt(output_format, **fmt_dict)
return ''.join(cell.alignment(output_format, **fmt) for cell in self)
def as_string(self, output_format='txt', **fmt_dict):
"""Return string: the formatted row.
This is the default formatter for rows.
Override this to get different formatting.
A row formatter must accept as arguments
a row (self) and an output format,
one of ('html', 'txt', 'csv', 'latex').
"""
fmt = self._get_fmt(output_format, **fmt_dict)
# get column widths
try:
colwidths = self.table.get_colwidths(output_format, **fmt)
except AttributeError:
colwidths = fmt.get('colwidths')
if colwidths is None:
colwidths = (0,) * len(self)
colsep = fmt['colsep']
row_pre = fmt.get('row_pre', '')
row_post = fmt.get('row_post', '')
formatted_cells = []
for cell, width in zip(self, colwidths):
content = cell.format(width, output_format=output_format, **fmt)
formatted_cells.append(content)
formatted_row = row_pre + colsep.join(formatted_cells) + row_post
formatted_row = self._decorate_below(formatted_row, output_format,
**fmt)
return formatted_row
def _decorate_below(self, row_as_string, output_format, **fmt_dict):
"""This really only makes sense for the text and latex output formats.
"""
dec_below = fmt_dict.get(self.dec_below, None)
if dec_below is None:
result = row_as_string
else:
output_format = get_output_format(output_format)
if output_format == 'txt':
row0len = len(row_as_string)
dec_len = len(dec_below)
repeat, addon = divmod(row0len, dec_len)
result = row_as_string + "\n" + (dec_below * repeat +
dec_below[:addon])
elif output_format == 'latex':
result = row_as_string + "\n" + dec_below
else:
raise ValueError("I cannot decorate a %s header." %
output_format)
return result
@property
def data(self):
return [cell.data for cell in self]
class Cell:
"""Provides a table cell.
A cell can belong to a Row, but does not have to.
"""
def __init__(self, data='', datatype=None, row=None, **fmt_dict):
if isinstance(data, Cell):
# might have passed a Cell instance
self.data = data.data
self._datatype = data.datatype
self._fmt = data._fmt
else:
self.data = data
self._datatype = datatype
self._fmt = dict()
self._fmt.update(fmt_dict)
self.row = row
def __str__(self):
return '%s' % self.data
def _get_fmt(self, output_format, **fmt_dict):
"""Return dict, the formatting options.
"""
output_format = get_output_format(output_format)
# first get the default formatting
try:
fmt = default_fmts[output_format].copy()
except KeyError:
raise ValueError('Unknown format: %s' % output_format)
# then get any table specific formtting
try:
fmt.update(self.row.table.output_formats[output_format])
except AttributeError:
pass
# then get any row specific formtting
try:
fmt.update(self.row._fmt)
except AttributeError:
pass
# finally add formatting for this instance and call
fmt.update(self._fmt)
fmt.update(fmt_dict)
return fmt
def alignment(self, output_format, **fmt_dict):
fmt = self._get_fmt(output_format, **fmt_dict)
datatype = self.datatype
data_aligns = fmt.get('data_aligns', 'c')
if isinstance(datatype, int):
align = data_aligns[datatype % len(data_aligns)]
elif datatype == 'stub':
# still support deprecated `stubs_align`
align = fmt.get('stubs_align') or fmt.get('stub_align', 'l')
elif datatype in fmt:
label_align = '%s_align' % datatype
align = fmt.get(label_align, 'c')
else:
raise ValueError('Unknown cell datatype: %s' % datatype)
return align
@staticmethod
def _latex_escape(data, fmt, output_format):
if output_format != 'latex':
return data
if "replacements" in fmt:
if isinstance(data, str):
for repl in sorted(fmt["replacements"]):
data = data.replace(repl, fmt["replacements"][repl])
return data
def format(self, width, output_format='txt', **fmt_dict):
"""Return string.
This is the default formatter for cells.
Override this to get different formating.
A cell formatter must accept as arguments
a cell (self) and an output format,
one of ('html', 'txt', 'csv', 'latex').
It will generally respond to the datatype,
one of (int, 'header', 'stub').
"""
fmt = self._get_fmt(output_format, **fmt_dict)
data = self.data
datatype = self.datatype
data_fmts = fmt.get('data_fmts')
if data_fmts is None:
# chk allow for deprecated use of data_fmt
data_fmt = fmt.get('data_fmt')
if data_fmt is None:
data_fmt = '%s'
data_fmts = [data_fmt]
if isinstance(datatype, int):
datatype = datatype % len(data_fmts) # constrain to indexes
data_fmt = data_fmts[datatype]
if isinstance(data_fmt, str):
content = data_fmt % (data,)
elif callable(data_fmt):
content = data_fmt(data)
else:
raise TypeError("Must be a string or a callable")
if datatype == 0:
content = self._latex_escape(content, fmt, output_format)
elif datatype in fmt:
data = self._latex_escape(data, fmt, output_format)
dfmt = fmt.get(datatype)
try:
content = dfmt % (data,)
except TypeError: # dfmt is not a substitution string
content = dfmt
else:
raise ValueError('Unknown cell datatype: %s' % datatype)
align = self.alignment(output_format, **fmt)
return pad(content, width, align)
def get_datatype(self):
if self._datatype is None:
dtype = self.row.datatype
else:
dtype = self._datatype
return dtype
def set_datatype(self, val):
# TODO: add checking
self._datatype = val
datatype = property(get_datatype, set_datatype)
# begin: default formats for SimpleTable
""" Some formatting suggestions:
- if you want rows to have no extra spacing,
set colwidths=0 and colsep=''.
(Naturally the columns will not align.)
- if you want rows to have minimal extra spacing,
set colwidths=1. The columns will align.
- to get consistent formatting, you should leave
all field width handling to SimpleTable:
use 0 as the field width in data_fmts. E.g., ::
data_fmts = ["%#0.6g","%#0.6g","%#0.4g","%#0.4g"],
colwidths = 14,
data_aligns = "r",
"""
default_txt_fmt = dict(
fmt='txt',
# basic table formatting
table_dec_above='=',
table_dec_below='-',
title_align='c',
# basic row formatting
row_pre='',
row_post='',
header_dec_below='-',
row_dec_below=None,
colwidths=None,
colsep=' ',
data_aligns="r", # GH 1477
# data formats
# data_fmt="%s", #deprecated; use data_fmts
data_fmts=["%s"],
# labeled alignments
# stubs_align='l', #deprecated; use data_fmts
stub_align='l',
header_align='c',
# labeled formats
header_fmt='%s', # deprecated; just use 'header'
stub_fmt='%s', # deprecated; just use 'stub'
header='%s',
stub='%s',
empty_cell='', # deprecated; just use 'empty'
empty='',
missing='--',
)
default_csv_fmt = dict(
fmt='csv',
table_dec_above=None, # '',
table_dec_below=None, # '',
# basic row formatting
row_pre='',
row_post='',
header_dec_below=None, # '',
row_dec_below=None,
title_align='',
data_aligns="l",
colwidths=None,
colsep=',',
# data formats
data_fmt='%s', # deprecated; use data_fmts
data_fmts=['%s'],
# labeled alignments
# stubs_align='l', # deprecated; use data_fmts
stub_align="l",
header_align='c',
# labeled formats
header_fmt='"%s"', # deprecated; just use 'header'
stub_fmt='"%s"', # deprecated; just use 'stub'
empty_cell='', # deprecated; just use 'empty'
header='%s',
stub='%s',
empty='',
missing='--',
)
default_html_fmt = dict(
# basic table formatting
table_dec_above=None,
table_dec_below=None,
header_dec_below=None,
row_dec_below=None,
title_align='c',
# basic row formatting
colwidths=None,
colsep=' ',
row_pre='<tr>\n ',
row_post='\n</tr>',
data_aligns="c",
# data formats
data_fmts=['<td>%s</td>'],
data_fmt="<td>%s</td>", # deprecated; use data_fmts
# labeled alignments
# stubs_align='l', #deprecated; use data_fmts
stub_align='l',
header_align='c',
# labeled formats
header_fmt='<th>%s</th>', # deprecated; just use `header`
stub_fmt='<th>%s</th>', # deprecated; just use `stub`
empty_cell='<td></td>', # deprecated; just use `empty`
header='<th>%s</th>',
stub='<th>%s</th>',
empty='<td></td>',
missing='<td>--</td>',
)
default_latex_fmt = dict(
fmt='ltx',
# basic table formatting
table_dec_above=r'\toprule',
table_dec_below=r'\bottomrule',
header_dec_below=r'\midrule',
row_dec_below=None,
strip_backslash=True, # NotImplemented
# row formatting
row_post=r' \\',
data_aligns='c',
colwidths=None,
colsep=' & ',
# data formats
data_fmts=['%s'],
data_fmt='%s', # deprecated; use data_fmts
# labeled alignments
# stubs_align='l', # deprecated; use data_fmts
stub_align='l',
header_align='c',
empty_align='l',
# labeled formats
header_fmt=r'\textbf{%s}', # deprecated; just use 'header'
stub_fmt=r'\textbf{%s}', # deprecated; just use 'stub'
empty_cell='', # deprecated; just use 'empty'
header=r'\textbf{%s}',
stub=r'\textbf{%s}',
empty='',
missing='--',
# replacements will be processed in lexicographical order
replacements={"#": r"\#",
"$": r"\$",
"%": r"\%",
"&": r"\&",
">": r"$>$",
"_": r"\_",
"|": r"$|$"}
)
default_fmts = dict(
html=default_html_fmt,
txt=default_txt_fmt,
latex=default_latex_fmt,
csv=default_csv_fmt
)
output_format_translations = dict(
htm='html',
text='txt',
ltx='latex'
)
def get_output_format(output_format):
if output_format not in ('html', 'txt', 'latex', 'csv'):
try:
output_format = output_format_translations[output_format]
except KeyError:
raise ValueError('unknown output format %s' % output_format)
return output_format

View File

@ -0,0 +1,152 @@
"""
Summary Table formating
This is here to help keep the formating consistent across the different models
"""
import copy
gen_fmt = {
"data_fmts": ["%s", "%s", "%s", "%s", "%s"],
"empty_cell": '',
"colwidths": 7,
"colsep": ' ',
"row_pre": ' ',
"row_post": ' ',
"table_dec_above": '": ',
"table_dec_below": None,
"header_dec_below": None,
"header_fmt": '%s',
"stub_fmt": '%s',
"title_align": 'c',
"header_align": 'r',
"data_aligns": "r",
"stubs_align": "l",
"fmt": 'txt'
}
# Note table_1l_fmt over rides the below formating unless it is not
# appended to table_1l
fmt_1_right = {
"data_fmts": ["%s", "%s", "%s", "%s", "%s"],
"empty_cell": '',
"colwidths": 16,
"colsep": ' ',
"row_pre": '',
"row_post": '',
"table_dec_above": '": ',
"table_dec_below": None,
"header_dec_below": None,
"header_fmt": '%s',
"stub_fmt": '%s',
"title_align": 'c',
"header_align": 'r',
"data_aligns": "r",
"stubs_align": "l",
"fmt": 'txt'
}
fmt_2 = {
"data_fmts": ["%s", "%s", "%s", "%s"],
"empty_cell": '',
"colwidths": 10,
"colsep": ' ',
"row_pre": ' ',
"row_post": ' ',
"table_dec_above": '": ',
"table_dec_below": '": ',
"header_dec_below": '-',
"header_fmt": '%s',
"stub_fmt": '%s',
"title_align": 'c',
"header_align": 'r',
"data_aligns": 'r',
"stubs_align": 'l',
"fmt": 'txt'
}
# new version # TODO: as of when? compared to what? is old version needed?
fmt_base = {
"data_fmts": ["%s", "%s", "%s", "%s", "%s"],
"empty_cell": '',
"colwidths": 10,
"colsep": ' ',
"row_pre": '',
"row_post": '',
"table_dec_above": '=',
"table_dec_below": '=', # TODO need '=' at the last subtable
"header_dec_below": '-',
"header_fmt": '%s',
"stub_fmt": '%s',
"title_align": 'c',
"header_align": 'r',
"data_aligns": 'r',
"stubs_align": 'l',
"fmt": 'txt'
}
fmt_2cols = copy.deepcopy(fmt_base)
fmt2 = {
"data_fmts": ["%18s", "-%19s", "%18s", "%19s"], # TODO: TODO: what?
"colsep": ' ',
"colwidths": 18,
"stub_fmt": '-%21s',
}
fmt_2cols.update(fmt2)
fmt_params = copy.deepcopy(fmt_base)
fmt3 = {
"data_fmts": ["%s", "%s", "%8s", "%s", "%11s", "%11s"],
}
fmt_params.update(fmt3)
"""
Summary Table formating
This is here to help keep the formating consistent across the different models
"""
fmt_latex = {
'colsep': ' & ',
'colwidths': None,
'data_aligns': 'r',
'data_fmt': '%s',
'data_fmts': ['%s'],
'empty': '',
'empty_cell': '',
'fmt': 'ltx',
'header': '%s',
'header_align': 'c',
'header_dec_below': '\\hline',
'header_fmt': '%s',
'missing': '--',
'row_dec_below': None,
'row_post': ' \\\\',
'strip_backslash': True,
'stub': '%s',
'stub_align': 'l',
'stub_fmt': '%s',
'table_dec_above': '\\hline',
'table_dec_below': '\\hline'}
fmt_txt = {
'colsep': ' ',
'colwidths': None,
'data_aligns': 'r',
'data_fmts': ['%s'],
'empty': '',
'empty_cell': '',
'fmt': 'txt',
'header': '%s',
'header_align': 'c',
'header_dec_below': '-',
'header_fmt': '%s',
'missing': '--',
'row_dec_below': None,
'row_post': '',
'row_pre': '',
'stub': '%s',
'stub_align': 'l',
'stub_fmt': '%s',
'table_dec_above': '-',
'table_dec_below': None,
'title_align': 'c'}

View File

@ -0,0 +1,413 @@
from numpy import array
macrodata_result = array([
(1959.0, 1.0, 2710.349, 1707.4, 286.898, 470.045, 1886.9,
28.98, 139.7, 2.82, 5.8, 177.146, 0.0, 0.0),
(1959.0, 2.0, 2778.801, 1733.7, 310.859, 481.301, 1919.7,
29.15, 141.7, 3.08, 5.1, 177.83, 2.34, 0.74),
(1959.0, 3.0, 2775.488, 1751.8, 289.226, 491.26, 1916.4,
29.35, 140.5, 3.82, 5.3, 178.657, 2.74, 1.09),
(1959.0, 4.0, 2785.204, 1753.7, 299.356, 484.052, 1931.3,
29.37, 140.0, 4.33, 5.6, 179.386, 0.27, 4.06),
(1960.0, 1.0, 2847.699, 1770.5, 331.722, 462.199, 1955.5,
29.54, 139.6, 3.5, 5.2, 180.007, 2.31, 1.19),
(1960.0, 2.0, 2834.39, 1792.9, 298.152, 460.4, 1966.1,
29.55, 140.2, 2.68, 5.2, 180.671, 0.14, 2.55),
(1960.0, 3.0, 2839.022, 1785.8, 296.375, 474.676, 1967.8,
29.75, 140.9, 2.36, 5.6, 181.528, 2.7, -0.34),
(1960.0, 4.0, 2802.616, 1788.2, 259.764, 476.434, 1966.6,
29.84, 141.1, 2.29, 6.3, 182.287, 1.21, 1.08),
(1961.0, 1.0, 2819.264, 1787.7, 266.405, 475.854, 1984.5,
29.81, 142.1, 2.37, 6.8, 182.992, -0.4, 2.77),
(1961.0, 2.0, 2872.005, 1814.3, 286.246, 480.328, 2014.4,
29.92, 142.9, 2.29, 7.0, 183.691, 1.47, 0.81),
(1961.0, 3.0, 2918.419, 1823.1, 310.227, 493.828, 2041.9,
29.98, 144.1, 2.32, 6.8, 184.524, 0.8, 1.52),
(1961.0, 4.0, 2977.83, 1859.6, 315.463, 502.521, 2082.0,
30.04, 145.2, 2.6, 6.2, 185.242, 0.8, 1.8),
(1962.0, 1.0, 3031.241, 1879.4, 334.271, 520.96, 2101.7,
30.21, 146.4, 2.73, 5.6, 185.874, 2.26, 0.47),
(1962.0, 2.0, 3064.709, 1902.5, 331.039, 523.066, 2125.2,
30.22, 146.5, 2.78, 5.5, 186.538, 0.13, 2.65),
(1962.0, 3.0, 3093.047, 1917.9, 336.962, 538.838, 2137.0,
30.38, 146.7, 2.78, 5.6, 187.323, 2.11, 0.67),
(1962.0, 4.0, 3100.563, 1945.1, 325.65, 535.912, 2154.6,
30.44, 148.3, 2.87, 5.5, 188.013, 0.79, 2.08),
(1963.0, 1.0, 3141.087, 1958.2, 343.721, 522.917, 2172.5,
30.48, 149.7, 2.9, 5.8, 188.58, 0.53, 2.38),
(1963.0, 2.0, 3180.447, 1976.9, 348.73, 518.108, 2193.1,
30.69, 151.3, 3.03, 5.7, 189.242, 2.75, 0.29),
(1963.0, 3.0, 3240.332, 2003.8, 360.102, 546.893, 2217.9,
30.75, 152.6, 3.38, 5.5, 190.028, 0.78, 2.6),
(1963.0, 4.0, 3264.967, 2020.6, 364.534, 532.383, 2254.6,
30.94, 153.7, 3.52, 5.6, 190.668, 2.46, 1.06),
(1964.0, 1.0, 3338.246, 2060.5, 379.523, 529.686, 2299.6,
30.95, 154.8, 3.51, 5.5, 191.245, 0.13, 3.38),
(1964.0, 2.0, 3376.587, 2096.7, 377.778, 526.175, 2362.1,
31.02, 156.8, 3.47, 5.2, 191.889, 0.9, 2.57),
(1964.0, 3.0, 3422.469, 2135.2, 386.754, 522.008, 2392.7,
31.12, 159.2, 3.53, 5.0, 192.631, 1.29, 2.25),
(1964.0, 4.0, 3431.957, 2141.2, 389.91, 514.603, 2420.4,
31.28, 160.7, 3.76, 5.0, 193.223, 2.05, 1.71),
(1965.0, 1.0, 3516.251, 2188.8, 429.145, 508.006, 2447.4,
31.38, 162.0, 3.93, 4.9, 193.709, 1.28, 2.65),
(1965.0, 2.0, 3563.96, 2213.0, 429.119, 508.931, 2474.5,
31.58, 163.1, 3.84, 4.7, 194.303, 2.54, 1.3),
(1965.0, 3.0, 3636.285, 2251.0, 444.444, 529.446, 2542.6,
31.65, 166.0, 3.93, 4.4, 194.997, 0.89, 3.04),
(1965.0, 4.0, 3724.014, 2314.3, 446.493, 544.121, 2594.1,
31.88, 169.1, 4.35, 4.1, 195.539, 2.9, 1.46),
(1966.0, 1.0, 3815.423, 2348.5, 484.244, 556.593, 2618.4,
32.28, 171.8, 4.62, 3.9, 195.999, 4.99, -0.37),
(1966.0, 2.0, 3828.124, 2354.5, 475.408, 571.371, 2624.7,
32.45, 170.3, 4.65, 3.8, 196.56, 2.1, 2.55),
(1966.0, 3.0, 3853.301, 2381.5, 470.697, 594.514, 2657.8,
32.85, 171.2, 5.23, 3.8, 197.207, 4.9, 0.33),
(1966.0, 4.0, 3884.52, 2391.4, 472.957, 599.528, 2688.2,
32.9, 171.9, 5.0, 3.7, 197.736, 0.61, 4.39),
(1967.0, 1.0, 3918.74, 2405.3, 460.007, 640.682, 2728.4,
33.1, 174.2, 4.22, 3.8, 198.206, 2.42, 1.8),
(1967.0, 2.0, 3919.556, 2438.1, 440.393, 631.43, 2750.8,
33.4, 178.1, 3.78, 3.8, 198.712, 3.61, 0.17),
(1967.0, 3.0, 3950.826, 2450.6, 453.033, 641.504, 2777.1,
33.7, 181.6, 4.42, 3.8, 199.311, 3.58, 0.84),
(1967.0, 4.0, 3980.97, 2465.7, 462.834, 640.234, 2797.4,
34.1, 184.3, 4.9, 3.9, 199.808, 4.72, 0.18),
(1968.0, 1.0, 4063.013, 2524.6, 472.907, 651.378, 2846.2,
34.4, 186.6, 5.18, 3.7, 200.208, 3.5, 1.67),
(1968.0, 2.0, 4131.998, 2563.3, 492.026, 646.145, 2893.5,
34.9, 190.5, 5.5, 3.5, 200.706, 5.77, -0.28),
(1968.0, 3.0, 4160.267, 2611.5, 476.053, 640.615, 2899.3,
35.3, 194.0, 5.21, 3.5, 201.29, 4.56, 0.65),
(1968.0, 4.0, 4178.293, 2623.5, 480.998, 636.729, 2918.4,
35.7, 198.7, 5.85, 3.4, 201.76, 4.51, 1.34),
(1969.0, 1.0, 4244.1, 2652.9, 512.686, 633.224, 2923.4,
36.3, 200.7, 6.08, 3.4, 202.161, 6.67, -0.58),
(1969.0, 2.0, 4256.46, 2669.8, 508.601, 623.16, 2952.9,
36.8, 201.7, 6.49, 3.4, 202.677, 5.47, 1.02),
(1969.0, 3.0, 4283.378, 2682.7, 520.36, 623.613, 3012.9,
37.3, 202.9, 7.02, 3.6, 203.302, 5.4, 1.63),
(1969.0, 4.0, 4263.261, 2704.1, 492.334, 606.9, 3034.9,
37.9, 206.2, 7.64, 3.6, 203.849, 6.38, 1.26),
(1970.0, 1.0, 4256.573, 2720.7, 476.925, 594.888, 3050.1,
38.5, 206.7, 6.76, 4.2, 204.401, 6.28, 0.47),
(1970.0, 2.0, 4264.289, 2733.2, 478.419, 576.257, 3103.5,
38.9, 208.0, 6.66, 4.8, 205.052, 4.13, 2.52),
(1970.0, 3.0, 4302.259, 2757.1, 486.594, 567.743, 3145.4,
39.4, 212.9, 6.15, 5.2, 205.788, 5.11, 1.04),
(1970.0, 4.0, 4256.637, 2749.6, 458.406, 564.666, 3135.1,
39.9, 215.5, 4.86, 5.8, 206.466, 5.04, -0.18),
(1971.0, 1.0, 4374.016, 2802.2, 517.935, 542.709, 3197.3,
40.1, 220.0, 3.65, 5.9, 207.065, 2.0, 1.65),
(1971.0, 2.0, 4398.829, 2827.9, 533.986, 534.905, 3245.3,
40.6, 224.9, 4.76, 5.9, 207.661, 4.96, -0.19),
(1971.0, 3.0, 4433.943, 2850.4, 541.01, 532.646, 3259.7,
40.9, 227.2, 4.7, 6.0, 208.345, 2.94, 1.75),
(1971.0, 4.0, 4446.264, 2897.8, 524.085, 516.14, 3294.2,
41.2, 230.1, 3.87, 6.0, 208.917, 2.92, 0.95),
(1972.0, 1.0, 4525.769, 2936.5, 561.147, 518.192, 3314.9,
41.5, 235.6, 3.55, 5.8, 209.386, 2.9, 0.64),
(1972.0, 2.0, 4633.101, 2992.6, 595.495, 526.473, 3346.1,
41.8, 238.8, 3.86, 5.7, 209.896, 2.88, 0.98),
(1972.0, 3.0, 4677.503, 3038.8, 603.97, 498.116, 3414.6,
42.2, 245.0, 4.47, 5.6, 210.479, 3.81, 0.66),
(1972.0, 4.0, 4754.546, 3110.1, 607.104, 496.54, 3550.5,
42.7, 251.5, 5.09, 5.3, 210.985, 4.71, 0.38),
(1973.0, 1.0, 4876.166, 3167.0, 645.654, 504.838, 3590.7,
43.7, 252.7, 5.98, 5.0, 211.42, 9.26, -3.28),
(1973.0, 2.0, 4932.571, 3165.4, 675.837, 497.033, 3626.2,
44.2, 257.5, 7.19, 4.9, 211.909, 4.55, 2.64),
(1973.0, 3.0, 4906.252, 3176.7, 649.412, 475.897, 3644.4,
45.6, 259.0, 8.06, 4.8, 212.475, 12.47, -4.41),
(1973.0, 4.0, 4953.05, 3167.4, 674.253, 476.174, 3688.9,
46.8, 263.8, 7.68, 4.8, 212.932, 10.39, -2.71),
(1974.0, 1.0, 4909.617, 3139.7, 631.23, 491.043, 3632.3,
48.1, 267.2, 7.8, 5.1, 213.361, 10.96, -3.16),
(1974.0, 2.0, 4922.188, 3150.6, 628.102, 490.177, 3601.1,
49.3, 269.3, 7.89, 5.2, 213.854, 9.86, -1.96),
(1974.0, 3.0, 4873.52, 3163.6, 592.672, 492.586, 3612.4,
51.0, 272.3, 8.16, 5.6, 214.451, 13.56, -5.4),
(1974.0, 4.0, 4854.34, 3117.3, 598.306, 496.176, 3596.0,
52.3, 273.9, 6.96, 6.6, 214.931, 10.07, -3.11),
(1975.0, 1.0, 4795.295, 3143.4, 493.212, 490.603, 3581.9,
53.0, 276.2, 5.53, 8.2, 215.353, 5.32, 0.22),
(1975.0, 2.0, 4831.942, 3195.8, 476.085, 486.679, 3749.3,
54.0, 283.7, 5.57, 8.9, 215.973, 7.48, -1.91),
(1975.0, 3.0, 4913.328, 3241.4, 516.402, 498.836, 3698.6,
54.9, 285.4, 6.27, 8.5, 216.587, 6.61, -0.34),
(1975.0, 4.0, 4977.511, 3275.7, 530.596, 500.141, 3736.0,
55.8, 288.4, 5.26, 8.3, 217.095, 6.5, -1.24),
(1976.0, 1.0, 5090.663, 3341.2, 585.541, 495.568, 3791.0,
56.1, 294.7, 4.91, 7.7, 217.528, 2.14, 2.77),
(1976.0, 2.0, 5128.947, 3371.8, 610.513, 494.532, 3822.2,
57.0, 297.2, 5.28, 7.6, 218.035, 6.37, -1.09),
(1976.0, 3.0, 5154.072, 3407.5, 611.646, 493.141, 3856.7,
57.9, 302.0, 5.05, 7.7, 218.644, 6.27, -1.22),
(1976.0, 4.0, 5191.499, 3451.8, 615.898, 494.415, 3884.4,
58.7, 308.3, 4.57, 7.8, 219.179, 5.49, -0.92),
(1977.0, 1.0, 5251.762, 3491.3, 646.198, 498.509, 3887.5,
60.0, 316.0, 4.6, 7.5, 219.684, 8.76, -4.16),
(1977.0, 2.0, 5356.131, 3510.6, 696.141, 506.695, 3931.8,
60.8, 320.2, 5.06, 7.1, 220.239, 5.3, -0.24),
(1977.0, 3.0, 5451.921, 3544.1, 734.078, 509.605, 3990.8,
61.6, 326.4, 5.82, 6.9, 220.904, 5.23, 0.59),
(1977.0, 4.0, 5450.793, 3597.5, 713.356, 504.584, 4071.2,
62.7, 334.4, 6.2, 6.6, 221.477, 7.08, -0.88),
(1978.0, 1.0, 5469.405, 3618.5, 727.504, 506.314, 4096.4,
63.9, 339.9, 6.34, 6.3, 221.991, 7.58, -1.24),
(1978.0, 2.0, 5684.569, 3695.9, 777.454, 518.366, 4143.4,
65.5, 347.6, 6.72, 6.0, 222.585, 9.89, -3.18),
(1978.0, 3.0, 5740.3, 3711.4, 801.452, 520.199, 4177.1,
67.1, 353.3, 7.64, 6.0, 223.271, 9.65, -2.01),
(1978.0, 4.0, 5816.222, 3741.3, 819.689, 524.782, 4209.8,
68.5, 358.6, 9.02, 5.9, 223.865, 8.26, 0.76),
(1979.0, 1.0, 5825.949, 3760.2, 819.556, 525.524, 4255.9,
70.6, 368.0, 9.42, 5.9, 224.438, 12.08, -2.66),
(1979.0, 2.0, 5831.418, 3758.0, 817.66, 532.04, 4226.1,
73.0, 377.2, 9.3, 5.7, 225.055, 13.37, -4.07),
(1979.0, 3.0, 5873.335, 3794.9, 801.742, 531.232, 4250.3,
75.2, 380.8, 10.49, 5.9, 225.801, 11.88, -1.38),
(1979.0, 4.0, 5889.495, 3805.0, 786.817, 531.126, 4284.3,
78.0, 385.8, 11.94, 5.9, 226.451, 14.62, -2.68),
(1980.0, 1.0, 5908.467, 3798.4, 781.114, 548.115, 4296.2,
80.9, 383.8, 13.75, 6.3, 227.061, 14.6, -0.85),
(1980.0, 2.0, 5787.373, 3712.2, 710.64, 561.895, 4236.1,
82.6, 394.0, 7.9, 7.3, 227.726, 8.32, -0.42),
(1980.0, 3.0, 5776.617, 3752.0, 656.477, 554.292, 4279.7,
84.7, 409.0, 10.34, 7.7, 228.417, 10.04, 0.3),
(1980.0, 4.0, 5883.46, 3802.0, 723.22, 556.13, 4368.1,
87.2, 411.3, 14.75, 7.4, 228.937, 11.64, 3.11),
(1981.0, 1.0, 6005.717, 3822.8, 795.091, 567.618, 4358.1,
89.1, 427.4, 13.95, 7.4, 229.403, 8.62, 5.32),
(1981.0, 2.0, 5957.795, 3822.8, 757.24, 584.54, 4358.6,
91.5, 426.9, 15.33, 7.4, 229.966, 10.63, 4.69),
(1981.0, 3.0, 6030.184, 3838.3, 804.242, 583.89, 4455.4,
93.4, 428.4, 14.58, 7.4, 230.641, 8.22, 6.36),
(1981.0, 4.0, 5955.062, 3809.3, 773.053, 590.125, 4464.4,
94.4, 442.7, 11.33, 8.2, 231.157, 4.26, 7.07),
(1982.0, 1.0, 5857.333, 3833.9, 692.514, 591.043, 4469.6,
95.0, 447.1, 12.95, 8.8, 231.645, 2.53, 10.42),
(1982.0, 2.0, 5889.074, 3847.7, 691.9, 596.403, 4500.8,
97.5, 448.0, 11.97, 9.4, 232.188, 10.39, 1.58),
(1982.0, 3.0, 5866.37, 3877.2, 683.825, 605.37, 4520.6,
98.1, 464.5, 8.1, 9.9, 232.816, 2.45, 5.65),
(1982.0, 4.0, 5871.001, 3947.9, 622.93, 623.307, 4536.4,
97.9, 477.2, 7.96, 10.7, 233.322, -0.82, 8.77),
(1983.0, 1.0, 5944.02, 3986.6, 645.11, 630.873, 4572.2,
98.8, 493.2, 8.22, 10.4, 233.781, 3.66, 4.56),
(1983.0, 2.0, 6077.619, 4065.7, 707.372, 644.322, 4605.5,
99.8, 507.8, 8.69, 10.1, 234.307, 4.03, 4.66),
(1983.0, 3.0, 6197.468, 4137.6, 754.937, 662.412, 4674.7,
100.8, 517.2, 8.99, 9.4, 234.907, 3.99, 5.01),
(1983.0, 4.0, 6325.574, 4203.2, 834.427, 639.197, 4771.1,
102.1, 525.1, 8.89, 8.5, 235.385, 5.13, 3.76),
(1984.0, 1.0, 6448.264, 4239.2, 921.763, 644.635, 4875.4,
103.3, 535.0, 9.43, 7.9, 235.839, 4.67, 4.76),
(1984.0, 2.0, 6559.594, 4299.9, 952.841, 664.839, 4959.4,
104.1, 540.9, 9.94, 7.5, 236.348, 3.09, 6.85),
(1984.0, 3.0, 6623.343, 4333.0, 974.989, 662.294, 5036.6,
105.1, 543.7, 10.19, 7.4, 236.976, 3.82, 6.37),
(1984.0, 4.0, 6677.264, 4390.1, 958.993, 684.282, 5084.5,
105.7, 557.0, 8.14, 7.3, 237.468, 2.28, 5.87),
(1985.0, 1.0, 6740.275, 4464.6, 927.375, 691.613, 5072.0,
107.0, 570.4, 8.25, 7.3, 237.9, 4.89, 3.36),
(1985.0, 2.0, 6797.344, 4505.2, 943.383, 708.524, 5172.7,
107.7, 589.1, 7.17, 7.3, 238.466, 2.61, 4.56),
(1985.0, 3.0, 6903.523, 4590.8, 932.959, 732.305, 5140.7,
108.5, 607.8, 7.13, 7.2, 239.113, 2.96, 4.17),
(1985.0, 4.0, 6955.918, 4600.9, 969.434, 732.026, 5193.9,
109.9, 621.4, 7.14, 7.0, 239.638, 5.13, 2.01),
(1986.0, 1.0, 7022.757, 4639.3, 967.442, 728.125, 5255.8,
108.7, 641.0, 6.56, 7.0, 240.094, -4.39, 10.95),
(1986.0, 2.0, 7050.969, 4688.7, 945.972, 751.334, 5315.5,
109.5, 670.3, 6.06, 7.2, 240.651, 2.93, 3.13),
(1986.0, 3.0, 7118.95, 4770.7, 916.315, 779.77, 5343.3,
110.2, 694.9, 5.31, 7.0, 241.274, 2.55, 2.76),
(1986.0, 4.0, 7153.359, 4799.4, 917.736, 767.671, 5346.5,
111.4, 730.2, 5.44, 6.8, 241.784, 4.33, 1.1),
(1987.0, 1.0, 7193.019, 4792.1, 945.776, 772.247, 5379.4,
112.7, 743.9, 5.61, 6.6, 242.252, 4.64, 0.97),
(1987.0, 2.0, 7269.51, 4856.3, 947.1, 782.962, 5321.0,
113.8, 743.0, 5.67, 6.3, 242.804, 3.89, 1.79),
(1987.0, 3.0, 7332.558, 4910.4, 948.055, 783.804, 5416.2,
115.0, 756.2, 6.19, 6.0, 243.446, 4.2, 1.99),
(1987.0, 4.0, 7458.022, 4922.2, 1021.98, 795.467, 5493.1,
116.0, 756.2, 5.76, 5.9, 243.981, 3.46, 2.29),
(1988.0, 1.0, 7496.6, 5004.4, 964.398, 773.851, 5562.1,
117.2, 768.1, 5.76, 5.7, 244.445, 4.12, 1.64),
(1988.0, 2.0, 7592.881, 5040.8, 987.858, 765.98, 5614.3,
118.5, 781.4, 6.48, 5.5, 245.021, 4.41, 2.07),
(1988.0, 3.0, 7632.082, 5080.6, 994.204, 760.245, 5657.5,
119.9, 783.3, 7.22, 5.5, 245.693, 4.7, 2.52),
(1988.0, 4.0, 7733.991, 5140.4, 1007.371, 783.065, 5708.5,
121.2, 785.7, 8.03, 5.3, 246.224, 4.31, 3.72),
(1989.0, 1.0, 7806.603, 5159.3, 1045.975, 767.024, 5773.4,
123.1, 779.2, 8.67, 5.2, 246.721, 6.22, 2.44),
(1989.0, 2.0, 7865.016, 5182.4, 1033.753, 784.275, 5749.8,
124.5, 777.8, 8.15, 5.2, 247.342, 4.52, 3.63),
(1989.0, 3.0, 7927.393, 5236.1, 1021.604, 791.819, 5787.0,
125.4, 786.6, 7.76, 5.3, 248.067, 2.88, 4.88),
(1989.0, 4.0, 7944.697, 5261.7, 1011.119, 787.844, 5831.3,
127.5, 795.4, 7.65, 5.4, 248.659, 6.64, 1.01),
(1990.0, 1.0, 8027.693, 5303.3, 1021.07, 799.681, 5875.1,
128.9, 806.2, 7.8, 5.3, 249.306, 4.37, 3.44),
(1990.0, 2.0, 8059.598, 5320.8, 1021.36, 800.639, 5913.9,
130.5, 810.1, 7.7, 5.3, 250.132, 4.93, 2.76),
(1990.0, 3.0, 8059.476, 5341.0, 997.319, 793.513, 5918.1,
133.4, 819.8, 7.33, 5.7, 251.057, 8.79, -1.46),
(1990.0, 4.0, 7988.864, 5299.5, 934.248, 800.525, 5878.2,
134.7, 827.2, 6.67, 6.1, 251.889, 3.88, 2.79),
(1991.0, 1.0, 7950.164, 5284.4, 896.21, 806.775, 5896.3,
135.1, 843.2, 5.83, 6.6, 252.643, 1.19, 4.65),
(1991.0, 2.0, 8003.822, 5324.7, 891.704, 809.081, 5941.1,
136.2, 861.5, 5.54, 6.8, 253.493, 3.24, 2.29),
(1991.0, 3.0, 8037.538, 5345.0, 913.904, 793.987, 5953.6,
137.2, 878.0, 5.18, 6.9, 254.435, 2.93, 2.25),
(1991.0, 4.0, 8069.046, 5342.6, 948.891, 778.378, 5992.4,
138.3, 910.4, 4.14, 7.1, 255.214, 3.19, 0.95),
(1992.0, 1.0, 8157.616, 5434.5, 927.796, 778.568, 6082.9,
139.4, 943.8, 3.88, 7.4, 255.992, 3.17, 0.71),
(1992.0, 2.0, 8244.294, 5466.7, 988.912, 777.762, 6129.5,
140.5, 963.2, 3.5, 7.6, 256.894, 3.14, 0.36),
(1992.0, 3.0, 8329.361, 5527.1, 999.135, 786.639, 6160.6,
141.7, 1003.8, 2.97, 7.6, 257.861, 3.4, -0.44),
(1992.0, 4.0, 8417.016, 5594.6, 1030.758, 787.064, 6248.2,
142.8, 1030.4, 3.12, 7.4, 258.679, 3.09, 0.02),
(1993.0, 1.0, 8432.485, 5617.2, 1054.979, 762.901, 6156.5,
143.8, 1047.6, 2.92, 7.2, 259.414, 2.79, 0.13),
(1993.0, 2.0, 8486.435, 5671.1, 1063.263, 752.158, 6252.3,
144.5, 1084.5, 3.02, 7.1, 260.255, 1.94, 1.08),
(1993.0, 3.0, 8531.108, 5732.7, 1062.514, 744.227, 6265.7,
145.6, 1113.0, 3.0, 6.8, 261.163, 3.03, -0.04),
(1993.0, 4.0, 8643.769, 5783.7, 1118.583, 748.102, 6358.1,
146.3, 1131.6, 3.05, 6.6, 261.919, 1.92, 1.13),
(1994.0, 1.0, 8727.919, 5848.1, 1166.845, 721.288, 6332.6,
147.2, 1141.1, 3.48, 6.6, 262.631, 2.45, 1.02),
(1994.0, 2.0, 8847.303, 5891.5, 1234.855, 717.197, 6440.6,
148.4, 1150.5, 4.2, 6.2, 263.436, 3.25, 0.96),
(1994.0, 3.0, 8904.289, 5938.7, 1212.655, 736.89, 6487.9,
149.4, 1150.1, 4.68, 6.0, 264.301, 2.69, 2.0),
(1994.0, 4.0, 9003.18, 5997.3, 1269.19, 716.702, 6574.0,
150.5, 1151.4, 5.53, 5.6, 265.044, 2.93, 2.6),
(1995.0, 1.0, 9025.267, 6004.3, 1282.09, 715.326, 6616.6,
151.8, 1149.3, 5.72, 5.5, 265.755, 3.44, 2.28),
(1995.0, 2.0, 9044.668, 6053.5, 1247.61, 712.492, 6617.2,
152.6, 1145.4, 5.52, 5.7, 266.557, 2.1, 3.42),
(1995.0, 3.0, 9120.684, 6107.6, 1235.601, 707.649, 6666.8,
153.5, 1137.3, 5.32, 5.7, 267.456, 2.35, 2.97),
(1995.0, 4.0, 9184.275, 6150.6, 1270.392, 681.081, 6706.2,
154.7, 1123.5, 5.17, 5.6, 268.151, 3.11, 2.05),
(1996.0, 1.0, 9247.188, 6206.9, 1287.128, 695.265, 6777.7,
156.1, 1124.8, 4.91, 5.5, 268.853, 3.6, 1.31),
(1996.0, 2.0, 9407.052, 6277.1, 1353.795, 705.172, 6850.6,
157.0, 1112.4, 5.09, 5.5, 269.667, 2.3, 2.79),
(1996.0, 3.0, 9488.879, 6314.6, 1422.059, 692.741, 6908.9,
158.2, 1086.1, 5.04, 5.3, 270.581, 3.05, 2.0),
(1996.0, 4.0, 9592.458, 6366.1, 1418.193, 690.744, 6946.8,
159.4, 1081.5, 4.99, 5.3, 271.36, 3.02, 1.97),
(1997.0, 1.0, 9666.235, 6430.2, 1451.304, 681.445, 7008.9,
159.9, 1063.8, 5.1, 5.2, 272.083, 1.25, 3.85),
(1997.0, 2.0, 9809.551, 6456.2, 1543.976, 693.525, 7061.5,
160.4, 1066.2, 5.01, 5.0, 272.912, 1.25, 3.76),
(1997.0, 3.0, 9932.672, 6566.0, 1571.426, 691.261, 7142.4,
161.5, 1065.5, 5.02, 4.9, 273.852, 2.73, 2.29),
(1997.0, 4.0, 10008.874, 6641.1, 1596.523, 690.311, 7241.5,
162.0, 1074.4, 5.11, 4.7, 274.626, 1.24, 3.88),
(1998.0, 1.0, 10103.425, 6707.2, 1672.732, 668.783, 7406.2,
162.2, 1076.1, 5.02, 4.6, 275.304, 0.49, 4.53),
(1998.0, 2.0, 10194.277, 6822.6, 1652.716, 687.184, 7512.0,
163.2, 1075.0, 4.98, 4.4, 276.115, 2.46, 2.52),
(1998.0, 3.0, 10328.787, 6913.1, 1700.071, 681.472, 7591.0,
163.9, 1086.0, 4.49, 4.5, 277.003, 1.71, 2.78),
(1998.0, 4.0, 10507.575, 7019.1, 1754.743, 688.147, 7646.5,
164.7, 1097.8, 4.38, 4.4, 277.79, 1.95, 2.43),
(1999.0, 1.0, 10601.179, 7088.3, 1809.993, 683.601, 7698.4,
165.9, 1101.9, 4.39, 4.3, 278.451, 2.9, 1.49),
(1999.0, 2.0, 10684.049, 7199.9, 1803.674, 683.594, 7716.0,
166.7, 1098.7, 4.54, 4.3, 279.295, 1.92, 2.62),
(1999.0, 3.0, 10819.914, 7286.4, 1848.949, 697.936, 7765.9,
168.1, 1102.3, 4.75, 4.2, 280.203, 3.35, 1.41),
(1999.0, 4.0, 11014.254, 7389.2, 1914.567, 713.445, 7887.7,
169.3, 1121.9, 5.2, 4.1, 280.976, 2.85, 2.35),
(2000.0, 1.0, 11043.044, 7501.3, 1887.836, 685.216, 8053.4,
170.9, 1113.5, 5.63, 4.0, 281.653, 3.76, 1.87),
(2000.0, 2.0, 11258.454, 7571.8, 2018.529, 712.641, 8135.9,
172.7, 1103.0, 5.81, 3.9, 282.385, 4.19, 1.62),
(2000.0, 3.0, 11267.867, 7645.9, 1986.956, 698.827, 8222.3,
173.9, 1098.7, 6.07, 4.0, 283.19, 2.77, 3.3),
(2000.0, 4.0, 11334.544, 7713.5, 1987.845, 695.597, 8234.6,
175.6, 1097.7, 5.7, 3.9, 283.9, 3.89, 1.81),
(2001.0, 1.0, 11297.171, 7744.3, 1882.691, 710.403, 8296.5,
176.4, 1114.9, 4.39, 4.2, 284.55, 1.82, 2.57),
(2001.0, 2.0, 11371.251, 7773.5, 1876.65, 725.623, 8273.7,
177.4, 1139.7, 3.54, 4.4, 285.267, 2.26, 1.28),
(2001.0, 3.0, 11340.075, 7807.7, 1837.074, 730.493, 8484.5,
177.6, 1166.0, 2.72, 4.8, 286.047, 0.45, 2.27),
(2001.0, 4.0, 11380.128, 7930.0, 1731.189, 739.318, 8385.5,
177.7, 1190.9, 1.74, 5.5, 286.728, 0.23, 1.51),
(2002.0, 1.0, 11477.868, 7957.3, 1789.327, 756.915, 8611.6,
179.3, 1185.9, 1.75, 5.7, 287.328, 3.59, -1.84),
(2002.0, 2.0, 11538.77, 7997.8, 1810.779, 774.408, 8658.9,
180.0, 1199.5, 1.7, 5.8, 288.028, 1.56, 0.14),
(2002.0, 3.0, 11596.43, 8052.0, 1814.531, 786.673, 8629.2,
181.2, 1204.0, 1.61, 5.7, 288.783, 2.66, -1.05),
(2002.0, 4.0, 11598.824, 8080.6, 1813.219, 799.967, 8649.6,
182.6, 1226.8, 1.2, 5.8, 289.421, 3.08, -1.88),
(2003.0, 1.0, 11645.819, 8122.3, 1813.141, 800.196, 8681.3,
183.2, 1248.4, 1.14, 5.9, 290.019, 1.31, -0.17),
(2003.0, 2.0, 11738.706, 8197.8, 1823.698, 838.775, 8812.5,
183.7, 1287.9, 0.96, 6.2, 290.704, 1.09, -0.13),
(2003.0, 3.0, 11935.461, 8312.1, 1889.883, 839.598, 8935.4,
184.9, 1297.3, 0.94, 6.1, 291.449, 2.6, -1.67),
(2003.0, 4.0, 12042.817, 8358.0, 1959.783, 845.722, 8986.4,
186.3, 1306.1, 0.9, 5.8, 292.057, 3.02, -2.11),
(2004.0, 1.0, 12127.623, 8437.6, 1970.015, 856.57, 9025.9,
187.4, 1332.1, 0.94, 5.7, 292.635, 2.35, -1.42),
(2004.0, 2.0, 12213.818, 8483.2, 2055.58, 861.44, 9115.0,
189.1, 1340.5, 1.21, 5.6, 293.31, 3.61, -2.41),
(2004.0, 3.0, 12303.533, 8555.8, 2082.231, 876.385, 9175.9,
190.8, 1361.0, 1.63, 5.4, 294.066, 3.58, -1.95),
(2004.0, 4.0, 12410.282, 8654.2, 2125.152, 865.596, 9303.4,
191.8, 1366.6, 2.2, 5.4, 294.741, 2.09, 0.11),
(2005.0, 1.0, 12534.113, 8719.0, 2170.299, 869.204, 9189.6,
193.8, 1357.8, 2.69, 5.3, 295.308, 4.15, -1.46),
(2005.0, 2.0, 12587.535, 8802.9, 2131.468, 870.044, 9253.0,
194.7, 1366.6, 3.01, 5.1, 295.994, 1.85, 1.16),
(2005.0, 3.0, 12683.153, 8865.6, 2154.949, 890.394, 9308.0,
199.2, 1375.0, 3.52, 5.0, 296.77, 9.14, -5.62),
(2005.0, 4.0, 12748.699, 8888.5, 2232.193, 875.557, 9358.7,
199.4, 1380.6, 4.0, 4.9, 297.435, 0.4, 3.6),
(2006.0, 1.0, 12915.938, 8986.6, 2264.721, 900.511, 9533.8,
200.7, 1380.5, 4.51, 4.7, 298.061, 2.6, 1.91),
(2006.0, 2.0, 12962.462, 9035.0, 2261.247, 892.839, 9617.3,
202.7, 1369.2, 4.82, 4.7, 298.766, 3.97, 0.85),
(2006.0, 3.0, 12965.916, 9090.7, 2229.636, 892.002, 9662.5,
201.9, 1369.4, 4.9, 4.7, 299.593, -1.58, 6.48),
(2006.0, 4.0, 13060.679, 9181.6, 2165.966, 894.404, 9788.8,
203.574, 1373.6, 4.92, 4.4, 300.32, 3.3, 1.62),
(2007.0, 1.0, 13099.901, 9265.1, 2132.609, 882.766, 9830.2,
205.92, 1379.7, 4.95, 4.5, 300.977, 4.58, 0.36),
(2007.0, 2.0, 13203.977, 9291.5, 2162.214, 898.713, 9842.7,
207.338, 1370.0, 4.72, 4.5, 301.714, 2.75, 1.97),
(2007.0, 3.0, 13321.109, 9335.6, 2166.491, 918.983, 9883.9,
209.133, 1379.2, 4.0, 4.7, 302.509, 3.45, 0.55),
(2007.0, 4.0, 13391.249, 9363.6, 2123.426, 925.11, 9886.2,
212.495, 1377.4, 3.01, 4.8, 303.204, 6.38, -3.37),
(2008.0, 1.0, 13366.865, 9349.6, 2082.886, 943.372, 9826.8,
213.997, 1384.0, 1.56, 4.9, 303.803, 2.82, -1.26),
(2008.0, 2.0, 13415.266, 9351.0, 2026.518, 961.28, 10059.0,
218.61, 1409.3, 1.74, 5.4, 304.483, 8.53, -6.79),
(2008.0, 3.0, 13324.6, 9267.7, 1990.693, 991.551, 9838.3,
216.889, 1474.7, 1.17, 6.0, 305.27, -3.16, 4.33),
(2008.0, 4.0, 13141.92, 9195.3, 1857.661, 1007.273, 9920.4,
212.174, 1576.5, 0.12, 6.9, 305.952, -8.79, 8.91),
(2009.0, 1.0, 12925.41, 9209.2, 1558.494, 996.287, 9926.4,
212.671, 1592.8, 0.22, 8.1, 306.547, 0.94, -0.71),
(2009.0, 2.0, 12901.504, 9189.0, 1456.678, 1023.528, 10077.5,
214.469, 1653.6, 0.18, 9.2, 307.226, 3.37, -3.19),
(2009.0, 3.0, 12990.341, 9256.0, 1486.398, 1044.088, 10040.6,
216.385, 1673.9, 0.12, 9.6, 308.013, 3.56, -3.44)],
dtype=[('year', 'i4'), ('quarter', 'i2'), ('realgdp', 'f4'),
('realcons', 'f4'), ('realinv', 'f4'), ('realgovt', 'f4'),
('realdpi', 'f4'), ('cpi', 'f4'), ('m1', 'f4'), ('tbilrate', 'f4'),
('unemp', 'f4'), ('pop', 'f4'), ('infl', 'f4'), ('realint', 'f4')])

View File

@ -0,0 +1,75 @@
from statsmodels.compat.python import lrange
from io import BytesIO
import os
import pathlib
import tempfile
from numpy.testing import assert_equal
from statsmodels.iolib.smpickle import load_pickle, save_pickle
def test_pickle():
tmpdir = tempfile.mkdtemp(prefix="pickle")
a = lrange(10)
# test with str
path_str = tmpdir + "/res.pkl"
save_pickle(a, path_str)
b = load_pickle(path_str)
assert_equal(a, b)
# test with pathlib
path_pathlib = pathlib.Path(tmpdir) / "res2.pkl"
save_pickle(a, path_pathlib)
c = load_pickle(path_pathlib)
assert_equal(a, c)
# cleanup, tested on Windows
try:
os.remove(path_str)
os.remove(path_pathlib)
os.rmdir(tmpdir)
except OSError:
pass
assert not os.path.exists(tmpdir)
# test with file handle
fh = BytesIO()
save_pickle(a, fh)
fh.seek(0, 0)
d = load_pickle(fh)
fh.close()
assert_equal(a, d)
def test_pickle_supports_open():
tmpdir = tempfile.mkdtemp(prefix="pickle")
a = lrange(10)
class SubPath:
def __init__(self, path):
self._path = pathlib.Path(path)
def open(
self,
mode="r",
buffering=-1,
encoding=None,
errors=None,
newline=None,
):
return self._path.open(
mode=mode,
buffering=buffering,
encoding=encoding,
errors=errors,
newline=newline,
)
# test with pathlib
path_pathlib = SubPath(tmpdir + os.pathsep + "res2.pkl")
save_pickle(a, path_pathlib)
c = load_pickle(path_pathlib)
assert_equal(a, c)

View File

@ -0,0 +1,119 @@
'''examples to check summary, not converted to tests yet
'''
import numpy as np # noqa: F401
import pytest
from numpy.testing import assert_equal
from statsmodels.datasets import macrodata
from statsmodels.tools.tools import add_constant
from statsmodels.regression.linear_model import OLS
def test_escaped_variable_name():
# Rename 'cpi' column to 'CPI_'
data = macrodata.load().data
data.rename(columns={'cpi': 'CPI_'}, inplace=True)
mod = OLS.from_formula('CPI_ ~ 1 + np.log(realgdp)', data=data)
res = mod.fit()
assert 'CPI\\_' in res.summary().as_latex()
assert 'CPI_' in res.summary().as_text()
def test_wrong_len_xname(reset_randomstate):
y = np.random.randn(100)
x = np.random.randn(100, 2)
res = OLS(y, x).fit()
with pytest.raises(ValueError):
res.summary(xname=['x1'])
with pytest.raises(ValueError):
res.summary(xname=['x1', 'x2', 'x3'])
class TestSummaryLatex:
def test__repr_latex_(self):
desired = r'''
\begin{center}
\begin{tabular}{lcccccc}
\toprule
& \textbf{coef} & \textbf{std err} & \textbf{t} & \textbf{P$> |$t$|$} & \textbf{[0.025} & \textbf{0.975]} \\
\midrule
\textbf{const} & 7.2248 & 0.866 & 8.346 & 0.000 & 5.406 & 9.044 \\
\textbf{x1} & -0.6609 & 0.177 & -3.736 & 0.002 & -1.033 & -0.289 \\
\bottomrule
\end{tabular}
\end{center}
'''
x = [1, 5, 7, 3, 5, 5, 8, 3, 3, 4, 6, 4, 2, 7, 4, 2, 1, 9, 2, 6]
x = add_constant(x)
y = [6, 4, 2, 7, 4, 2, 1, 9, 2, 6, 1, 5, 7, 3, 5, 5, 8, 3, 3, 4]
reg = OLS(y, x).fit()
actual = reg.summary().tables[1]._repr_latex_()
actual = '\n%s\n' % actual
assert_equal(actual, desired)
if __name__ == '__main__':
from statsmodels.regression.tests.test_regression import TestOLS
#def mytest():
aregression = TestOLS()
TestOLS.setup_class()
results = aregression.res1
r_summary = str(results.summary_old())
print(r_summary)
olsres = results
print('\n\n')
r_summary = str(results.summary())
print(r_summary)
print('\n\n')
from statsmodels.discrete.tests.test_discrete import TestProbitNewton
aregression = TestProbitNewton()
TestProbitNewton.setup_class()
results = aregression.res1
r_summary = str(results.summary())
print(r_summary)
print('\n\n')
probres = results
from statsmodels.robust.tests.test_rlm import TestHampel
aregression = TestHampel()
#TestHampel.setup_class()
results = aregression.res1
r_summary = str(results.summary())
print(r_summary)
rlmres = results
print('\n\n')
from statsmodels.genmod.tests.test_glm import TestGlmBinomial
aregression = TestGlmBinomial()
#TestGlmBinomial.setup_class()
results = aregression.res1
r_summary = str(results.summary())
print(r_summary)
#print(results.summary2(return_fmt='latex'))
#print(results.summary2(return_fmt='csv'))
smry = olsres.summary()
print(smry.as_csv())
# import matplotlib.pyplot as plt
# plt.plot(rlmres.model.endog,'o')
# plt.plot(rlmres.fittedvalues,'-')
#
# plt.show()

View File

@ -0,0 +1,210 @@
import warnings
import numpy as np
import pandas as pd
import pytest
from numpy.testing import assert_equal
from statsmodels.iolib.summary2 import summary_col
from statsmodels.tools.tools import add_constant
from statsmodels.regression.linear_model import OLS
class TestSummaryLatex:
def test_summarycol(self):
# Test for latex output of summary_col object
desired = r'''
\begin{table}
\caption{}
\label{}
\begin{center}
\begin{tabular}{lll}
\hline
& y I & y II \\
\hline
const & 7.7500 & 12.4231 \\
& (1.1058) & (3.1872) \\
x1 & -0.7500 & -1.5769 \\
& (0.2368) & (0.6826) \\
R-squared & 0.7697 & 0.6401 \\
R-squared Adj. & 0.6930 & 0.5202 \\
\hline
\end{tabular}
\end{center}
\end{table}
\bigskip
Standard errors in parentheses.
'''
x = [1, 5, 7, 3, 5]
x = add_constant(x)
y1 = [6, 4, 2, 7, 4]
y2 = [8, 5, 0, 12, 4]
reg1 = OLS(y1, x).fit()
reg2 = OLS(y2, x).fit()
actual = summary_col([reg1, reg2]).as_latex()
actual = '\n%s\n' % actual
assert_equal(desired, actual)
def test_summarycol_float_format(self):
# Test for latex output of summary_col object
desired = r"""
==========================
y I y II
--------------------------
const 7.7 12.4
(1.1) (3.2)
x1 -0.7 -1.6
(0.2) (0.7)
R-squared 0.8 0.6
R-squared Adj. 0.7 0.5
==========================
Standard errors in
parentheses.
""" # noqa:W291
x = [1, 5, 7, 3, 5]
x = add_constant(x)
y1 = [6, 4, 2, 7, 4]
y2 = [8, 5, 0, 12, 4]
reg1 = OLS(y1, x).fit()
reg2 = OLS(y2, x).fit()
actual = summary_col([reg1, reg2], float_format='%0.1f').as_text()
actual = '%s\n' % actual
starred = summary_col([reg1, reg2], stars=True, float_format='%0.1f')
assert "7.7***" in str(starred)
assert "12.4**" in str(starred)
assert "12.4***" not in str(starred)
assert_equal(actual, desired)
def test_summarycol_drop_omitted(self):
# gh-3702
x = [1, 5, 7, 3, 5]
x = add_constant(x)
x2 = np.concatenate([x, np.array([[3], [9], [-1], [4], [0]])], 1)
y1 = [6, 4, 2, 7, 4]
y2 = [8, 5, 0, 12, 4]
reg1 = OLS(y1, x).fit()
reg2 = OLS(y2, x2).fit()
actual = summary_col([reg1, reg2], regressor_order=['const', 'x1'],
drop_omitted=True)
assert 'x2' not in str(actual)
actual = summary_col([reg1, reg2], regressor_order=['x1'],
drop_omitted=False)
assert 'const' in str(actual)
assert 'x2' in str(actual)
def test_summary_col_ordering_preserved(self):
# gh-3767
x = [1, 5, 7, 3, 5]
x = add_constant(x)
x2 = np.concatenate([x, np.array([[3], [9], [-1], [4], [0]])], 1)
x2 = pd.DataFrame(x2, columns=['const', 'b', 'a'])
y1 = [6, 4, 2, 7, 4]
y2 = [8, 5, 0, 12, 4]
reg1 = OLS(y1, x2).fit()
reg2 = OLS(y2, x2).fit()
info_dict = {'R2': lambda x: f'{int(x.rsquared):.3f}',
'N': lambda x: f'{int(x.nobs):d}'}
original = actual = summary_col([reg1, reg2], float_format='%0.4f')
actual = summary_col([reg1, reg2], regressor_order=['a', 'b'],
float_format='%0.4f',
info_dict=info_dict)
variables = ('const', 'b', 'a')
for line in str(original).split('\n'):
for variable in variables:
if line.startswith(variable):
assert line in str(actual)
def test__repr_latex_(self):
desired = r'''
\begin{table}
\caption{}
\label{}
\begin{center}
\begin{tabular}{lll}
\hline
& y I & y II \\
\hline
const & 7.7500 & 12.4231 \\
& (1.1058) & (3.1872) \\
x1 & -0.7500 & -1.5769 \\
& (0.2368) & (0.6826) \\
R-squared & 0.7697 & 0.6401 \\
R-squared Adj. & 0.6930 & 0.5202 \\
\hline
\end{tabular}
\end{center}
\end{table}
\bigskip
Standard errors in parentheses.
'''
x = [1, 5, 7, 3, 5]
x = add_constant(x)
y1 = [6, 4, 2, 7, 4]
y2 = [8, 5, 0, 12, 4]
reg1 = OLS(y1, x).fit()
reg2 = OLS(y2, x).fit()
actual = summary_col([reg1, reg2])._repr_latex_()
actual = '\n%s\n' % actual
assert_equal(actual, desired)
def test_OLSsummary(self):
# Test that latex output of regular OLS output still contains
# multiple tables
x = [1, 5, 7, 3, 5]
x = add_constant(x)
y1 = [6, 4, 2, 7, 4]
reg1 = OLS(y1, x).fit()
with warnings.catch_warnings():
warnings.simplefilter("ignore")
actual = reg1.summary().as_latex()
string_to_find = r'''\end{tabular}
\begin{tabular}'''
result = string_to_find in actual
assert (result is True)
def test_ols_summary_rsquared_label():
# Check that the "uncentered" label is correctly added after rsquared
x = [1, 5, 7, 3, 5, 2, 5, 3]
y = [6, 4, 2, 7, 4, 9, 10, 2]
reg_with_constant = OLS(y, add_constant(x)).fit()
r2_str = 'R-squared:'
with pytest.warns(UserWarning):
assert r2_str in str(reg_with_constant.summary2())
with pytest.warns(UserWarning):
assert r2_str in str(reg_with_constant.summary())
reg_without_constant = OLS(y, x, hasconst=False).fit()
r2_str = 'R-squared (uncentered):'
with pytest.warns(UserWarning):
assert r2_str in str(reg_without_constant.summary2())
with pytest.warns(UserWarning):
assert r2_str in str(reg_without_constant.summary())
class TestSummaryLabels:
"""
Test that the labels are correctly set in the summary table"""
@classmethod
def setup_class(cls):
y = [1, 1, 4, 2] * 4
x = add_constant([1, 2, 3, 4] * 4)
cls.mod = OLS(endog=y, exog=x).fit()
def test_summary_col_r2(self,):
# GH 6578
table = summary_col(results=self.mod, include_r2=True)
assert "R-squared " in str(table)
assert "R-squared Adj." in str(table)
def test_absence_of_r2(self,):
table = summary_col(results=self.mod, include_r2=False)
assert "R-squared" not in str(table)
assert "R-squared Adj." not in str(table)

View File

@ -0,0 +1,78 @@
import warnings
import pytest
@pytest.mark.xfail(strict=True)
def test_regression_summary():
#little luck getting this test to pass (It should?), can be used for
#visual testing of the regression.summary table
#fixed, might fail at minute changes
from statsmodels.regression.tests.test_regression import TestOLS
#from test_regression import TestOLS
import time
from string import Template
t = time.localtime()
desired = Template(
''' Summary of Regression Results
=======================================
| Dependent Variable: y|
| Model: OLS|
| Method: Least Squares|
| Date: $XXcurrentXdateXX|
| Time: $XXtimeXXX|
| # obs: 16.0|
| Df residuals: 9.0|
| Df model: 6.0|
==============================================================================
| coefficient std. error t-statistic prob. |
------------------------------------------------------------------------------
| x1 15.06 84.91 0.1774 0.8631 |
| x2 -0.03582 0.03349 -1.0695 0.3127 |
| x3 -2.020 0.4884 -4.1364 0.0025 |
| x4 -1.033 0.2143 -4.8220 0.0009 |
| x5 -0.05110 0.2261 -0.2261 0.8262 |
| x6 1829. 455.5 4.0159 0.0030 |
| const -3.482e+06 8.904e+05 -3.9108 0.0036 |
==============================================================================
| Models stats Residual stats |
------------------------------------------------------------------------------
| R-squared: 0.9955 Durbin-Watson: 2.559 |
| Adjusted R-squared: 0.9925 Omnibus: 0.7486 |
| F-statistic: 330.3 Prob(Omnibus): 0.6878 |
| Prob (F-statistic): 4.984e-10 JB: 0.6841 |
| Log likelihood: -109.6 Prob(JB): 0.7103 |
| AIC criterion: 233.2 Skew: 0.4200 |
| BIC criterion: 238.6 Kurtosis: 2.434 |
------------------------------------------------------------------------------'''
).substitute(XXcurrentXdateXX = str(time.strftime("%a, %d %b %Y",t)),
XXtimeXXX = str(time.strftime("%H:%M:%S",t)))
desired = str(desired)
aregression = TestOLS()
TestOLS.setup_class()
results = aregression.res1
# be quiet!
original_filters = warnings.filters[:] # copy original
warnings.simplefilter("ignore")
try:
r_summary = str(results.summary_old())
finally:
warnings.filters = original_filters # restore filters
## print('###')
## print(r_summary)
## print('###')
## print(desired)
## print('###')
actual = r_summary
import numpy as np
actual = '\n'.join(line.rstrip() for line in actual.split('\n'))
# print len(actual), len(desired)
# print repr(actual)
# print repr(desired)
# counter = 0
# for c1,c2 in zip(actual, desired):
# if not c1==c2 and counter<20:
# print c1,c2
# counter += 1
np.testing.assert_(actual == desired)

View File

@ -0,0 +1,263 @@
from numpy.testing import assert_equal
from statsmodels.iolib.table import SimpleTable, default_txt_fmt
from statsmodels.iolib.table import default_latex_fmt
from statsmodels.iolib.table import default_html_fmt
import pandas
from statsmodels.regression.linear_model import OLS
ltx_fmt1 = default_latex_fmt.copy()
html_fmt1 = default_html_fmt.copy()
class TestSimpleTable:
def test_simple_table_1(self):
# Basic test, test_simple_table_1
desired = '''
=====================
header1 header2
---------------------
stub1 1.30312 2.73999
stub2 1.95038 2.65765
---------------------
'''
test1data = [[1.30312, 2.73999],[1.95038, 2.65765]]
test1stubs = ('stub1', 'stub2')
test1header = ('header1', 'header2')
actual = SimpleTable(test1data, test1header, test1stubs,
txt_fmt=default_txt_fmt)
actual = '\n%s\n' % actual.as_text()
assert_equal(desired, str(actual))
def test_simple_table_2(self):
# Test SimpleTable.extend_right()
desired = '''
=============================================================
header s1 header d1 header s2 header d2
-------------------------------------------------------------
stub R1 C1 10.30312 10.73999 stub R1 C2 50.95038 50.65765
stub R2 C1 90.30312 90.73999 stub R2 C2 40.95038 40.65765
-------------------------------------------------------------
'''
data1 = [[10.30312, 10.73999], [90.30312, 90.73999]]
data2 = [[50.95038, 50.65765], [40.95038, 40.65765]]
stubs1 = ['stub R1 C1', 'stub R2 C1']
stubs2 = ['stub R1 C2', 'stub R2 C2']
header1 = ['header s1', 'header d1']
header2 = ['header s2', 'header d2']
actual1 = SimpleTable(data1, header1, stubs1, txt_fmt=default_txt_fmt)
actual2 = SimpleTable(data2, header2, stubs2, txt_fmt=default_txt_fmt)
actual1.extend_right(actual2)
actual = '\n%s\n' % actual1.as_text()
assert_equal(desired, str(actual))
def test_simple_table_3(self):
# Test SimpleTable.extend() as in extend down
desired = '''
==============================
header s1 header d1
------------------------------
stub R1 C1 10.30312 10.73999
stub R2 C1 90.30312 90.73999
header s2 header d2
------------------------------
stub R1 C2 50.95038 50.65765
stub R2 C2 40.95038 40.65765
------------------------------
'''
data1 = [[10.30312, 10.73999], [90.30312, 90.73999]]
data2 = [[50.95038, 50.65765], [40.95038, 40.65765]]
stubs1 = ['stub R1 C1', 'stub R2 C1']
stubs2 = ['stub R1 C2', 'stub R2 C2']
header1 = ['header s1', 'header d1']
header2 = ['header s2', 'header d2']
actual1 = SimpleTable(data1, header1, stubs1, txt_fmt=default_txt_fmt)
actual2 = SimpleTable(data2, header2, stubs2, txt_fmt=default_txt_fmt)
actual1.extend(actual2)
actual = '\n%s\n' % actual1.as_text()
assert_equal(desired, str(actual))
def test_simple_table_4(self):
# Basic test, test_simple_table_4 test uses custom txt_fmt
txt_fmt1 = dict(data_fmts = ['%3.2f', '%d'],
empty_cell = ' ',
colwidths = 1,
colsep=' * ',
row_pre = '* ',
row_post = ' *',
table_dec_above='*',
table_dec_below='*',
header_dec_below='*',
header_fmt = '%s',
stub_fmt = '%s',
title_align='r',
header_align = 'r',
data_aligns = "r",
stubs_align = "l",
fmt = 'txt'
)
ltx_fmt1 = default_latex_fmt.copy()
html_fmt1 = default_html_fmt.copy()
cell0data = 0.0000
cell1data = 1
row0data = [cell0data, cell1data]
row1data = [2, 3.333]
table1data = [ row0data, row1data ]
test1stubs = ('stub1', 'stub2')
test1header = ('header1', 'header2')
tbl = SimpleTable(table1data, test1header, test1stubs,txt_fmt=txt_fmt1,
ltx_fmt=ltx_fmt1, html_fmt=html_fmt1)
def test_txt_fmt1(self):
# Limited test of custom txt_fmt
desired = """
*****************************
* * header1 * header2 *
*****************************
* stub1 * 0.00 * 1 *
* stub2 * 2.00 * 3 *
*****************************
"""
actual = '\n%s\n' % tbl.as_text()
#print(actual)
#print(desired)
assert_equal(actual, desired)
def test_ltx_fmt1(self):
# Limited test of custom ltx_fmt
desired = r"""
\begin{tabular}{lcc}
\toprule
& \textbf{header1} & \textbf{header2} \\
\midrule
\textbf{stub1} & 0.0 & 1 \\
\textbf{stub2} & 2 & 3.333 \\
\bottomrule
\end{tabular}
"""
actual = '\n%s\n' % tbl.as_latex_tabular(center=False)
#print(actual)
#print(desired)
assert_equal(actual, desired)
# Test "center=True" (the default):
desired_centered = r"""
\begin{center}
%s
\end{center}
""" % desired[1:-1]
actual_centered = '\n%s\n' % tbl.as_latex_tabular()
assert_equal(actual_centered, desired_centered)
def test_html_fmt1(self):
# Limited test of custom html_fmt
desired = """
<table class="simpletable">
<tr>
<td></td> <th>header1</th> <th>header2</th>
</tr>
<tr>
<th>stub1</th> <td>0.0</td> <td>1</td>
</tr>
<tr>
<th>stub2</th> <td>2</td> <td>3.333</td>
</tr>
</table>
""" # noqa:W291
actual = '\n%s\n' % tbl.as_html()
assert_equal(actual, desired)
test_txt_fmt1(self)
test_ltx_fmt1(self)
test_html_fmt1(self)
def test_simple_table_special_chars(self):
# Simple table with characters: (%, >, |, _, $, &, #)
cell0c_data = 22
cell1c_data = 1053
row0c_data = [cell0c_data, cell1c_data]
row1c_data = [23, 6250.4]
table1c_data = [ row0c_data, row1c_data ]
test1c_stubs = ('>stub1%', 'stub_2')
test1c_header = ('#header1$', 'header&|')
tbl_c = SimpleTable(table1c_data, test1c_header, test1c_stubs, ltx_fmt=ltx_fmt1)
def test_ltx_special_chars(self):
# Test for special characters (latex) in headers and stubs
desired = r"""
\begin{tabular}{lcc}
\toprule
& \textbf{\#header1\$} & \textbf{header\&$|$} \\
\midrule
\textbf{$>$stub1\%} & 22 & 1053 \\
\textbf{stub\_2} & 23 & 6250.4 \\
\bottomrule
\end{tabular}
"""
actual = '\n%s\n' % tbl_c.as_latex_tabular(center=False)
assert_equal(actual, desired)
test_ltx_special_chars(self)
def test_regression_with_tuples(self):
i = pandas.Series([1, 2, 3, 4] * 10, name="i")
y = pandas.Series([1, 2, 3, 4, 5] * 8, name="y")
x = pandas.Series([1, 2, 3, 4, 5, 6, 7, 8] * 5, name="x")
df = pandas.DataFrame(index=i.index)
df = df.join(i)
endo = df.join(y)
exo = df.join(x)
endo_groups = endo.groupby("i")
exo_groups = exo.groupby("i")
exo_df = exo_groups.agg(['sum', 'max'])
endo_df = endo_groups.agg(['sum', 'max'])
reg = OLS(exo_df[[("x", "sum")]], endo_df).fit()
interesting_lines = []
import warnings
with warnings.catch_warnings():
# Catch ominormal warning, not interesting here
warnings.simplefilter("ignore")
for line in str(reg.summary()).splitlines():
if "_" in line:
interesting_lines.append(line[:38])
desired = ["Dep. Variable: x_sum ",
"y_sum 1.4595 0.209 ",
"y_max 0.2432 0.035 "]
assert_equal(sorted(desired), sorted(interesting_lines))
def test_default_alignment(self):
desired = '''
=====================
header1 header2
---------------------
stub1 1.30312 2.73
stub2 1.95038 2.6
---------------------
'''
test1data = [[1.30312, 2.73], [1.95038, 2.6]]
test1stubs = ('stub1', 'stub2')
test1header = ('header1', 'header2')
actual = SimpleTable(test1data, test1header, test1stubs,
txt_fmt=default_txt_fmt)
actual = '\n%s\n' % actual.as_text()
assert_equal(desired, str(actual))
def test__repr_latex(self):
desired = r"""
\begin{center}
\begin{tabular}{lcc}
\toprule
& \textbf{header1} & \textbf{header2} \\
\midrule
\textbf{stub1} & 5.394 & 29.3 \\
\textbf{stub2} & 343 & 34.2 \\
\bottomrule
\end{tabular}
\end{center}
"""
testdata = [[5.394, 29.3], [343, 34.2]]
teststubs = ('stub1', 'stub2')
testheader = ('header1', 'header2')
tbl = SimpleTable(testdata, testheader, teststubs,
txt_fmt=default_txt_fmt)
actual = '\n%s\n' % tbl._repr_latex_()
assert_equal(actual, desired)

View File

@ -0,0 +1,139 @@
'''
Unit tests table.py.
:see: http://docs.python.org/lib/minimal-example.html for an intro to unittest
:see: http://agiletesting.blogspot.com/2005/01/python-unit-testing-part-1-unittest.html
:see: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305292
'''
import numpy as np
from numpy.testing import assert_equal
__docformat__ = "restructuredtext en"
from statsmodels.iolib.table import Cell, SimpleTable
from statsmodels.iolib.table import default_latex_fmt
from statsmodels.iolib.table import default_html_fmt
ltx_fmt1 = default_latex_fmt.copy()
html_fmt1 = default_html_fmt.copy()
txt_fmt1 = dict(
data_fmts = ['%0.2f', '%d'],
empty_cell = ' ',
colwidths = 1,
colsep=' * ',
row_pre = '* ',
row_post = ' *',
table_dec_above='*',
table_dec_below='*',
header_dec_below='*',
header_fmt = '%s',
stub_fmt = '%s',
title_align='r',
header_align = 'r',
data_aligns = "r",
stubs_align = "l",
fmt = 'txt'
)
cell0data = 0.0000
cell1data = 1
row0data = [cell0data, cell1data]
row1data = [2, 3.333]
table1data = [ row0data, row1data ]
test1stubs = ('stub1', 'stub2')
test1header = ('header1', 'header2')
#test1header = ('header1\nheader1a', 'header2\nheader2a')
tbl = SimpleTable(table1data, test1header, test1stubs,
txt_fmt=txt_fmt1, ltx_fmt=ltx_fmt1, html_fmt=html_fmt1)
def custom_labeller(cell):
if cell.data is np.nan:
return 'missing'
class TestCell:
def test_celldata(self):
celldata = cell0data, cell1data, row1data[0], row1data[1]
cells = [Cell(datum, datatype=i % 2)
for i, datum in enumerate(celldata)]
for cell, datum in zip(cells, celldata):
assert_equal(cell.data, datum)
class TestSimpleTable:
def test_txt_fmt1(self):
# Limited test of custom txt_fmt
desired = """
*****************************
* * header1 * header2 *
*****************************
* stub1 * 0.00 * 1 *
* stub2 * 2.00 * 3 *
*****************************
"""
actual = '\n%s\n' % tbl.as_text()
#print('actual')
#print(actual)
#print('desired')
#print(desired)
assert_equal(actual, desired)
def test_ltx_fmt1(self):
# Limited test of custom ltx_fmt
desired = r"""
\begin{center}
\begin{tabular}{lcc}
\toprule
& \textbf{header1} & \textbf{header2} \\
\midrule
\textbf{stub1} & 0.0 & 1 \\
\textbf{stub2} & 2 & 3.333 \\
\bottomrule
\end{tabular}
\end{center}
"""
actual = '\n%s\n' % tbl.as_latex_tabular()
#print(actual)
#print(desired)
assert_equal(actual, desired)
def test_html_fmt1(self):
# Limited test of custom html_fmt
desired = """
<table class="simpletable">
<tr>
<td></td> <th>header1</th> <th>header2</th>
</tr>
<tr>
<th>stub1</th> <td>0.0</td> <td>1</td>
</tr>
<tr>
<th>stub2</th> <td>2</td> <td>3.333</td>
</tr>
</table>
"""
#the previous has significant trailing whitespace that got removed
#desired = '''\n<table class="simpletable">\n<tr>\n <td></td> <th>header1</th> <th>header2</th>\n</tr>\n<tr>\n <th>stub1</th> <td>0.0</td> <td>1</td> \n</tr>\n<tr>\n <th>stub2</th> <td>2</td> <td>3.333</td> \n</tr>\n</table>\n'''
actual = '\n%s\n' % tbl.as_html()
actual = '\n'.join(line.rstrip() for line in actual.split('\n'))
#print(actual)
#print(desired)
#print len(actual), len(desired)
assert_equal(actual, desired)
def test_customlabel(self):
# Limited test of custom custom labeling
tbl = SimpleTable(table1data, test1header, test1stubs, txt_fmt=txt_fmt1)
tbl[1][1].data = np.nan
tbl.label_cells(custom_labeller)
#print([[c.datatype for c in row] for row in tbl])
desired = """
*****************************
* * header1 * header2 *
*****************************
* stub1 * -- * 1 *
* stub2 * 2.00 * 3 *
*****************************
"""
actual = '\n%s\n' % tbl.as_text(missing='--')
assert_equal(actual, desired)