Any Python Library Produces Publication Style Regression Tables
Asked Answered
E

3

24

I've been using Python for regression analysis. After getting the regression results, I need to summarize all the results into one single table and convert them to LaTex (for publication). Is there any package that does this in Python? Something like estout in Stata that gives the following table:

enter image description here

Eximious answered 10/5, 2014 at 1:49 Comment(3)
Any modern update to this question? There's summary2 which is still quite lacking.Nibble
@MatthewGunn This function is far from what you can do with Stata's estout package (or R's package). So what I ended up doing for my workflow is to call a user-defined function in Python that calls Stata to run a do-file in Terminal that run all the regressions and output the table.Eximious
Please review Why not upload images of code/errors when asking a question? (e.g., "Images should only be used to illustrate problems that can't be made clear in any other way, such as to provide screenshots of a user interface.") and do the right thing. Thanks in advance.Nudicaul
E
39

Well, there is summary_col in statsmodels; it doesn't have all the bells and whistles of estout, but it does have the basic functionality you are looking for (including export to LaTeX):

import statsmodels.api as sm
from statsmodels.iolib.summary2 import summary_col

p['const'] = 1
reg0 = sm.OLS(p['p0'],p[['const','exmkt','smb','hml']]).fit()
reg1 = sm.OLS(p['p2'],p[['const','exmkt','smb','hml']]).fit()
reg2 = sm.OLS(p['p4'],p[['const','exmkt','smb','hml']]).fit()

print summary_col([reg0,reg1,reg2],stars=True,float_format='%0.2f')

===============================
         p0       p2      p4   
-------------------------------
const -1.03*** -0.01   0.62*** 
      (0.11)   (0.04)  (0.07)  
exmkt 1.28***  0.97*** 0.98*** 
       (0.02)   (0.01)  (0.01)  
smb   0.37***  0.28*** -0.14***
      (0.03)   (0.01)  (0.02)  
hml   0.77***  0.46*** 0.69*** 
      (0.04)   (0.01)  (0.02)  
===============================
Standard errors in parentheses.
* p<.1, ** p<.05, ***p<.01

Or here is a version where I add R-Squared and the number of observations:

print summary_col([reg0,reg1,reg2],stars=True,float_format='%0.2f',
                  info_dict={'N':lambda x: "{0:d}".format(int(x.nobs)),
                             'R2':lambda x: "{:.2f}".format(x.rsquared)})

===============================
         p0       p2      p4   
-------------------------------
const -1.03*** -0.01   0.62*** 
      (0.11)   (0.04)  (0.07)  
exmkt 1.28***  0.97*** 0.98*** 
      (0.02)   (0.01)  (0.01)  
smb   0.37***  0.28*** -0.14***
      (0.03)   (0.01)  (0.02)  
hml   0.77***  0.46*** 0.69*** 
      (0.04)   (0.01)  (0.02)  
R2    0.86     0.95    0.88    
N     1044     1044    1044    
===============================
Standard errors in parentheses.
* p<.1, ** p<.05, ***p<.01

Another example, this time showing the use of the model_names option and regressions where the independent variables vary:

reg3 = sm.OLS(p['p4'],p[['const','exmkt']]).fit()
reg4 = sm.OLS(p['p4'],p[['const','exmkt','smb','hml']]).fit()
reg5 = sm.OLS(p['p4'],p[['const','exmkt','smb','hml','umd']]).fit()

print summary_col([reg3,reg4,reg5],stars=True,float_format='%0.2f',
                  model_names=['p4\n(0)','p4\n(1)','p4\n(2)'],
                  info_dict={'N':lambda x: "{0:d}".format(int(x.nobs)),
                             'R2':lambda x: "{:.2f}".format(x.rsquared)})

==============================
         p4      p4       p4  
        (0)     (1)      (2)  
------------------------------
const 0.66*** 0.62***  0.15***
      (0.10)  (0.07)   (0.04) 
exmkt 1.10*** 0.98***  1.08***
      (0.02)  (0.01)   (0.01) 
hml           0.69***  0.72***
              (0.02)   (0.01) 
smb           -0.14*** 0.07***
              (0.02)   (0.01) 
umd                    0.46***
                       (0.01) 
R2    0.78    0.88     0.96   
N     1044    1044     1044   
==============================
Standard errors in
parentheses.
* p<.1, ** p<.05, ***p<.01

To export to LaTeX use the as_latex method.

I could be wrong but I don't think an option for t-stats instead of standard errors (like in your example) is implemented.

Ergosterol answered 10/5, 2014 at 2:28 Comment(8)
Could you please let me know how to find this command on Google search? I spent half an hour looking for a command but could not find it. THanks!Eximious
I don't know ... I forget how I ran across it; Here is a link to the source file on github; it has a docstring: summary2.pyErgosterol
Great. It would be helpful for others if you include regressions with different explanatory variables.Eximious
Sure, added an example.Ergosterol
@KarlD. Great answer, it would be nice if pandas or statsmodels have a texreg or outreg2 like package for this. Academics use these a lot.Galicia
Is there a way to restrict which of the regressors will be included? I want to use the same approach as in the answer, but have many regressors whose coefficients I do not want to present. Didn't find in the documentation a way to do itBedford
@Bedford Not as near as I can tell. You could modify the function to do that pretty easily I think or post process the table it creates but it doesn't do it by default.Ergosterol
Yep, post processing is easy. It's disappointing though that python is so behind on this kind of stuffBedford
B
5

One alternative is Stargazer. To get started quickly, refer to the set of demo tables that Stargazer can produce.

Related posts include: post1 and post2.

Billon answered 16/9, 2020 at 23:45 Comment(0)
P
3

In addition to Karl D.'s great answer with the Statsmodels as_latex method, you can also check out the pystout package.

!pip install pystout
import pandas as pd
from sklearn.datasets import load_iris
import statsmodels.api as sm
from pystout import pystout
data = load_iris()
df = pd.DataFrame(data = data.data, columns = data.feature_names)
df.columns = ['s_len', 's_w', 'p_len', 'p_w']

y = df['p_w']

X = df[['s_len', 's_w', 'p_len']]
m1 = sm.OLS(y, X).fit()

X = df[['s_len', 's_w']]
m2 =  sm.OLS(y, X).fit()

X = df[['s_len']]
m3 =  sm.OLS(y, X).fit()

pystout(models=[m1, m2, m3],
        file='test_table.tex',
        addnotes=['Note above','Note below'],
        digits=2,
        endog_names=['petal width', 'petal width', 'petal width'],
        varlabels={'const':'Constant',
                   'displacement':'Disp','mpg':'MPG'},
        mgroups={'First Group':[1,2],'Second Group':3},
        modstat={'nobs':'Obs','rsquared_adj':'Adj. R\sym{2}','fvalue':'F-stat'}
        )

Don't spend hours like me trying to print out pystout. The LaTeX output is directly written on the .tex document you pass for file.

When compiled, the output looks like this:

Some rendered LaTeX

Planksheer answered 14/2, 2022 at 5:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.