Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import copy 

2from itertools import zip_longest 

3import time 

4 

5from statsmodels.compat.python import lrange, lmap, lzip 

6import numpy as np 

7from statsmodels.iolib.table import SimpleTable 

8from statsmodels.iolib.tableformatting import (gen_fmt, fmt_2, 

9 fmt_params, fmt_2cols) 

10from .summary2 import _model_types 

11 

12 

13def forg(x, prec=3): 

14 if prec == 3: 

15 # for 3 decimals 

16 if (abs(x) >= 1e4) or (abs(x) < 1e-4): 

17 return '%9.3g' % x 

18 else: 

19 return '%9.3f' % x 

20 elif prec == 4: 

21 if (abs(x) >= 1e4) or (abs(x) < 1e-4): 

22 return '%10.4g' % x 

23 else: 

24 return '%10.4f' % x 

25 else: 

26 raise ValueError("`prec` argument must be either 3 or 4, not {prec}" 

27 .format(prec=prec)) 

28 

29 

30def d_or_f(x, width=6): 

31 """convert number to string with either integer of float formatting 

32 

33 This is used internally for nobs and degrees of freedom which are usually 

34 integers but can be float in some cases. 

35 

36 Parameters 

37 ---------- 

38 x : int or float 

39 width : int 

40 only used if x is nan 

41 

42 Returns 

43 ------- 

44 str : str 

45 number as formatted string 

46 """ 

47 if np.isnan(x): 

48 return (width - 3) * ' ' + 'NaN' 

49 

50 if x // 1 == x: 

51 return "%#6d" % x 

52 else: 

53 return "%#8.2f" % x 

54 

55 

56def summary(self, yname=None, xname=None, title=0, alpha=.05, 

57 returns='text', model_info=None): 

58 """ 

59 Parameters 

60 ---------- 

61 yname : str 

62 optional, Default is `Y` 

63 xname : list[str] 

64 optional, Default is `X.#` for # in p the number of regressors 

65 Confidance interval : (0,1) not implimented 

66 title : str 

67 optional, Defualt is 'Generalized linear model' 

68 returns : str 

69 'text', 'table', 'csv', 'latex', 'html' 

70 

71 Returns 

72 ------- 

73 Default : 

74 returns='print' 

75 Prints the summarirized results 

76 

77 Option : 

78 returns='text' 

79 Prints the summarirized results 

80 

81 Option : 

82 returns='table' 

83 SimpleTable instance : summarizing the fit of a linear model. 

84 

85 Option : 

86 returns='csv' 

87 returns a string of csv of the results, to import into a spreadsheet 

88 

89 Option : 

90 returns='latex' 

91 Not implimented yet 

92 

93 Option : 

94 returns='HTML' 

95 Not implimented yet 

96 

97 

98 Examples (needs updating) 

99 -------- 

100 >>> import statsmodels as sm 

101 >>> data = sm.datasets.longley.load(as_pandas=False) 

102 >>> data.exog = sm.add_constant(data.exog) 

103 >>> ols_results = sm.OLS(data.endog, data.exog).results 

104 >>> print ols_results.summary() 

105 ... 

106 

107 Notes 

108 ----- 

109 conf_int calculated from normal dist. 

110 """ 

111 if title == 0: 

112 title = _model_types[self.model.__class__.__name__] 

113 

114 if xname is not None and len(xname) != len(self.params): 

115 # GH 2298 

116 raise ValueError('User supplied xnames must have the same number of ' 

117 'entries as the number of model parameters ' 

118 '({0})'.format(len(self.params))) 

119 

120 yname, xname = _getnames(self, yname, xname) 

121 

122 time_now = time.localtime() 

123 time_of_day = [time.strftime("%H:%M:%S", time_now)] 

124 date = time.strftime("%a, %d %b %Y", time_now) 

125 modeltype = self.model.__class__.__name__ 

126 nobs = self.nobs 

127 df_model = self.df_model 

