reconnect moved files to git repo
This commit is contained in:
@ -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()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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
|
||||
140
venv/lib/python3.11/site-packages/statsmodels/iolib/foreign.py
Normal file
140
venv/lib/python3.11/site-packages/statsmodels/iolib/foreign.py
Normal 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')
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
911
venv/lib/python3.11/site-packages/statsmodels/iolib/summary.py
Normal file
911
venv/lib/python3.11/site-packages/statsmodels/iolib/summary.py
Normal 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
|
||||
648
venv/lib/python3.11/site-packages/statsmodels/iolib/summary2.py
Normal file
648
venv/lib/python3.11/site-packages/statsmodels/iolib/summary2.py
Normal 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
|
||||
927
venv/lib/python3.11/site-packages/statsmodels/iolib/table.py
Normal file
927
venv/lib/python3.11/site-packages/statsmodels/iolib/table.py
Normal 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
|
||||
@ -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'}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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')])
|
||||
Binary file not shown.
@ -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)
|
||||
@ -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()
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
Reference in New Issue
Block a user