Skip to content
Browse files

deprecate old summary

  • Loading branch information...
1 parent 981bde6 commit 621aef5f9212c12b3fa677bcf7a19fb5ecc24266 @vincentarelbundock committed
View
2 statsmodels/discrete/discrete_margins.py
@@ -515,7 +515,7 @@ def summary(self, alpha=.05):
('Method:', [method]),
('At:', [self.margeff_options['at']]),]
- from statsmodels.iolib.summary import (Summary, summary_params,
+ from statsmodels.iolib.summary_old import (Summary, summary_params,
table_extend)
exog_names = model.exog_names[:] # copy
smry = Summary()
View
2 statsmodels/discrete/discrete_model.py
@@ -2088,7 +2088,7 @@ def summary(self, title=None, xname=None, yname=None, alpha=.05,
"""
# Summary
- from statsmodels.iolib.summary2 import Summary
+ from statsmodels.iolib.summary import Summary
smry = Summary()
smry.add_base(results=self, alpha=alpha, float_format=float_format,
xname=xname, yname=yname, title=title)
View
2 statsmodels/genmod/generalized_linear_model.py
@@ -651,7 +651,7 @@ def summary(self, title=None, xname=None, yname=None, alpha=.05,
"""
# Summary
self.method = 'IRLS'
- from statsmodels.iolib.summary2 import Summary
+ from statsmodels.iolib.summary import Summary
smry = Summary()
smry.add_base(results=self, alpha=alpha, float_format=float_format,
xname=xname, yname=yname, title=title)
View
1,257 statsmodels/iolib/summary.py
@@ -1,905 +1,456 @@
-
-
import numpy as np
-from statsmodels.compatnp.iter_compat import zip_longest
+import pandas as pd
+import datetime
+import copy
+import collections
from statsmodels.iolib.table import SimpleTable
-from statsmodels.iolib.tableformatting import (gen_fmt, fmt_2,
- fmt_params, fmt_base, fmt_2cols)
-#from statsmodels.iolib.summary2d import summary_params_2dflat
-#from summary2d import summary_params_2dflat
-
-def forg(x, prec=3):
- 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 NotImplementedError
-
-
-def summary(self, yname=None, xname=None, title=0, alpha=.05,
- returns='text', model_info=None):
- """
- Parameters
- -----------
- yname : string
- optional, Default is `Y`
- xname : list of strings
- optional, Default is `X.#` for # in p the number of regressors
- Confidance interval : (0,1) not implimented
- title : string
- optional, Defualt is 'Generalized linear model'
- returns : string
- 'text', 'table', 'csv', 'latex', 'html'
-
- Returns
- -------
- Defualt :
- 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.
- """
- import time as time
-
-
-
- #TODO Make sure all self.model.__class__.__name__ are listed
- model_types = {'OLS' : 'Ordinary least squares',
- 'GLS' : 'Generalized least squares',
- 'GLSAR' : 'Generalized least squares with AR(p)',
- 'WLS' : 'Weigthed least squares',
- 'RLM' : 'Robust linear model',
- 'GLM' : 'Generalized linear model'
- }
- model_methods = {'OLS' : 'Least Squares',
- 'GLS' : 'Least Squares',
- 'GLSAR' : 'Least Squares',
- 'WLS' : 'Least Squares',
- 'RLM' : '?',
- 'GLM' : '?'
- }
- if title==0:
- title = model_types[self.model.__class__.__name__]
- if yname is None:
- try:
- yname = self.model.endog_names
- except AttributeError:
- yname = 'y'
- if xname is None:
- try:
- xname = self.model.exog_names
- except AttributeError:
- xname = ['var_%d' % i for i in range(len(self.params))]
- 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__
- #dist_family = self.model.family.__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), #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_stubs_left = ('Model type:',
-## 'Date:',
-## 'Dependent Variable:',
-## 'df model'
-## )
-## gen_data_left = [[modeltype],
-## [date],
-## yname, #What happens with multiple names?
-## [df_model]
-## ]
- 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 = xrange(len(xname))
- tstat = tstats[modeltype]
- prob_stat = prob_stats[modeltype]
-
- # Simpletable should be able to handle the formating
- params_data = zip(["%#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, #gen_fmt,
- )
-
- #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 ols_to_csv():
- """
- exports ols summary data to csv
- """
- pass
- def glm_printer():
- table = str(general_table)+'\n'+str(parameter_table)
- return table
- pass
-
- 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:
- try:
- yname = self.model.endog_names
- except AttributeError:
- yname = 'y'
- if xname is None:
- try:
- xname = self.model.exog_names
- except AttributeError:
- 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
- import time
- 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
- #alternate spellings are commented out to force unique labels
- default_items = dict([
- ('Dependent Variable:', lambda: [yname]),
- ('Dep. Variable:', lambda: [yname]),
- ('Model:', lambda: [results.model.__class__.__name__]),
- #('Model type:', lambda: [results.model.__class__.__name__]),
- ('Date:', lambda: [date]),
- ('Time:', lambda: time_of_day),
- ('Number of Obs:', lambda: [results.nobs]),
- #('No. of Observations:', lambda: ["%#6d" % results.nobs]),
- ('No. Observations:', lambda: ["%#6d" % results.nobs]),
- #('Df model:', lambda: [results.df_model]),
- ('Df Model:', lambda: ["%#6d" % results.df_model]),
- #TODO: check when we have non-integer df
- ('Df Residuals:', lambda: ["%#6d" % results.df_resid]),
- #('Df resid:', lambda: [results.df_resid]),
- #('df resid:', lambda: [results.df_resid]), #check capitalization
- ('Log-Likelihood:', lambda: ["%#8.5g" % results.llf]) #doesn't exist for RLM - exception
- #('Method:', lambda: [???]), #no default for this
- ])
-
- 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
- gen_left.append(('Log-Likelihood', None))
- except: #AttributeError, NotImplementedError
- pass
-
- gen_right = []
-
-
- gen_title = title
- gen_header = None
-
- #needed_values = [k for k,v in gleft + gright if v is None] #not used anymore
- #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
- 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 doesn't 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 #gen_fmt
- )
- 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 #, gen_table_left, gen_table_right
-
-
-
-def summary_params(results, yname=None, xname=None, alpha=.05, use_t=True,
- skip_header=False):
- '''create a summary table for the parameters
-
- Parameters
- ----------
- res : results instance
- some required information is directly taken from the result
- instance
- yname : string or None
- optional name for the endogenous variable, default is "y"
- xname : list of strings or 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 don't 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
- alp = str((1-alpha)*100)+'%'
- if use_t:
- param_header = ['coef', 'std err', 't', 'P>|t|',
- '[' + alp + ' Conf. Int.]']
- else:
- param_header = ['coef', 'std err', 'z', 'P>|z|',
- '[' + alp + ' Conf. Int.]']
-
- if skip_header:
- param_header = None
-
-
- _, xname = _getnames(results, yname=yname, xname=xname)
-
- params_stubs = xname
-
- exog_idx = xrange(len(xname))
-
- #center confidence intervals if they are unequal lengths
-# confint = ["(%#6.3g, %#6.3g)" % tuple(conf_int[i]) for i in \
-# exog_idx]
- confint = ["%s %s" % tuple(map(forg, conf_int[i])) for i in \
- exog_idx]
- len_ci = map(len, confint)
- max_ci = max(len_ci)
- min_ci = min(len_ci)
-
- if min_ci < max_ci:
- confint = [ci.center(max_ci) for ci in confint]
-
- #explicit f/g formatting, now uses forg, f or g depending on values
-# params_data = zip(["%#6.4g" % (params[i]) for i in exog_idx],
-# ["%#6.4f" % (std_err[i]) for i in exog_idx],
-# ["%#6.3f" % (tvalues[i]) for i in exog_idx],
-# ["%#6.3f" % (pvalues[i]) for i in exog_idx],
-# confint
-## ["(%#6.3g, %#6.3g)" % tuple(conf_int[i]) for i in \
-## exog_idx]
-# )
-
- params_data = zip([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],
- confint
-# ["(%#6.3g, %#6.3g)" % tuple(conf_int[i]) for i in \
-# exog_idx]
- )
- parameter_table = SimpleTable(params_data,
- param_header,
- params_stubs,
- title = None,
- txt_fmt = fmt_params #gen_fmt #fmt_2, #gen_fmt,
- )
-
- return parameter_table
-
-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
-
- Parameter
- ---------
- result : result instance
- the result instance with params and attributes in extras
- extras : list of strings
- additional attributes to add below a parameter row, e.g. bse or tvalues
- endog_names : None or list of strings
- names for rows of the parameter array (multivariate endog)
- exog_names : None or list of strings
- 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 = [['%10.4f'%item for item in row] for row in result.params]
- res_params = [[forg(item, prec=4) for item in row] for row in result.params]
- if extras: #not None or non-empty
- #maybe this should be a simple triple loop instead of list comprehension?
- #below_list = [[['%10s' % ('('+('%10.3f'%v).strip()+')')
- extras_list = [[['%10s' % ('(' + forg(v, prec=3).strip() + ')')
- for v in col]
- for col in getattr(result, what)]
- for what in extras
- ]
- data = zip(res_params, *extras_list)
- data = [i for j in data for i in j] #flatten
- stubs = zip(endog_names, *[['']*len(endog_names)]*len(extras))
- stubs = [i for j in stubs for i in j] #flatten
- #return SimpleTable(data, headers=exog_names, stubs=stubs)
- else:
- data = res_params
- stubs = endog_names
-# return SimpleTable(data, headers=exog_names, stubs=stubs,
-# data_fmts=['%10.4f'])
-
- import copy
- txt_fmt = copy.deepcopy(fmt_params)
- txt_fmt.update(dict(data_fmts = ["%s"]*result.params.shape[1]))
- return SimpleTable(data, headers=exog_names,
- stubs=stubs,
- title=title,
-# data_fmts = ["%s"]),
- txt_fmt = txt_fmt)
-
-
-def summary_params_2dflat(result, endog_names=None, exog_names=None, alpha=0.95,
- use_t=True, keep_headers=True, endog_cols=False):
- #skip_headers2=True):
- '''summary table for parameters that are 2d, e.g. multi-equation models
-
- Parameter
- ---------
- result : result instance
- the result instance with params, bse, tvalues and conf_int
- endog_names : None or list of strings
- names for rows of the parameter array (multivariate endog)
- exog_names : None or list of strings
- 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 not len(endog_names) == params.shape[1]:
- raise ValueError('endog_names has wrong length')
- else:
- if not len(endog_names) == len(params):
- raise ValueError('endog_names has wrong length')
- n_equ = 1
-
- #VAR doesn't have conf_int
- #params = res.params.T # this is a convention for multi-eq models
-
- if not isinstance(endog_names, list):
- #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:]
-
- #check if we have the right length of names
-
- 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])
-
- #not used anymore in current version
-# if skip_headers2:
-# skiph = (row != 0)
-# else:
-# skiph = False
- skiph = False
- tble = summary_params(restup, yname=endog_names[eq],
- xname=exog_names, alpha=.05, 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.
-
- Parameter
- ---------
- 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'):
- ######## Return Summary Tables ########
- # 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(map(strdrop, tables[:-1]) + [str(tables[-1])])
- elif return_fmt == 'tables':
- return tables
- elif return_fmt == 'csv':
- return '\n'.join(map(lambda x: x.as_csv(), tables))
- elif return_fmt == 'latex':
- #TODO: insert \hline after updating SimpleTable
- import copy
- table = copy.deepcopy(tables[0])
- del table[-1]
- 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')
-
+import StringIO
+import textwrap
class Summary(object):
- '''class to hold tables for result summary presentation
-
- 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 : string
- extra lines that are added to the text output, used for warnings and
- explanations.
-
- '''
def __init__(self):
self.tables = []
- self.extra_txt = None
+ self.settings = []
+ self.extra_txt = []
+ self.title = None
def __str__(self):
return self.as_text()
def __repr__(self):
- #return '<' + str(type(self)) + '>\n"""\n' + self.__str__() + '\n"""'
return str(type(self)) + '\n"""\n' + self.__str__() + '\n"""'
def _repr_html_(self):
'''Display as HTML in IPython notebook.'''
return self.as_html()
- 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
+ def add_df(self, df, index=True, header=True, float_format='%.4f',
+ align='r'):
+ '''Add the contents of a DataFrame to summary table
Parameters
----------
- res : results instance
- some required information is directly taken from the result
- instance
- title : string or None
- if None, then a default title is used.
- ?how did I do no title?
- gleft : list of tuples
- elements for the left table, tuples are (name, value) pairs
- If gleft is None, then a default table is created
- gright : list of tuples or None
- elements for the right table, tuples are (name, value) pairs
- yname : string or None
- optional name for the endogenous variable, default is "y"
- xname : list of strings or None
- optional names for the exogenous variables, default is "var_xx"
-
- Returns
- -------
- None : tables are attached
+ df : DataFrame
+ header: bool
+ Reproduce the DataFrame column labels in summary table
+ index: bool
+ Reproduce the DataFrame row labels in summary table
+ float_format: string
+ Formatting to float data columns
+ align : string
+ Data alignment (l/c/r)
+ '''
+
+ settings = {'ncols':df.shape[1],
+ 'index':index, 'header':header,
+ 'float_format':float_format, 'align':align}
+ if index:
+ settings['ncols'] += 1
+ self.tables.append(df)
+ self.settings.append(settings)
+ def add_array(self, array, align='l', float_format="%.4f"):
+ '''Add the contents of a Numpy array to summary table
+
+ Parameters
+ ----------
+ array : numpy array (2D)
+ float_format: string
+ Formatting to array if type is float
+ align : string
+ Data alignment (l/c/r)
'''
- table = summary_top(res, title=title, gleft=gleft, gright=gright,
- yname=yname, xname=xname)
- self.tables.append(table)
+ table = pd.DataFrame(array)
+ self.add_df(table, index=False, header=False,
+ float_format=float_format, align=align)
- def add_table_params(self, res, yname=None, xname=None, alpha=.05,
- use_t=True):
- '''create and add a table for the parameter estimates
+ def add_dict(self, d, ncols=2, align='l'):
+ '''Add the contents of a Dict to summary table
Parameters
----------
- res : results instance
- some required information is directly taken from the result
- instance
- yname : string or None
- optional name for the endogenous variable, default is "y"
- xname : list of strings or 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)
+ 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 : string
+ Data alignment (l/c/r)
+ '''
- Returns
- -------
- None : table is attached
+ key = map(str, d.keys())
+ val = map(str, d.values())
+ data = np.array([key, val]).T
+ # Pad if necessary to fill columns
+ if data.shape[0] % ncols != 0:
+ pad = ncols - (data.shape[0] % ncols)
+ data = np.vstack([data, np.array(pad * [['','']])])
+ # Split and join as multi-columns
+ 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 indendented.
+ '''
+ 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 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, yname=yname, xname=xname,
-# alpha=alpha, use_t=use_t)
- _, table = summary_params_2dflat(res, endog_names=yname,
- exog_names=xname,
- alpha=alpha, use_t=use_t)
+ if type(title) == str:
+ self.title = title
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
+ try:
+ model = results.model.__class__.__name__
+ if model in _model_types:
+ model = _model_types[model]
+ self.title = 'Results: ' + model
+ except:
+ 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
----------
- etext : string
- string with lines that are added to the text output.
-
+ results : Model results instance
+ alpha : float
+ significance level for the confidence intervals (optional)
+ float_formatting: string
+ Float formatting for summary of parameters (optional)
+ title : string
+ Title of the summary table (optional)
+ xname : List of strings of length equal to the number of parameters
+ Names of the independent variables (optional)
+ yname : string
+ Name of the dependent variable (optional)
'''
- self.extra_txt = '\n'.join(etext)
- def as_text(self):
- '''return tables as string
+ param = summary_params(results, alpha=alpha)
+ info = summary_model(results)
+ if xname != None:
+ param.index = xname
+ if yname != None:
+ info['Dependent Variable:'] = yname
+ self.add_dict(info)
+ self.add_df(param, float_format=float_format)
+ self.add_title(title=title, results=results)
- Returns
- -------
- txt : string
- summary tables and extra text as one string
+ def as_text(self):
+ '''Generate ASCII Summary Table
+ '''
+ pad_sep, pad_index, widest = _pad(self.tables, self.settings)
+ tables = []
+ for i in range(len(self.tables)):
+ tab = _df_to_ascii(self.tables[i], pad_sep[i]+2, pad_index[i], **self.settings[i])
+ tables.append(tab)
+ rule_equal = '\n' + widest * '=' + '\n'
+ rule_dash = '\n' + widest * '-' + '\n'
+ ntxt = len(self.extra_txt)
+ if ntxt > 0:
+ txt = copy.deepcopy(self.extra_txt)
+ for i in range(ntxt):
+ txt[i] = '\n'.join(textwrap.wrap(txt[i], widest))
+ txt = '\n'.join(txt)
+ out = rule_equal + rule_dash.join(tables) + rule_equal + txt
+ else:
+ out = rule_equal + rule_dash.join(tables) + rule_equal
+ if type(self.title) == str:
+ if len(self.title) < widest:
+ title = ' ' * int(widest/2 - len(self.title)/2) + self.title
+ out = title + out
+ return out
+ def as_html(self):
+ '''Generate HTML Summary Table
'''
- txt = summary_return(self.tables, return_fmt='text')
- if not self.extra_txt is None:
- txt = txt + '\n\n' + self.extra_txt
- return txt
+ tables = copy.deepcopy(self.tables)
+ for i in range(len(tables)):
+ tables[i] = tables[i].to_html(header=self.settings[i]['header'],
+ index=self.settings[i]['index'],
+ float_format=self.settings[i]['float_format'])
+ out = '\n'.join(tables)
+ return out
def as_latex(self):
- '''return tables as string
-
- Returns
- -------
- latex : string
- summary tables and extra text as string of Latex
+ '''Generate LaTeX Summary Table
+ '''
+ tables = copy.deepcopy(self.tables)
+ for i in range(len(tables)):
+ tables[i] = tables[i].to_latex(header=self.settings[i]['index'],
+ index=self.settings[i]['index'])
+ tables[i] = tables[i].replace('\\hline\n', '')
+ out = '\\begin{table}\n' + '\n'.join(tables) + '\\end{table}\n'
+ return out
- Notes
- -----
- This currently merges tables with different number of columns.
- It is recommended to use `as_latex_tabular` directly on the individual
- tables.
- '''
- return summary_return(self.tables, return_fmt='latex')
+# ASCII table formatting
+def _df_to_ascii(df, pad_sep=2, pad_index=0, header=False, index=False,
+ float_format='%.4f', align='l', **kwargs):
+ '''Convert a DataFrame to ASCII table
- def as_csv(self):
- '''return tables as string
+ Parameters
+ ----------
+ df : DataFrame
+ Print df as ASCII table
+ pad_sep : int
+ Number of spaces in between columns
+ pad_index : int
+ Number of spaces after the first column
+ header : bool
+ Reproduce the DataFrame header in ASCII Table?
+ index : bool
+ Reproduce the DataFrame row index in ASCII Table?
+ float_format : string
+ Float format
+ align : string:
+ data alignment (l/c/r)
- Returns
- -------
- csv : string
- concatenated summary tables in comma delimited format
+ Returns
+ -------
+ ASCII table as string
+ '''
+ # Format numbers where possible and convert to Numpy array (type str)
+ for col in df.columns:
+ try:
+ df[col] = [float_format % x for x in df[col]]
+ except:
+ pass
+ try:
+ df[col] = [x.lstrip().rstrip() for x in df[col]]
+ except:
+ pass
+ data = np.array(df)
+ # Headers and index
+ if header:
+ headers = map(str, df.columns)
+ else:
+ headers = None
+ if index:
+ # Pad right-side of index if necessary
+ try:
+ index = [str(x) + ' ' * pad_index for x in df.index]
+ except:
+ pass
+ else:
+ index=None
+ try:
+ # Pad right-side of first column if necessary
+ data[:,0] = [str(x) + ' ' * pad_index for x in data[:,0]]
+ except:
+ pass
+ # Numpy -> SimpleTable -> ASCII
+ st_fmt = {'fmt':'txt', 'title_align':'c', 'data_aligns':align,
+ 'table_dec_above':None, 'table_dec_below':None}
+ st_fmt['colsep'] = ' ' * pad_sep
+ ascii = SimpleTable(data, headers=headers, stubs=index, txt_fmt=st_fmt).as_text()
+ return ascii
+
+def _pad(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.
+ '''
+ tab = []
+ pad_index = []
+ pad_sep = []
+ for i in range(len(tables)):
+ tab.append(_df_to_ascii(tables[i], **settings[i]))
+ length = [len(x.splitlines()[0]) for x in tab]
+ len_max = max(length)
+ pad_sep = []
+ for i in range(len(tab)):
+ nsep = settings[i]['ncols'] - 1
+ pad = (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
+_model_types = {'OLS' : 'Ordinary least squares',
+ 'GLS' : 'Generalized least squares',
+ 'GLSAR' : 'Generalized least squares with AR(p)',
+ 'WLS' : 'Weigthed 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(**kwrds):
+ now = datetime.datetime.now()
+ return now.strftime('%Y-%m-%d %H:%M')
+ info = collections.OrderedDict()
+ 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']
+ info['R-squared:'] = lambda x: "%#8.3f" % x.rsquared
+ info['Adj. R-squared:'] = lambda x: "%#8.3f" % x.rsquared_adj
+ 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" % self.fvalue
+ info ['Prob (F-statistic):'] = lambda x: "%#6.3g" % self.f_pvalue
+ info['Scale:'] = lambda x: "%#8.5g" % x.scale
+ out = collections.OrderedDict()
+ for key in info.keys():
+ try:
+ out[key] = info[key](results)
+ except:
+ pass
+ return out
+
+def summary_params(results, alpha=.05):
+ '''create a summary table of parameters from results instance
- '''
- return summary_return(self.tables, return_fmt='csv')
+ Parameters
+ ----------
+ res : results instance
+ some required information is directly taken from the result
+ instance
+ alpha : float
+ significance level for the confidence intervals
- def as_html(self):
- '''return tables as string
+ Returns
+ -------
+ params_table : DataFrame instance
+ '''
+ #Parameters part of the summary table
+ data = np.array([results.params, results.bse, results.tvalues, results.pvalues]).T
+ data = np.hstack([data, results.conf_int(alpha)])
+ data = pd.DataFrame(data)
+ data.columns = ['Coef.', 'Std.Err.', 't', 'P>|t|',
+ '[' + str(alpha/2), str(1-alpha/2) + ']']
+ data.index = results.model.exog_names
+ return data
+
+
+# Vertical summary instance for multiple models
+def _col_params(result, float_format='%.4f', stars=True):
+ '''Stack coefficients and standard errors in single column
+ '''
- Returns
- -------
- html : string
- concatenated summary tables in HTML format
+ # 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.ix[:,1] = '(' + res.ix[:,1] + ')'
+ # Significance stars
+ if stars:
+ idx = res.ix[:,3] < .1
+ res.ix[:,0][idx] = res.ix[:,0][idx] + '*'
+ idx = res.ix[:,3] < .05
+ res.ix[:,0][idx] = res.ix[:,0][idx] + '*'
+ idx = res.ix[:,3] < .01
+ res.ix[:,0][idx] = res.ix[:,0][idx] + '*'
+ # Stack Coefs and Std.Errors
+ res = res.ix[:,:2]
+ res = res.stack()
+ res = pd.DataFrame(res)
+ return res
+
+def _col_info(result, info_dict=None):
+ '''Stack model info in a column
+ '''
- '''
- return summary_return(self.tables, return_fmt='html')
+ if info_dict == None:
+ info_dict = {'N': lambda x: str(int(x.nobs)),
+ 'AIC': lambda x: '%.3f' % x.aic,
+ 'R2': lambda x: '%.3f' % x.rsquared}
+ out = []
+ for i in info_dict:
+ try:
+ out.append(info_dict[i](result))
+ except:
+ out.append('')
+ out = pd.DataFrame(out)
+ out.index = pd.Index(info_dict.keys())
+ return out
+def summary_col(results, float_format='%.4f', model_names=None, stars=True,
+ info_dict=None):
+ '''Add the contents of a Dict to summary table
-if __name__ == "__main__":
- import statsmodels.api as sm
- data = sm.datasets.longley.load()
- data.exog = sm.add_constant(data.exog)
- res = sm.OLS(data.endog, data.exog).fit()
- #summary(
+ Parameters
+ ----------
+ results : statsmodels results instance or list of result instances
+ float_format : string
+ float format for coefficients and standard errors
+ model_names : list of strings of length len(results)
+ stars : bool
+ print significance stars
+ info_dict : dict
+ dict of lambda functions to be applied to results instances to retrieve
+ model info
+ '''
+ # Coerce to list if user feeds a results instance
+ if type(results) != list:
+ results = [results]
+ # Params as dataframe columns
+ cols = [_col_params(x, stars=stars, float_format=float_format) for x in results]
+ merg = lambda x,y: x.merge(y, how='outer', right_index=True, left_index=True)
+ summ = reduce(merg, cols)
+ # Index
+ idx1 = summ.index.get_level_values(0).tolist()
+ idx2 = range(1,len(idx1),2)
+ for i in idx2:
+ idx1[i] = ''
+ summ.index = pd.Index(idx1)
+ # Header
+ if model_names == None:
+ header = []
+ try:
+ for r in results:
+ header.append(r.model.endog_names)
+ except:
+ i = 0
+ for r in results:
+ header.append('Model ' + i)
+ i += 1
+ else:
+ header = model_names
+ summ.columns = pd.Index(header)
+ summ = summ.fillna('')
+ # Info as dataframe columns
+ cols = [_col_info(x, info_dict) for x in results]
+ merg = lambda x,y: x.merge(y, how='outer', right_index=True, left_index=True)
+ info = reduce(merg, cols)
+ # Summary
+ smry = Summary()
+ smry.add_df(summ, header=True, align='l')
+ smry.add_df(info, header=False, align='l')
+ smry.add_text('Standard errors in parentheses.')
+ if stars:
+ smry.add_text('* p<.1, ** p<.05, ***p<.01')
+ return smry
View
456 statsmodels/iolib/summary2.py
@@ -1,456 +0,0 @@
-import numpy as np
-import pandas as pd
-import datetime
-import copy
-import collections
-from statsmodels.iolib.table import SimpleTable
-import StringIO
-import textwrap
-
-class Summary(object):
- def __init__(self):
- self.tables = []
- self.settings = []
- self.extra_txt = []
- self.title = 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 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: string
- Formatting to float data columns
- align : string
- Data alignment (l/c/r)
- '''
-
- settings = {'ncols':df.shape[1],
- 'index':index, 'header':header,
- 'float_format':float_format, 'align':align}
- if index:
- settings['ncols'] += 1
- self.tables.append(df)
- self.settings.append(settings)
-
- def add_array(self, array, align='l', float_format="%.4f"):
- '''Add the contents of a Numpy array to summary table
-
- Parameters
- ----------
- array : numpy array (2D)
- float_format: string
- Formatting to array if type is float
- align : string
- 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'):
- '''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 : string
- Data alignment (l/c/r)
- '''
-
- key = map(str, d.keys())
- val = map(str, d.values())
- data = np.array([key, val]).T
- # Pad if necessary to fill columns
- if data.shape[0] % ncols != 0:
- pad = ncols - (data.shape[0] % ncols)
- data = np.vstack([data, np.array(pad * [['','']])])
- # Split and join as multi-columns
- 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 indendented.
- '''
- 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 type(title) == str:
- self.title = title
- else:
- try:
- model = results.model.__class__.__name__
- if model in _model_types:
- model = _model_types[model]
- self.title = 'Results: ' + model
- except:
- 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_formatting: string
- Float formatting for summary of parameters (optional)
- title : string
- Title of the summary table (optional)
- xname : List of strings of length equal to the number of parameters
- Names of the independent variables (optional)
- yname : string
- Name of the dependent variable (optional)
- '''
-
- param = summary_params(results, alpha=alpha)
- info = summary_model(results)
- if xname != None:
- param.index = xname
- if yname != None:
- info['Dependent Variable:'] = yname
- self.add_dict(info)
- self.add_df(param, float_format=float_format)
- self.add_title(title=title, results=results)
-
- def as_text(self):
- '''Generate ASCII Summary Table
- '''
- pad_sep, pad_index, widest = _pad(self.tables, self.settings)
- tables = []
- for i in range(len(self.tables)):
- tab = _df_to_ascii(self.tables[i], pad_sep[i]+2, pad_index[i], **self.settings[i])
- tables.append(tab)
- rule_equal = '\n' + widest * '=' + '\n'
- rule_dash = '\n' + widest * '-' + '\n'
- ntxt = len(self.extra_txt)
- if ntxt > 0:
- txt = copy.deepcopy(self.extra_txt)
- for i in range(ntxt):
- txt[i] = '\n'.join(textwrap.wrap(txt[i], widest))
- txt = '\n'.join(txt)
- out = rule_equal + rule_dash.join(tables) + rule_equal + txt
- else:
- out = rule_equal + rule_dash.join(tables) + rule_equal
- if type(self.title) == str:
- if len(self.title) < widest:
- title = ' ' * int(widest/2 - len(self.title)/2) + self.title
- out = title + out
- return out
-
- def as_html(self):
- '''Generate HTML Summary Table
- '''
- tables = copy.deepcopy(self.tables)
- for i in range(len(tables)):
- tables[i] = tables[i].to_html(header=self.settings[i]['header'],
- index=self.settings[i]['index'],
- float_format=self.settings[i]['float_format'])
- out = '\n'.join(tables)
- return out
-
- def as_latex(self):
- '''Generate LaTeX Summary Table
- '''
- tables = copy.deepcopy(self.tables)
- for i in range(len(tables)):
- tables[i] = tables[i].to_latex(header=self.settings[i]['index'],
- index=self.settings[i]['index'])
- tables[i] = tables[i].replace('\\hline\n', '')
- out = '\\begin{table}\n' + '\n'.join(tables) + '\\end{table}\n'
- return out
-
-
-# ASCII table formatting
-def _df_to_ascii(df, pad_sep=2, pad_index=0, header=False, index=False,
- float_format='%.4f', align='l', **kwargs):
- '''Convert a DataFrame to ASCII table
-
- Parameters
- ----------
- df : DataFrame
- Print df as ASCII table
- pad_sep : int
- Number of spaces in between columns
- pad_index : int
- Number of spaces after the first column
- header : bool
- Reproduce the DataFrame header in ASCII Table?
- index : bool
- Reproduce the DataFrame row index in ASCII Table?
- float_format : string
- Float format
- align : string:
- data alignment (l/c/r)
-
- Returns
- -------
- ASCII table as string
- '''
- # Format numbers where possible and convert to Numpy array (type str)
- for col in df.columns:
- try:
- df[col] = [float_format % x for x in df[col]]
- except:
- pass
- try:
- df[col] = [x.lstrip().rstrip() for x in df[col]]
- except:
- pass
- data = np.array(df)
- # Headers and index
- if header:
- headers = map(str, df.columns)
- else:
- headers = None
- if index:
- # Pad right-side of index if necessary
- try:
- index = [str(x) + ' ' * pad_index for x in df.index]
- except:
- pass
- else:
- index=None
- try:
- # Pad right-side of first column if necessary
- data[:,0] = [str(x) + ' ' * pad_index for x in data[:,0]]
- except:
- pass
- # Numpy -> SimpleTable -> ASCII
- st_fmt = {'fmt':'txt', 'title_align':'c', 'data_aligns':align,
- 'table_dec_above':None, 'table_dec_below':None}
- st_fmt['colsep'] = ' ' * pad_sep
- ascii = SimpleTable(data, headers=headers, stubs=index, txt_fmt=st_fmt).as_text()
- return ascii
-
-def _pad(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.
- '''
- tab = []
- pad_index = []
- pad_sep = []
- for i in range(len(tables)):
- tab.append(_df_to_ascii(tables[i], **settings[i]))
- length = [len(x.splitlines()[0]) for x in tab]
- len_max = max(length)
- pad_sep = []
- for i in range(len(tab)):
- nsep = settings[i]['ncols'] - 1
- pad = (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
-_model_types = {'OLS' : 'Ordinary least squares',
- 'GLS' : 'Generalized least squares',
- 'GLSAR' : 'Generalized least squares with AR(p)',
- 'WLS' : 'Weigthed 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(**kwrds):
- now = datetime.datetime.now()
- return now.strftime('%Y-%m-%d %H:%M')
- info = collections.OrderedDict()
- 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']
- info['R-squared:'] = lambda x: "%#8.3f" % x.rsquared
- info['Adj. R-squared:'] = lambda x: "%#8.3f" % x.rsquared_adj
- 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" % self.fvalue
- info ['Prob (F-statistic):'] = lambda x: "%#6.3g" % self.f_pvalue
- info['Scale:'] = lambda x: "%#8.5g" % x.scale
- out = collections.OrderedDict()
- for key in info.keys():
- try:
- out[key] = info[key](results)
- except:
- pass
- return out
-
-def summary_params(results, alpha=.05):
- '''create a summary table of parameters from results instance
-
- Parameters
- ----------
- res : results instance
- some required information is directly taken from the result
- instance
- alpha : float
- significance level for the confidence intervals
-
- Returns
- -------
- params_table : DataFrame instance
- '''
- #Parameters part of the summary table
- data = np.array([results.params, results.bse, results.tvalues, results.pvalues]).T
- data = np.hstack([data, results.conf_int(alpha)])
- data = pd.DataFrame(data)
- data.columns = ['Coef.', 'Std.Err.', 't', 'P>|t|',
- '[' + str(alpha/2), str(1-alpha/2) + ']']
- data.index = results.model.exog_names
- return data
-
-
-# Vertical summary instance for multiple models
-def _col_params(result, float_format='%.4f', stars=True):
- '''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.ix[:,1] = '(' + res.ix[:,1] + ')'
- # Significance stars
- if stars:
- idx = res.ix[:,3] < .1
- res.ix[:,0][idx] = res.ix[:,0][idx] + '*'
- idx = res.ix[:,3] < .05
- res.ix[:,0][idx] = res.ix[:,0][idx] + '*'
- idx = res.ix[:,3] < .01
- res.ix[:,0][idx] = res.ix[:,0][idx] + '*'
- # Stack Coefs and Std.Errors
- res = res.ix[:,:2]
- res = res.stack()
- res = pd.DataFrame(res)
- return res
-
-def _col_info(result, info_dict=None):
- '''Stack model info in a column
- '''
-
- if info_dict == None:
- info_dict = {'N': lambda x: str(int(x.nobs)),
- 'AIC': lambda x: '%.3f' % x.aic,
- 'R2': lambda x: '%.3f' % x.rsquared}
- out = []
- for i in info_dict:
- try:
- out.append(info_dict[i](result))
- except:
- out.append('')
- out = pd.DataFrame(out)
- out.index = pd.Index(info_dict.keys())
- return out
-
-def summary_col(results, float_format='%.4f', model_names=None, stars=True,
- info_dict=None):
- '''Add the contents of a Dict to summary table
-
- Parameters
- ----------
- results : statsmodels results instance or list of result instances
- float_format : string
- float format for coefficients and standard errors
- model_names : list of strings of length len(results)
- stars : bool
- print significance stars
- info_dict : dict
- dict of lambda functions to be applied to results instances to retrieve
- model info
- '''
-
- # Coerce to list if user feeds a results instance
- if type(results) != list:
- results = [results]
- # Params as dataframe columns
- cols = [_col_params(x, stars=stars, float_format=float_format) for x in results]
- merg = lambda x,y: x.merge(y, how='outer', right_index=True, left_index=True)
- summ = reduce(merg, cols)
- # Index
- idx1 = summ.index.get_level_values(0).tolist()
- idx2 = range(1,len(idx1),2)
- for i in idx2:
- idx1[i] = ''
- summ.index = pd.Index(idx1)
- # Header
- if model_names == None:
- header = []
- try:
- for r in results:
- header.append(r.model.endog_names)
- except:
- i = 0
- for r in results:
- header.append('Model ' + i)
- i += 1
- else:
- header = model_names
- summ.columns = pd.Index(header)
- summ = summ.fillna('')
- # Info as dataframe columns
- cols = [_col_info(x, info_dict) for x in results]
- merg = lambda x,y: x.merge(y, how='outer', right_index=True, left_index=True)
- info = reduce(merg, cols)
- # Summary
- smry = Summary()
- smry.add_df(summ, header=True, align='l')
- smry.add_df(info, header=False, align='l')
- smry.add_text('Standard errors in parentheses.')
- if stars:
- smry.add_text('* p<.1, ** p<.05, ***p<.01')
- return smry
View
905 statsmodels/iolib/summary_old.py
@@ -0,0 +1,905 @@
+
+
+import numpy as np
+from statsmodels.compatnp.iter_compat import zip_longest
+from statsmodels.iolib.table import SimpleTable
+from statsmodels.iolib.tableformatting import (gen_fmt, fmt_2,
+ fmt_params, fmt_base, fmt_2cols)
+#from statsmodels.iolib.summaryd import summary_params_2dflat
+#from summaryd import summary_params_2dflat
+
+def forg(x, prec=3):
+ 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 NotImplementedError
+
+
+def summary(self, yname=None, xname=None, title=0, alpha=.05,
+ returns='text', model_info=None):
+ """
+ Parameters
+ -----------
+ yname : string
+ optional, Default is `Y`
+ xname : list of strings
+ optional, Default is `X.#` for # in p the number of regressors
+ Confidance interval : (0,1) not implimented
+ title : string
+ optional, Defualt is 'Generalized linear model'
+ returns : string
+ 'text', 'table', 'csv', 'latex', 'html'
+
+ Returns
+ -------
+ Defualt :
+ 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.
+ """
+ import time as time
+
+
+
+ #TODO Make sure all self.model.__class__.__name__ are listed
+ model_types = {'OLS' : 'Ordinary least squares',
+ 'GLS' : 'Generalized least squares',
+ 'GLSAR' : 'Generalized least squares with AR(p)',
+ 'WLS' : 'Weigthed least squares',
+ 'RLM' : 'Robust linear model',
+ 'GLM' : 'Generalized linear model'
+ }
+ model_methods = {'OLS' : 'Least Squares',
+ 'GLS' : 'Least Squares',
+ 'GLSAR' : 'Least Squares',
+ 'WLS' : 'Least Squares',
+ 'RLM' : '?',
+ 'GLM' : '?'
+ }
+ if title==0:
+ title = model_types[self.model.__class__.__name__]
+ if yname is None:
+ try:
+ yname = self.model.endog_names
+ except AttributeError:
+ yname = 'y'
+ if xname is None:
+ try:
+ xname = self.model.exog_names
+ except AttributeError:
+ xname = ['var_%d' % i for i in range(len(self.params))]
+ 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__
+ #dist_family = self.model.family.__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), #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_stubs_left = ('Model type:',
+## 'Date:',
+## 'Dependent Variable:',
+## 'df model'
+## )
+## gen_data_left = [[modeltype],
+## [date],
+## yname, #What happens with multiple names?
+## [df_model]
+## ]
+ 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 = xrange(len(xname))
+ tstat = tstats[modeltype]
+ prob_stat = prob_stats[modeltype]
+
+ # Simpletable should be able to handle the formating
+ params_data = zip(["%#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, #gen_fmt,
+ )
+
+ #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 ols_to_csv():
+ """
+ exports ols summary data to csv
+ """
+ pass
+ def glm_printer():
+ table = str(general_table)+'\n'+str(parameter_table)
+ return table
+ pass
+
+ 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:
+ try:
+ yname = self.model.endog_names
+ except AttributeError:
+ yname = 'y'
+ if xname is None:
+ try:
+ xname = self.model.exog_names
+ except AttributeError:
+ 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
+ import time
+ 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
+ #alternate spellings are commented out to force unique labels
+ default_items = dict([
+ ('Dependent Variable:', lambda: [yname]),
+ ('Dep. Variable:', lambda: [yname]),
+ ('Model:', lambda: [results.model.__class__.__name__]),
+ #('Model type:', lambda: [results.model.__class__.__name__]),
+ ('Date:', lambda: [date]),
+ ('Time:', lambda: time_of_day),
+ ('Number of Obs:', lambda: [results.nobs]),
+ #('No. of Observations:', lambda: ["%#6d" % results.nobs]),
+ ('No. Observations:', lambda: ["%#6d" % results.nobs]),
+ #('Df model:', lambda: [results.df_model]),
+ ('Df Model:', lambda: ["%#6d" % results.df_model]),
+ #TODO: check when we have non-integer df
+ ('Df Residuals:', lambda: ["%#6d" % results.df_resid]),
+ #('Df resid:', lambda: [results.df_resid]),
+ #('df resid:', lambda: [results.df_resid]), #check capitalization
+ ('Log-Likelihood:', lambda: ["%#8.5g" % results.llf]) #doesn't exist for RLM - exception
+ #('Method:', lambda: [???]), #no default for this
+ ])
+
+ 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
+ gen_left.append(('Log-Likelihood', None))
+ except: #AttributeError, NotImplementedError
+ pass
+
+ gen_right = []
+
+
+ gen_title = title
+ gen_header = None
+
+ #needed_values = [k for k,v in gleft + gright if v is None] #not used anymore
+ #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
+ 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 doesn't 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 #gen_fmt
+ )
+ 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 #, gen_table_left, gen_table_right
+
+
+
+def summary_params(results, yname=None, xname=None, alpha=.05, use_t=True,
+ skip_header=False):
+ '''create a summary table for the parameters
+
+ Parameters
+ ----------
+ res : results instance
+ some required information is directly taken from the result
+ instance
+ yname : string or None
+ optional name for the endogenous variable, default is "y"
+ xname : list of strings or 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 don't 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
+ alp = str((1-alpha)*100)+'%'
+ if use_t:
+ param_header = ['coef', 'std err', 't', 'P>|t|',
+ '[' + alp + ' Conf. Int.]']
+ else:
+ param_header = ['coef', 'std err', 'z', 'P>|z|',
+ '[' + alp + ' Conf. Int.]']
+
+ if skip_header:
+ param_header = None
+
+
+ _, xname = _getnames(results, yname=yname, xname=xname)
+
+ params_stubs = xname
+
+ exog_idx = xrange(len(xname))
+
+ #center confidence intervals if they are unequal lengths
+# confint = ["(%#6.3g, %#6.3g)" % tuple(conf_int[i]) for i in \
+# exog_idx]
+ confint = ["%s %s" % tuple(map(forg, conf_int[i])) for i in \
+ exog_idx]
+ len_ci = map(len, confint)
+ max_ci = max(len_ci)
+ min_ci = min(len_ci)
+
+ if min_ci < max_ci:
+ confint = [ci.center(max_ci) for ci in confint]
+
+ #explicit f/g formatting, now uses forg, f or g depending on values
+# params_data = zip(["%#6.4g" % (params[i]) for i in exog_idx],
+# ["%#6.4f" % (std_err[i]) for i in exog_idx],
+# ["%#6.3f" % (tvalues[i]) for i in exog_idx],
+# ["%#6.3f" % (pvalues[i]) for i in exog_idx],
+# confint
+## ["(%#6.3g, %#6.3g)" % tuple(conf_int[i]) for i in \
+## exog_idx]
+# )
+
+ params_data = zip([forg(params[i], prec=4) for i in exog_idx],