128 df_resid = self.df_resid 

129 

130 #General part of the summary table, Applicable to all? models 

131 #------------------------------------------------------------ 

132 # TODO: define this generically, overwrite in model classes 

133 #replace definition of stubs data by single list 

134 #e.g. 

135 gen_left = [('Model type:', [modeltype]), 

136 ('Date:', [date]), 

137 ('Dependent Variable:', yname), # TODO: What happens with multiple names? 

138 ('df model', [df_model]) 

139 ] 

140 gen_stubs_left, gen_data_left = zip_longest(*gen_left) #transpose row col 

141 

142 gen_title = title 

143 gen_header = None 

144 gen_table_left = SimpleTable(gen_data_left, 

145 gen_header, 

146 gen_stubs_left, 

147 title=gen_title, 

148 txt_fmt=gen_fmt 

149 ) 

150 

151 gen_stubs_right = ('Method:', 

152 'Time:', 

153 'Number of Obs:', 

154 'df resid') 

155 gen_data_right = ([modeltype], #was dist family need to look at more 

156 time_of_day, 

157 [nobs], 

158 [df_resid] 

159 ) 

160 gen_table_right = SimpleTable(gen_data_right, 

161 gen_header, 

162 gen_stubs_right, 

163 title=gen_title, 

164 txt_fmt=gen_fmt 

165 ) 

166 gen_table_left.extend_right(gen_table_right) 

167 general_table = gen_table_left 

168 

169 # Parameters part of the summary table 

170 # ------------------------------------ 

171 # Note: this is not necessary since we standardized names, 

172 # only t versus normal 

173 tstats = {'OLS': self.t(), 

174 'GLS': self.t(), 

175 'GLSAR': self.t(), 

176 'WLS': self.t(), 

177 'RLM': self.t(), 

178 'GLM': self.t()} 

179 prob_stats = {'OLS': self.pvalues, 

180 'GLS': self.pvalues, 

181 'GLSAR': self.pvalues, 

182 'WLS': self.pvalues, 

183 'RLM': self.pvalues, 

184 'GLM': self.pvalues 

185 } 

186 # Dictionary to store the header names for the parameter part of the 

187 # summary table. look up by modeltype 

188 alp = str((1-alpha)*100)+'%' 

189 param_header = { 

190 'OLS' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'], 

191 'GLS' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'], 

192 'GLSAR' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'], 

193 'WLS' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'], 

194 'GLM' : ['coef', 'std err', 't', 'P>|t|', alp + ' Conf. Interval'], #glm uses t-distribution 

195 'RLM' : ['coef', 'std err', 'z', 'P>|z|', alp + ' Conf. Interval'] #checke z 

196 } 

197 params_stubs = xname 

198 params = self.params 

199 conf_int = self.conf_int(alpha) 

200 std_err = self.bse 

201 exog_len = lrange(len(xname)) 

202 tstat = tstats[modeltype] 

203 prob_stat = prob_stats[modeltype] 

204 

205 # Simpletable should be able to handle the formating 

206 params_data = lzip(["%#6.4g" % (params[i]) for i in exog_len], 

207 ["%#6.4f" % (std_err[i]) for i in exog_len], 

208 ["%#6.4f" % (tstat[i]) for i in exog_len], 

209 ["%#6.4f" % (prob_stat[i]) for i in exog_len], 

210 ["(%#5g, %#5g)" % tuple(conf_int[i]) for i in exog_len]) 

211 parameter_table = SimpleTable(params_data, 

212 param_header[modeltype], 

213 params_stubs, 

214 title=None, 

215 txt_fmt=fmt_2 

216 ) 

217 

218 #special table 

219 #------------- 

220 #TODO: exists in linear_model, what about other models 

221 #residual diagnostics 

222 

223 #output options 

224 #-------------- 

225 #TODO: JP the rest needs to be fixed, similar to summary in linear_model 

226 

227 def ols_printer(): 

