Python中的Fama Macbeth回归(Pandas或Statsmodels)
计量经济学背景 Fama-Macbeth回归指的是对面板数据进行回归的过程(其中有N个不同的个体,每个个体对应于多个时段T,例如天、月、年)。所以总共有N x T OB。请注意,如果面板数据不平衡,则没有问题 Fama-Macbeth回归是对每个时期进行跨部门的首次回归,即在给定时期t内将N个个体汇集在一起。对于t=1,…t,这样做。因此,总的来说,T回归是可行的。然后我们有一个时间序列的系数为每个独立变量。然后,我们可以使用系数的时间序列进行假设检验。通常我们将平均值作为每个自变量的最终系数。我们用t-stats检验显著性 我的问题 我的问题是在熊猫身上实现这一点。从pandas的源代码中,我注意到有一个过程叫做Python中的Fama Macbeth回归(Pandas或Statsmodels),python,r,pandas,statsmodels,Python,R,Pandas,Statsmodels,计量经济学背景 Fama-Macbeth回归指的是对面板数据进行回归的过程(其中有N个不同的个体,每个个体对应于多个时段T,例如天、月、年)。所以总共有N x T OB。请注意,如果面板数据不平衡,则没有问题 Fama-Macbeth回归是对每个时期进行跨部门的首次回归,即在给定时期t内将N个个体汇集在一起。对于t=1,…t,这样做。因此,总的来说,T回归是可行的。然后我们有一个时间序列的系数为每个独立变量。然后,我们可以使用系数的时间序列进行假设检验。通常我们将平均值作为每个自变量的最终系数。
fama_macbeth
。但是我找不到任何关于这个的文档
操作也可以通过groupby
轻松完成。目前我正在这样做:
def fmreg(data,formula):
return smf.ols(formula,data=data).fit().params[1]
res=df.groupby('date').apply(fmreg,'ret~var1')
这样一来,res
是一个序列,它由date
索引,序列的值是params[1]
,它是var1
的系数。但是现在我想有更多的自变量,我需要提取所有这些自变量的系数,但我不能计算出来。我试过这个
def fmreg(data,formula):
return smf.ols(formula,data=data).fit().params
res=df.groupby('date').apply(fmreg,'ret~var1+var2+var3')
这行不通。理想的结果是,res
是一个按date
索引的数据帧,数据帧的每一列都应该包含每个变量截距、var1
、var2
和var3
的系数
我还检查了statsmodels
,它们也没有这样的内置过程
是否有任何软件包可以生成出版物质量回归表?像Stata中的outreg2
和R中的texreg
?
谢谢你的帮助 反映Fama MacBeth截至2018年秋季图书馆状况的更新。fama_macbeth
功能已从pandas
中删除一段时间。那么你有什么选择
如果您使用的是python 3,那么可以在LinearModels中使用Fama MacBeth方法:
如果您正在使用Python2,或者只是不想使用LinearModels,那么最好的选择可能是使用自己的
例如,假设您将Fama French行业投资组合放在如下面板中(您还计算了一些变量,如过去的beta或过去的回报率,用作您的x变量):
Fama MacBeth主要涉及按月计算相同的横截面回归模型,因此您可以使用groupby
实现它。您可以创建一个函数,该函数采用数据帧
(它将来自groupby
)和patsy
公式;然后拟合模型并返回参数估计值。下面是如何实现它的基本版本(注意,这是最初的提问者几年前试图做的……不确定为什么它不起作用,尽管当时可能是statsmodels
结果对象方法params
没有返回pandas
Series
,因此需要将返回显式转换为Series
。)k在当前版本的pandas
,0.23.4)中的罚款:
然后计算平均值、平均值的标准误差和t检验(或任何你想要的统计数据)。如下所示:
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
提高速度
使用statsmodels
进行回归有很大的开销(特别是在您只需要估计系数的情况下)。如果您想要更高的效率,那么您可以从statsmodels
切换到numpy.linalg.lstsq
。编写一个新的函数来进行ols估计…如下所示(请注意,我没有做任何类似于检查这些矩阵的秩的事情…):
如果您仍在使用较旧版本的pandas
,则以下操作将起作用:
下面是一个使用pandas
中的fama_macbeth
函数的示例:
>>> 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
请注意,结构。fama_macbeth
函数期望y-var和x-var具有多个指数,其中日期为第一个变量,股票/公司/实体id为指数中的第二个变量:
>>> 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---------------------------------
请注意,只需打印fm
即可调用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---------------------------------
另外,请注意,fama_macbeth
函数会自动添加一个截距(与statsmodels
例程相反)。此外,x-var必须是一个dataframe
,因此如果只传递一列,则需要将其作为df['x']
传递
如果您不想拦截,则必须执行以下操作:
>>> fm = pd.fama_macbeth(y=df['y'],x=df[['x']],intercept=False)
编辑:新库
已存在更新的库,可通过以下命令安装该库:
pip install finance-byu
此处的文档:
新库包括Fama Macbeth回归实现和一个有助于报告结果的Regtable
类
文档中的本页概述了Fama Macbeth的功能:
有一个实现非常类似于Karl D.的上面的实现,使用numpy
的线性代数函数,一个利用joblib
进行并行化的实现,当数据中存在大量时间段时,可以提高性能,另一个利用numba
进行优化的实现at在小型数据集上削减了一个数量级
以下是一个小的模拟数据集示例,如文档所示:
>>> from finance_byu.fama_macbeth import fama_macbeth, fama_macbeth_parallel, fm_summary, fama_macbeth_numba
>>> import pandas as pd
>>> import time
>>> import numpy as np
>>>
>>> n_jobs = 5
>>> n_firms = 1.0e2
>>> n_periods = 1.0e2
>>>
>>> def firm(fid):
>>> f = np.random.random((int(n_periods),4))
>>> f = pd.DataFrame(f)
>>> f['period'] = f.index
>>> f['firmid'] = fid
>>> return f
>>> df = [firm(i) for i in range(int(n_firms))]
>>> df = pd.concat(df).rename(columns={0:'ret',1:'exmkt',2:'smb',3:'hml'})
>>> df.head()
ret exmkt smb hml period firmid
0 0.766593 0.002390 0.496230 0.992345 0 0
1 0.346250 0.509880 0.083644 0.732374 1 0
2 0.787731 0.204211 0.705075 0.313182 2 0
3 0.904969 0.338722 0.437298 0.669285 3 0
4 0.121908 0.827623 0.319610 0.455530 4 0
>>> result = fama_macbeth(df,'period','ret',['exmkt','smb','hml'],intercept=True)
>>> result.head()
intercept exmkt smb hml
period
0 0.655784 -0.160938 -0.109336 0.028015
1 0.455177 0.033941 0.085344 0.013814
2 0.410705 -0.084130 0.218568 0.016897
3 0.410537 0.010719 0.208912 0.001029
4 0.439061 0.046104 -0.084381 0.199775
>>> fm_summary(result)
mean std_error tstat
intercept 0.506834 0.008793 57.643021
exmkt 0.004750 0.009828 0.483269
smb -0.012702 0.010842 -1.171530
hml 0.004276 0.010530 0.406119
>>> %timeit fama_macbeth(df,'period','ret',['exmkt','smb','hml'],intercept=True)
123 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 10 loops each
>>> %timeit fama_macbeth_parallel(df,'period','ret',['exmkt','smb','hml'],intercept=True,n_jobs=n_jobs,memmap=False)
146 ms ± 16.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit fama_macbeth_numba(df,'period','ret',['exmkt','smb','hml'],intercept=True)
5.04 ms ± 5.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
注意:关闭memmap有助于公平比较,而不会在每次运行时生成新数据
>>> fm = pd.fama_macbeth(y=df['y'],x=df[['x']],intercept=False)
pip install finance-byu
>>> from finance_byu.fama_macbeth import fama_macbeth, fama_macbeth_parallel, fm_summary, fama_macbeth_numba
>>> import pandas as pd
>>> import time
>>> import numpy as np
>>>
>>> n_jobs = 5
>>> n_firms = 1.0e2
>>> n_periods = 1.0e2
>>>
>>> def firm(fid):
>>> f = np.random.random((int(n_periods),4))
>>> f = pd.DataFrame(f)
>>> f['period'] = f.index
>>> f['firmid'] = fid
>>> return f
>>> df = [firm(i) for i in range(int(n_firms))]
>>> df = pd.concat(df).rename(columns={0:'ret',1:'exmkt',2:'smb',3:'hml'})
>>> df.head()
ret exmkt smb hml period firmid
0 0.766593 0.002390 0.496230 0.992345 0 0
1 0.346250 0.509880 0.083644 0.732374 1 0
2 0.787731 0.204211 0.705075 0.313182 2 0
3 0.904969 0.338722 0.437298 0.669285 3 0
4 0.121908 0.827623 0.319610 0.455530 4 0
>>> result = fama_macbeth(df,'period','ret',['exmkt','smb','hml'],intercept=True)
>>> result.head()
intercept exmkt smb hml
period
0 0.655784 -0.160938 -0.109336 0.028015
1 0.455177 0.033941 0.085344 0.013814
2 0.410705 -0.084130 0.218568 0.016897
3 0.410537 0.010719 0.208912 0.001029
4 0.439061 0.046104 -0.084381 0.199775
>>> fm_summary(result)
mean std_error tstat
intercept 0.506834 0.008793 57.643021
exmkt 0.004750 0.009828 0.483269
smb -0.012702 0.010842 -1.171530
hml 0.004276 0.010530 0.406119
>>> %timeit fama_macbeth(df,'period','ret',['exmkt','smb','hml'],intercept=True)
123 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 10 loops each
>>> %timeit fama_macbeth_parallel(df,'period','ret',['exmkt','smb','hml'],intercept=True,n_jobs=n_jobs,memmap=False)
146 ms ± 16.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit fama_macbeth_numba(df,'period','ret',['exmkt','smb','hml'],intercept=True)
5.04 ms ± 5.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> from finance_byu.regtables import Regtable
>>> import pandas as pd
>>> import statsmodels.formula.api as smf
>>> import numpy as np
>>>
>>>
>>> nobs = 1000
>>> df = pd.DataFrame(np.random.random((nobs,3))).rename(columns={0:'age',1:'bmi',2:'hincome'})
>>> df['age'] = df['age']*100
>>> df['bmi'] = df['bmi']*30
>>> df['hincome'] = df['hincome']*100000
>>> df['hincome'] = pd.qcut(df['hincome'],16,labels=False)
>>> df['rich'] = df['hincome'] > 13
>>> df['gender'] = np.random.choice(['M','F'],nobs)
>>> df['race'] = np.random.choice(['W','B','H','O'],nobs)
>>>
>>> regformulas = ['bmi ~ age',
>>> 'bmi ~ np.log(age)',
>>> 'bmi ~ C(gender) + np.log(age)',
>>> 'bmi ~ C(gender) + C(race) + np.log(age)',
>>> 'bmi ~ C(gender) + rich + C(gender)*rich + C(race) + np.log(age)',
>>> 'bmi ~ -1 + np.log(age)',
>>> 'bmi ~ -1 + C(race) + np.log(age)']
>>> reg = [smf.ols(f,df).fit() for f in regformulas]
>>> tbl = Regtable(reg)
>>> tbl.render()
>>> df2 = pd.DataFrame(np.random.random((nobs,10)))
>>> df2.columns = ['t0_vw','t4_vw','et_vw','t0_ew','t4_ew','et_ew','mktrf','smb','hml','umd']
>>> regformulas2 = ['t0_vw ~ mktrf',
>>> 't0_vw ~ mktrf + smb + hml',
>>> 't0_vw ~ mktrf + smb + hml + umd',
>>> 't4_vw ~ mktrf',
>>> 't4_vw ~ mktrf + smb + hml',
>>> 't4_vw ~ mktrf + smb + hml + umd',
>>> 'et_vw ~ mktrf',
>>> 'et_vw ~ mktrf + smb + hml',
>>> 'et_vw ~ mktrf + smb + hml + umd',
>>> 't0_ew ~ mktrf',
>>> 't0_ew ~ mktrf + smb + hml',
>>> 't0_ew ~ mktrf + smb + hml + umd',
>>> 't4_ew ~ mktrf',
>>> 't4_ew ~ mktrf + smb + hml',
>>> 't4_ew ~ mktrf + smb + hml + umd',
>>> 'et_ew ~ mktrf',
>>> 'et_ew ~ mktrf + smb + hml',
>>> 'et_ew ~ mktrf + smb + hml + umd'
>>> ]
>>> regnames = ['Small VW','','',
>>> 'Large VW','','',
>>> 'Spread VW','','',
>>> 'Small EW','','',
>>> 'Large EW','','',
>>> 'Spread EW','',''
>>> ]
>>> reg2 = [smf.ols(f,df2).fit() for f in regformulas2]
>>>
>>> tbl2 = Regtable(reg2,orientation='horizontal',regnames=regnames,sig='coeff',intercept_name='alpha',nobs=False,rsq=False,stat='se')
>>> tbl2.render()
tbl.to_latex()