Econometric Backgroud
Fama Macbeth regression refers to a procedure to run regression for panel data (where there are N different individuals and each individual corresponds to multiple periods T, e.g. day, months,year). So in total there are N x T obs. Notice it's OK if the panel data is not balanced.
The Fama Macbeth regression is to first run regression for each period cross-sectinally, i.e. pool N individuals together in a given period t. And do this for t=1,...T. So in total T regressions are run. Then we have a time series of coefficients for each independent variable. Then we can perform hypothesis test using the time series of coefficients. Usually we take the average as the final coefficients of each independent variable. And we use t-stats to test significance.
My Problem
My problem is to implement this in pandas. From the source code of pandas, I noticed there is a procedure called fama_macbeth
. But I can't find any documentation about this.
The operation can be easily done through groupby
as well. Currently I am doing this:
def fmreg(data,formula):
return smf.ols(formula,data=data).fit().params[1]
res=df.groupby('date').apply(fmreg,'ret~var1')
This works, res
is a Series which is indexed by date
and the values of Series are params[1]
, which is the coefficient of var1
. But now I want to have more independent variables, I need to extract the coefficients of all these independent variables, but I can't figure that out. I tried this
def fmreg(data,formula):
return smf.ols(formula,data=data).fit().params
res=df.groupby('date').apply(fmreg,'ret~var1+var2+var3')
This won't work. The desired result is that res
is a dataframe indexed by date
, and each column of the dataframe should contain the coefficients of each variable intercept
, var1
, var2
and var3
.
I also checked with statsmodels
, they don't have such built-in procedure as well.
And is there any package that can produce publication-quality regression tables? Like outreg2
in Stata and texreg
in R?
Thanks for your help!
An update to reflect the library situation for Fama-MacBeth as of Fall 2018. The fama_macbeth
function has been removed from pandas
for a while now. So what are your options?
If you're using python 3, then you can use the Fama-MacBeth method in LinearModels: https://github.com/bashtage/linearmodels/blob/master/linearmodels/panel/model.py
If you're using python 2 or just don't want to use LinearModels, then probably your best option is to roll you own.
For example, suppose you have the Fama-French industry portfolios in a panel like the following (you've also computed some variables like past beta or past returns to use as your x-variables):
In [1]: import pandas as pd
import numpy as np
import statsmodels.formula.api as smf
In [4]: df = pd.read_csv('industry.csv',parse_dates=['caldt'])
df.query("caldt == '1995-07-01'")
In [5]: Out[5]:
industry caldt ret beta r12to2 r36to13
18432 Aero 1995-07-01 6.26 0.9696 0.2755 0.3466
18433 Agric 1995-07-01 3.37 1.0412 0.1260 0.0581
18434 Autos 1995-07-01 2.42 1.0274 0.0293 0.2902
18435 Banks 1995-07-01 4.82 1.4985 0.1659 0.2951
Fama-MacBeth primarily involves computing the same cross-sectional regression model month by month, so you can implement it using a groupby
. You can create a function that takes a dataframe
(it will come from the groupby
) and a patsy
formula; it then fits the model and returns the parameter estimates. Here is a barebones version of how you could implement it (note this is what the original questioner tried to do a few years ago ... not sure why it didn't work although it's possible back then statsmodels
result object method params
wasn't returning a pandas
Series
so the return needed to be converted to a Series
explicitly ... it does work fine in the current version of pandas
, 0.23.4):
def ols_coef(x,formula):
return smf.ols(formula,data=x).fit().params
In [9]: gamma = (df.groupby('caldt')
.apply(ols_coef,'ret ~ 1 + beta + r12to2 + r36to13'))
gamma.head()
In [10]: Out[10]:
Intercept beta r12to2 r36to13
caldt
1963-07-01 -1.497012 -0.765721 4.379128 -1.918083
1963-08-01 11.144169 -6.506291 5.961584 -2.598048
1963-09-01 -2.330966 -0.741550 10.508617 -4.377293
1963-10-01 0.441941 1.127567 5.478114 -2.057173
1963-11-01 3.380485 -4.792643 3.660940 -1.210426
Then just compute the mean, standard error on the mean, and a t-test (or whatever statistics you want). Something like the following:
def fm_summary(p):
s = p.describe().T
s['std_error'] = s['std']/np.sqrt(s['count'])
s['tstat'] = s['mean']/s['std_error']
return s[['mean','std_error','tstat']]
In [12]: fm_summary(gamma)
Out[12]:
mean std_error tstat
Intercept 0.754904 0.177291 4.258000
beta -0.012176 0.202629 -0.060092
r12to2 1.794548 0.356069 5.039896
r36to13 0.237873 0.186680 1.274230
Improving Speed
Using statsmodels
for the regressions has significant overhead (particularly given you only need the estimated coefficients). If you want better efficiency, then you could switch from statsmodels
to numpy.linalg.lstsq
. Write a new function that does the ols estimation ... something like the following (notice I'm not doing anything like checking the rank of these matrices ...):
def ols_np(data,yvar,xvar):
gamma,_,_,_ = np.linalg.lstsq(data[xvar],data[yvar],rcond=None)
return pd.Series(gamma)
And if you're still using an older version of pandas
, the following will work:
Here is an example of using the fama_macbeth
function in pandas
:
>>> df
y x
date id
2012-01-01 1 0.1 0.4
2 0.3 0.6
3 0.4 0.2
4 0.0 1.2
2012-02-01 1 0.2 0.7
2 0.4 0.5
3 0.2 0.1
4 0.1 0.0
2012-03-01 1 0.4 0.8
2 0.6 0.1
3 0.7 0.6
4 0.4 -0.1
Notice, the structure. The fama_macbeth
function expects the y-var and x-vars to have a multi-index with date as the first variable and the stock/firm/entity id as the second variable in the index:
>>> fm = pd.fama_macbeth(y=df['y'],x=df[['x']])
>>> fm
----------------------Summary of Fama-MacBeth Analysis-------------------------
Formula: Y ~ x + intercept
# betas : 3
----------------------Summary of Estimated Coefficients------------------------
Variable Beta Std Err t-stat CI 2.5% CI 97.5%
(x) -0.0227 0.1276 -0.18 -0.2728 0.2273
(intercept) 0.3531 0.0842 4.19 0.1881 0.5181
--------------------------------End of Summary---------------------------------
Note that just printing fm
calls fm.summary
>>> fm.summary
----------------------Summary of Fama-MacBeth Analysis-------------------------
Formula: Y ~ x + intercept
# betas : 3
----------------------Summary of Estimated Coefficients------------------------
Variable Beta Std Err t-stat CI 2.5% CI 97.5%
(x) -0.0227 0.1276 -0.18 -0.2728 0.2273
(intercept) 0.3531 0.0842 4.19 0.1881 0.5181
--------------------------------End of Summary---------------------------------
Also, note the fama_macbeth
function automatically adds an intercept (as opposed to statsmodels
routines). Also the x-var has to be a dataframe
so if you pass just one column you need to pass it as df[['x']]
.
If you don't want an intercept you have to do:
>>> fm = pd.fama_macbeth(y=df['y'],x=df[['x']],intercept=False)