228 """ 

229 print summary table for ols models 

230 """ 

231 table = str(general_table)+'\n'+str(parameter_table) 

232 return table 

233 

234 def glm_printer(): 

235 table = str(general_table)+'\n'+str(parameter_table) 

236 return table 

237 

238 printers = {'OLS': ols_printer, 'GLM': glm_printer} 

239 

240 if returns == 'print': 

241 try: 

242 return printers[modeltype]() 

243 except KeyError: 

244 return printers['OLS']() 

245 

246 

247def _getnames(self, yname=None, xname=None): 

248 '''extract names from model or construct names 

249 ''' 

250 if yname is None: 

251 if getattr(self.model, 'endog_names', None) is not None: 

252 yname = self.model.endog_names 

253 else: 

254 yname = 'y' 

255 

256 if xname is None: 

257 if getattr(self.model, 'exog_names', None) is not None: 

258 xname = self.model.exog_names 

259 else: 

260 xname = ['var_%d' % i for i in range(len(self.params))] 

261 

262 return yname, xname 

263 

264 

265def summary_top(results, title=None, gleft=None, gright=None, yname=None, xname=None): 

266 '''generate top table(s) 

267 

268 

269 TODO: this still uses predefined model_methods 

270 ? allow gleft, gright to be 1 element tuples instead of filling with None? 

271 

272 ''' 

273 #change of names ? 

274 gen_left, gen_right = gleft, gright 

275 

276 # time and names are always included 

277 time_now = time.localtime() 

278 time_of_day = [time.strftime("%H:%M:%S", time_now)] 

279 date = time.strftime("%a, %d %b %Y", time_now) 

280 

281 yname, xname = _getnames(results, yname=yname, xname=xname) 

282 

283 # create dictionary with default 

284 # use lambdas because some values raise exception if they are not available 

285 default_items = dict([ 

286 ('Dependent Variable:', lambda: [yname]), 

287 ('Dep. Variable:', lambda: [yname]), 

288 ('Model:', lambda: [results.model.__class__.__name__]), 

289 ('Date:', lambda: [date]), 

290 ('Time:', lambda: time_of_day), 

291 ('Number of Obs:', lambda: [results.nobs]), 

292 ('No. Observations:', lambda: [d_or_f(results.nobs)]), 

293 ('Df Model:', lambda: [d_or_f(results.df_model)]), 

294 ('Df Residuals:', lambda: [d_or_f(results.df_resid)]), 

295 ('Log-Likelihood:', lambda: ["%#8.5g" % results.llf]) # does not exist for RLM - exception 

296 ]) 

297 

298 if title is None: 

299 title = results.model.__class__.__name__ + 'Regression Results' 

300 

301 if gen_left is None: 

302 # default: General part of the summary table, Applicable to all? models 

303 gen_left = [('Dep. Variable:', None), 

304 ('Model type:', None), 

305 ('Date:', None), 

306 ('No. Observations:', None), 

307 ('Df model:', None), 

308 ('Df resid:', None)] 

309 

310 try: 

311 llf = results.llf # noqa: F841 

312 gen_left.append(('Log-Likelihood', None)) 

313 except: # AttributeError, NotImplementedError 

314 pass 

315 

316 gen_right = [] 

317 

318 gen_title = title 

319 gen_header = None 

320 

321 # replace missing (None) values with default values 

322 gen_left_ = [] 

323 for item, value in gen_left: 

324 if value is None: 

325 value = default_items[item]() # let KeyErrors raise exception 

326 gen_left_.append((item, value)) 

327 gen_left = gen_left_ 

328 

329 if gen_right: 

330 gen_right_ = [] 

331 for item, value in gen_right: 

332 if value is None: 

333 value = default_items[item]() # let KeyErrors raise exception 

334 gen_right_.append((item, value)) 

335 gen_right = gen_right_ 

336 

337 # check nothing was missed 

338 missing_values = [k for k,v in gen_left + gen_right if v is None] 

339 assert missing_values == [], missing_values 

340 

341 # pad both tables to equal number of rows 

342 if gen_right: 

343 if len(gen_right) < len(gen_left): 

344 # fill up with blank lines to same length 

345 gen_right += [(' ', ' ')] * (len(gen_left) - len(gen_right)) 

346 elif len(gen_right) > len(gen_left): 

347 # fill up with blank lines to same length, just to keep it symmetric 

348 gen_left += [(' ', ' ')] * (len(gen_right) - len(gen_left)) 

349 

350 # padding in SimpleTable does not work like I want 

351 #force extra spacing and exact string length in right table 

352 gen_right = [('%-21s' % (' '+k), v) for k,v in gen_right] 

353 gen_stubs_right, gen_data_right = zip_longest(*gen_right) #transpose row col 

354 gen_table_right = SimpleTable(gen_data_right, 

355 gen_header, 

356 gen_stubs_right, 

357 title=gen_title, 

358 txt_fmt=fmt_2cols 

359 ) 

360 else: 

361 gen_table_right = [] #because .extend_right seems works with [] 

362 

363 #moved below so that we can pad if needed to match length of gen_right 

364 #transpose rows and columns, `unzip` 

365 gen_stubs_left, gen_data_left = zip_longest(*gen_left) #transpose row col 

366 

367 gen_table_left = SimpleTable(gen_data_left, 

368 gen_header, 

369 gen_stubs_left, 

370 title=gen_title, 

371 txt_fmt=fmt_2cols 

372 ) 

373 

374 gen_table_left.extend_right(gen_table_right) 

375 general_table = gen_table_left 

376 

377 return general_table 

378 

379 

380def summary_params(results, yname=None, xname=None, alpha=.05, use_t=True, 

381 skip_header=False, title=None): 

382 '''create a summary table for the parameters 

383 

384 Parameters 

385 ---------- 

386 res : results instance 

387 some required information is directly taken from the result 

388 instance 

389 yname : {str, None} 

390 optional name for the endogenous variable, default is "y" 

391 xname : {list[str], None} 

392 optional names for the exogenous variables, default is "var_xx" 

393 alpha : float 

394 significance level for the confidence intervals 

395 use_t : bool 

396 indicator whether the p-values are based on the Student-t 

397 distribution (if True) or on the normal distribution (if False) 

398 skip_headers : bool 

399 If false (default), then the header row is added. If true, then no 

400 header row is added. 

401 

402 Returns 

403 ------- 

404 params_table : SimpleTable instance 

405 ''' 

406 

407 # Parameters part of the summary table 

408 # ------------------------------------ 

409 # Note: this is not necessary since we standardized names, 

410 # only t versus normal 

411 

412 if isinstance(results, tuple): 

413 # for multivariate endog 

414 # TODO: check whether I do not want to refactor this 

415 #we need to give parameter alpha to conf_int 

416 results, params, std_err, tvalues, pvalues, conf_int = results 

417 else: 

418 params = results.params 

419 std_err = results.bse 

420 tvalues = results.tvalues # is this sometimes called zvalues 

421 pvalues = results.pvalues 

422 conf_int = results.conf_int(alpha) 

423 if params.size == 0: 

424 return SimpleTable([['No Model Parameters']]) 

425 # Dictionary to store the header names for the parameter part of the 

426 # summary table. look up by modeltype 

427 if use_t: 

428 param_header = ['coef', 'std err', 't', 'P>|t|', 

429 '[' + str(alpha/2), str(1-alpha/2) + ']'] 

430 else: 

431 param_header = ['coef', 'std err', 'z', 'P>|z|', 

432 '[' + str(alpha/2), str(1-alpha/2) + ']'] 

433 

434 if skip_header: 

435 param_header = None 

436 

437 _, xname = _getnames(results, yname=yname, xname=xname) 

438 

439 if len(xname) != len(params): 

440 raise ValueError('xnames and params do not have the same length') 

441 

442 params_stubs = xname 

443 

444 exog_idx = lrange(len(xname)) 

445 

446 params_data = lzip([forg(params[i], prec=4) for i in exog_idx], 

447 [forg(std_err[i]) for i in exog_idx], 

448 [forg(tvalues[i]) for i in exog_idx], 

449 ["%#6.3f" % (pvalues[i]) for i in exog_idx], 

450 [forg(conf_int[i,0]) for i in exog_idx], 

451 [forg(conf_int[i,1]) for i in exog_idx]) 

452 parameter_table = SimpleTable(params_data, 

453 param_header, 

454 params_stubs, 

455 title=title, 

456 txt_fmt=fmt_params 

457 ) 

458 

459 return parameter_table 

460 

461 

462def summary_params_frame(results, yname=None, xname=None, alpha=.05, 

463 use_t=True): 

464 '''create a summary table for the parameters 

465 

466 Parameters 

467 ---------- 

468 res : results instance 

469 some required information is directly taken from the result 

470 instance 

471 yname : {str, None} 

472 optional name for the endogenous variable, default is "y" 

473 xname : {list[str], None} 

474 optional names for the exogenous variables, default is "var_xx" 

475 alpha : float 

476 significance level for the confidence intervals 

477 use_t : bool 

478 indicator whether the p-values are based on the Student-t 

479 distribution (if True) or on the normal distribution (if False) 

480 skip_headers : bool 

481 If false (default), then the header row is added. If true, then no 

482 header row is added. 

483 

484 Returns 

485 ------- 

486 params_table : SimpleTable instance 

487 ''' 

488 

489 # Parameters part of the summary table 

490 # ------------------------------------ 

491 # Note: this is not necessary since we standardized names, 

492 # only t versus normal 

493 

494 if isinstance(results, tuple): 

495 # for multivariate endog 

496 # TODO: check whether I do not want to refactor this 

497 #we need to give parameter alpha to conf_int 

498 results, params, std_err, tvalues, pvalues, conf_int = results 

499 else: 

500 params = results.params 

501 std_err = results.bse 

502 tvalues = results.tvalues #is this sometimes called zvalues 

503 pvalues = results.pvalues 

504 conf_int = results.conf_int(alpha) 

505 

506 # Dictionary to store the header names for the parameter part of the 

507 # summary table. look up by modeltype 

508 if use_t: 

509 param_header = ['coef', 'std err', 't', 'P>|t|', 

510 'Conf. Int. Low', 'Conf. Int. Upp.'] 

511 else: 

512 param_header = ['coef', 'std err', 'z', 'P>|z|', 

513 'Conf. Int. Low', 'Conf. Int. Upp.'] 

514 

515 _, xname = _getnames(results, yname=yname, xname=xname) 

516 

517 from pandas import DataFrame 

518 table = np.column_stack((params, std_err, tvalues, pvalues, conf_int)) 

519 return DataFrame(table, columns=param_header, index=xname) 

520 

521 

522def summary_params_2d(result, extras=None, endog_names=None, exog_names=None, 

523 title=None): 

524 '''create summary table of regression parameters with several equations 

525 

526 This allows interleaving of parameters with bse and/or tvalues 

527 

528 Parameters 

529 ---------- 

530 result : result instance 

531 the result instance with params and attributes in extras 

532 extras : list[str] 

533 additional attributes to add below a parameter row, e.g. bse or tvalues 

534 endog_names : {list[str], None} 

535 names for rows of the parameter array (multivariate endog) 

536 exog_names : {list[str], None} 

537 names for columns of the parameter array (exog) 

538 alpha : float 

539 level for confidence intervals, default 0.95 

540 title : None or string 

541 

542 Returns 

543 ------- 

544 tables : list of SimpleTable 

545 this contains a list of all seperate Subtables 

546 table_all : SimpleTable 

547 the merged table with results concatenated for each row of the parameter 

548 array 

549 

550 ''' 

551 if endog_names is None: 

552 # TODO: note the [1:] is specific to current MNLogit 

553 endog_names = ['endog_%d' % i for i in 

554 np.unique(result.model.endog)[1:]] 

555 if exog_names is None: 

556 exog_names = ['var%d' % i for i in range(len(result.params))] 

557 

558 # TODO: check formatting options with different values 

559 res_params = [[forg(item, prec=4) for item in row] for row in result.params] 

560 if extras: 

561 extras_list = [[['%10s' % ('(' + forg(v, prec=3).strip() + ')') 

562 for v in col] 

563 for col in getattr(result, what)] 

564 for what in extras 

565 ] 

566 data = lzip(res_params, *extras_list) 

567 data = [i for j in data for i in j] #flatten 

568 stubs = lzip(endog_names, *[['']*len(endog_names)]*len(extras)) 

569 stubs = [i for j in stubs for i in j] #flatten 

570 else: 

571 data = res_params 

572 stubs = endog_names 

573 

574 txt_fmt = copy.deepcopy(fmt_params) 

575 txt_fmt["data_fmts"] = ["%s"]*result.params.shape[1] 

576 

577 return SimpleTable(data, headers=exog_names, 

578 stubs=stubs, 

579 title=title, 

580 txt_fmt=txt_fmt) 

581 

582 

583def summary_params_2dflat(result, endog_names=None, exog_names=None, alpha=0.05, 

584 use_t=True, keep_headers=True, endog_cols=False): 

585 '''summary table for parameters that are 2d, e.g. multi-equation models 

586 

587 Parameters 

588 ---------- 

589 result : result instance 

590 the result instance with params, bse, tvalues and conf_int 

591 endog_names : {list[str], None} 

592 names for rows of the parameter array (multivariate endog) 

593 exog_names : {list[str], None} 

594 names for columns of the parameter array (exog) 

595 alpha : float 

596 level for confidence intervals, default 0.95 

597 use_t : bool 

598 indicator whether the p-values are based on the Student-t 

599 distribution (if True) or on the normal distribution (if False) 

600 keep_headers : bool 

601 If true (default), then sub-tables keep their headers. If false, then 

602 only the first headers are kept, the other headerse are blanked out 

603 endog_cols : bool 

604 If false (default) then params and other result statistics have 

605 equations by rows. If true, then equations are assumed to be in columns. 

606 Not implemented yet. 

607 

608 Returns 

609 ------- 

610 tables : list of SimpleTable 

611 this contains a list of all seperate Subtables 

612 table_all : SimpleTable 

613 the merged table with results concatenated for each row of the parameter 

614 array 

615 

616 ''' 

617 

618 res = result 

619 params = res.params 

620 if params.ndim == 2: # we've got multiple equations 

621 n_equ = params.shape[1] 

622 if len(endog_names) != params.shape[1]: 

623 raise ValueError('endog_names has wrong length') 

624 else: 

625 if len(endog_names) != len(params): 

626 raise ValueError('endog_names has wrong length') 

627 n_equ = 1 

628 

629 #VAR does not have conf_int 

630 #params = res.params.T # this is a convention for multi-eq models 

631 

632 # check that we have the right length of names 

633 if not isinstance(endog_names, list): 

634 # TODO: this might be specific to multinomial logit type, move? 

635 if endog_names is None: 

636 endog_basename = 'endog' 

637 else: 

638 endog_basename = endog_names 

639 # TODO: note, the [1:] is specific to current MNLogit 

640 endog_names = res.model.endog_names[1:] 

641 

642 tables = [] 

643 for eq in range(n_equ): 

644 restup = (res, res.params[:,eq], res.bse[:,eq], res.tvalues[:,eq], 

645 res.pvalues[:,eq], res.conf_int(alpha)[eq]) 

646 

647 skiph = False 

648 tble = summary_params(restup, yname=endog_names[eq], 

649 xname=exog_names, alpha=alpha, use_t=use_t, 

650 skip_header=skiph) 

651 

652 tables.append(tble) 

653 

654 # add titles, they will be moved to header lines in table_extend 

655 for i in range(len(endog_names)): 

656 tables[i].title = endog_names[i] 

657 

658 table_all = table_extend(tables, keep_headers=keep_headers) 

659 

660 return tables, table_all 

661 

662 

663def table_extend(tables, keep_headers=True): 

664 '''extend a list of SimpleTables, adding titles to header of subtables 

665 

666 This function returns the merged table as a deepcopy, in contrast to the 

667 SimpleTable extend method. 

668 

669 Parameters 

670 ---------- 

671 tables : list of SimpleTable instances 

672 keep_headers : bool 

673 If true, then all headers are kept. If falls, then the headers of 

674 subtables are blanked out. 

675 

676 Returns 

677 ------- 

678 table_all : SimpleTable 

679 merged tables as a single SimpleTable instance 

680 

681 ''' 

682 from copy import deepcopy 

683 for ii, t in enumerate(tables[:]): #[1:]: 

684 t = deepcopy(t) 

685 

686 #move title to first cell of header 

687 # TODO: check if we have multiline headers 

688 if t[0].datatype == 'header': 

689 t[0][0].data = t.title 

690 t[0][0]._datatype = None 

691 t[0][0].row = t[0][1].row 

692 if not keep_headers and (ii > 0): 

693 for c in t[0][1:]: 

694 c.data = '' 

695 

696 # add separating line and extend tables 

697 if ii == 0: 

698 table_all = t 

699 else: 

700 r1 = table_all[-1] 

701 r1.add_format('txt', row_dec_below='-') 

702 table_all.extend(t) 

703 

704 table_all.title = None 

705 return table_all 

706 

707 

708def summary_return(tables, return_fmt='text'): 

709 # join table parts then print 

710 if return_fmt == 'text': 

711 strdrop = lambda x: str(x).rsplit('\n',1)[0] 

712 # convert to string drop last line 

713 return '\n'.join(lmap(strdrop, tables[:-1]) + [str(tables[-1])]) 

714 elif return_fmt == 'tables': 

715 return tables 

716 elif return_fmt == 'csv': 

717 return '\n'.join(x.as_csv() for x in tables) 

718 elif return_fmt == 'latex': 

719 # TODO: insert \hline after updating SimpleTable 

720 table = copy.deepcopy(tables[0]) 

721 del table[-1] 

722 for part in tables[1:]: 

723 table.extend(part) 

724 return table.as_latex_tabular() 

725 elif return_fmt == 'html': 

726 return "\n".join(table.as_html() for table in tables) 

727 else: 

728 raise ValueError('available output formats are text, csv, latex, html') 

729 

730 

731class Summary(object): 

732 """ 

733 Result summary 

734 

735 Construction does not take any parameters. Tables and text can be added 

736 with the `add_` methods. 

737 

738 Attributes 

739 ---------- 

740 tables : list of tables 

741 Contains the list of SimpleTable instances, horizontally concatenated 

742 tables are not saved separately. 

743 extra_txt : str 

744 extra lines that are added to the text output, used for warnings 

745 and explanations. 

746 """ 

747 def __init__(self): 

748 self.tables = [] 

749 self.extra_txt = None 

750 

751 def __str__(self): 

752 return self.as_text() 

753 

754 def __repr__(self): 

755 return str(type(self)) + '\n"""\n' + self.__str__() + '\n"""' 

756 

757 def _repr_html_(self): 

758 '''Display as HTML in IPython notebook.''' 

759 return self.as_html() 

760 

761 def add_table_2cols(self, res, title=None, gleft=None, gright=None, 

762 yname=None, xname=None): 

763 """ 

764 Add a double table, 2 tables with one column merged horizontally 

765 

766 Parameters 

767 ---------- 

768 res : results instance 

769 some required information is directly taken from the result 

770 instance 

771 title : str, optional 

772 if None, then a default title is used. 

773 gleft : list[tuple], optional 

774 elements for the left table, tuples are (name, value) pairs 

775 If gleft is None, then a default table is created 

776 gright : list[tuple], optional 

777 elements for the right table, tuples are (name, value) pairs 

778 yname : str, optional 

779 optional name for the endogenous variable, default is "y" 

780 xname : list[str], optional 

781 optional names for the exogenous variables, default is "var_xx". 

782 Must match the number of parameters in the model. 

783 """ 

784 

785 table = summary_top(res, title=title, gleft=gleft, gright=gright, 

786 yname=yname, xname=xname) 

787 self.tables.append(table) 

788 

789 def add_table_params(self, res, yname=None, xname=None, alpha=.05, 

790 use_t=True): 

791 '''create and add a table for the parameter estimates 

792 

793 Parameters 

794 ---------- 

795 res : results instance 

796 some required information is directly taken from the result 

797 instance 

798 yname : {str, None} 

799 optional name for the endogenous variable, default is "y" 

800 xname : {list[str], None} 

801 optional names for the exogenous variables, default is "var_xx" 

802 alpha : float 

803 significance level for the confidence intervals 

804 use_t : bool 

805 indicator whether the p-values are based on the Student-t 

806 distribution (if True) or on the normal distribution (if False) 

807 

808 Returns 

809 ------- 

810 None : table is attached 

811 

812 ''' 

813 if res.params.ndim == 1: 

814 table = summary_params(res, yname=yname, xname=xname, alpha=alpha, 

815 use_t=use_t) 

816 elif res.params.ndim == 2: 

817 _, table = summary_params_2dflat(res, endog_names=yname, 

818 exog_names=xname, 

819 alpha=alpha, use_t=use_t) 

820 else: 

821 raise ValueError('params has to be 1d or 2d') 

822 self.tables.append(table) 

823 

824 def add_extra_txt(self, etext): 

825 '''add additional text that will be added at the end in text format 

826 

827 Parameters 

828 ---------- 

829 etext : list[str] 

830 string with lines that are added to the text output. 

831 

832 ''' 

833 self.extra_txt = '\n'.join(etext) 

834 

835 def as_text(self): 

836 '''return tables as string 

837 

838 Returns 

839 ------- 

840 txt : str 

841 summary tables and extra text as one string 

842 

843 ''' 

844 txt = summary_return(self.tables, return_fmt='text') 

845 if self.extra_txt is not None: 

846 txt = txt + '\n\n' + self.extra_txt 

847 return txt 

848 

849 def as_latex(self): 

850 '''return tables as string 

851 

852 Returns 

853 ------- 

854 latex : str 

855 summary tables and extra text as string of Latex 

856 

857 Notes 

858 ----- 

859 This currently merges tables with different number of columns. 

860 It is recommended to use `as_latex_tabular` directly on the individual 

861 tables. 

862 

863 ''' 

864 latex = summary_return(self.tables, return_fmt='latex') 

865 if self.extra_txt is not None: 

866 latex = latex + '\n\n' + self.extra_txt.replace('\n', ' \\newline\n ') 

867 return latex 

868 

869 def as_csv(self): 

870 '''return tables as string 

871 

872 Returns 

873 ------- 

874 csv : str 

875 concatenated summary tables in comma delimited format 

876 

877 ''' 

878 csv = summary_return(self.tables, return_fmt='csv') 

879 if self.extra_txt is not None: 

880 csv = csv + '\n\n' + self.extra_txt 

881 return csv 

882 

883 def as_html(self): 

884 '''return tables as string 

885 

886 Returns 

887 ------- 

888 html : str 

889 concatenated summary tables in HTML format 

890 

891 ''' 

892 html = summary_return(self.tables, return_fmt='html') 

893 if self.extra_txt is not None: 

894 html = html + '<br/><br/>' + self.extra_txt.replace('\n', '<br/>') 

895 return html