#######################################################
# 9 Adding Models and Methods to Zelig 77
# 9.1 Making the Model Compatible with Zelig. . . . . . . . 78
#######################################################
9.1 使模型与Zelig兼容
你可以开发一个模型,编写适合模型的函数,在Zelig框架内检测它,无需Zelig团队的介入。(当然,我们很高兴来回答对于改进的任何问题或建议)。
Zelig的模块性依赖于R的两个程序设计约定:
1. 封装,它从R函数传递参量给其它R函数或外部函数的调用(如C, C++, 或 Fortran函数)
2. 类方法,告诉泛型函数怎么处理给定类的对象。
为R泛型函数指定方法采取的一般格式: method.class(),method是执行的类属过程的名,class是对象的类。你可以定义,例如, 以summary.contrib()来概述你的模型输出。注意S4类,泛型函数名不再有method.class(),只要用户调用method()就可以。
使用zelig()执行操作
Zelig对加入新模型的应用有独特方法,让模型编写者不用对任何zelig()函数修改,就可在Zelig的框架中来检测他们的模型。
使用zelig2contrib()封装函数(此处contrib是你的新模型名),zelig2contrib()重新定义对zelig()的输入来执行对你的函数contrib()所需要的输入。例如,如果你键入
zelig(..., model = "normal.regression")
zelig()搜索zelig2normal.regression()封装程序,在任意环境中进行(在已经载入包或你的工作空间)。如果封装程序存在,那么zelig()就运行这个模型。
如果你有一个先前存在的模型,编写一个zelig2contrib()函数是很容易的。让我们假定你的模型是contrib(),取下列参量:formula、data、 weights和 start。相反,在zelig() 函数中,仅取formula、data、model和by参量。你可以使用...来从zelig()中传递添加的参量给zelig2contrib(),还有 <- NULL来略去你不需要的元素。从8.1.2部分连续性正态回归示例,让formula、model和data为对zelig()的输入,M是了集的数目,而 ... 是在zelig()调用中没有定义的添加参量,但是它们传递给 normal.regression()。
zelig2normal.regression <-function(formula, model, data, M, ...) { <br />
mf <-match.call(expand.dots = TRUE) # [1] <br />
mf$model <-mf$M <-NULL # [2] <br />
mf[[1]] <-as.name("normal.regression") # [3] <br />
as.call(mf) # [4] <br />
}
上面括号内的数字对应下面的注释:
1. 在zelig2normal.regression()中建立一个参量列表的方式来建立一个调用(一个用来解析的表达式),包括用
normal.regression()提取的参量,但不是用zelig()。全部封装包必需取同样的标准化参量 (formula, model, data, and M),这些参量可能用在封装函数中来处理zelig()调入到normal.regression()调用中。添加参量给normal.regression(),如此,当start.val从zelig()中
以隐蔽方式传递给 ... 运算符。
2. 从调用mf对象中删除额外信息。在这个封装中,model和M没有被使用。在其它模型中,它们被用来进行处理更多的调用,也是包括在全部封装程序的标准输出中。
3. 对在normal.regression()中用来解析的函数名的调用(现在当前的是zelig2normal.regression)的第一个元素进行再赋值。
4. 返回调用给zelig(),它将为每个多重推理数据集或简单数据所解析调用,每一个子集用by来定义。
如果你使用S4类方法来表示你的模型,比如说mymodel,在zelig.default()内,Zelig的内部函数,建立.ZeligS4(),在两个添加槽(slots)的全局环境中,自动建立一个新S4类调用ZeligS4mymodel。这些包括保存模型名的zelig,如果save.data=TRUE和其它项为空,那还包括保存数据框的zelig.data。这些名字取自原始调用。新的输出继承原始的类mymodel,因此全部的泛型函数与mymodel联系在一起来运行。如果你想查看一个示例,查看该模型的应用,使用VGAM程序包,如多元概率。
使用setx()来执行操作
在setx()情况下,大多数模型将用setx.default(),它依次依敕于普通R函数model.matrix()。对这种程序的运行,你的输出列表必需包括:
-terms 用model.frame()建立或手动建立;
-formula 用户输入的公式对象;
-xlevels 它定义了解释变量的各层;
-contrasts 一个可选元素,它用在解释变量中定义因子变量的的类型。更多信息见help(contrasts)。
如果你的模型输出不能用setx.default()来执行,你必需编定你自己的setx.contrib()函数。例如,适合于多重推理的数据集有来自类方法MI的zelig()输出。特殊的setx.MI()在zelig()输出对象之前预封装程序,传递恰当的参量来setx.default()。
与sim()的兼容性
模拟“感兴趣的统计量”是解释模型结果的一个整体部分。使用这项功能来内嵌入Zelig sim()程序,你需要提供模拟参数的方式(调用param()函数),对于计算或图示来自模拟参数(调用qi()函数)的“感兴趣的统计量”。
模拟参数
是否你选择使用默认方法或为模拟参数写入一个指定模型方法,这些函数要求同样的三个输入:
-object 估计模型或zelig()输出。
-num 模拟的数量
-bootstrap 选择TRUE或FALSE
来自param()的输出将是下列之一
-如果bootstrap=FALSE(默认),一个行矩阵对应模拟和列对应模型参数。任意辅助参数将包括在输出矩阵中。
-如果bootstrap=TRUE,含有全部模型参数的向量,包括辅助参数。
有两种方式来模拟参数:
1. 使用param.default()函数来提取从模型中提取参数,如果bootstrapping没有选择,模拟系数使用渐进的正态近似。
(a) coef(): 提取系数。从上面的连续正态回归样本,恰当的coef.normal()函数是容易的:
coef.normal <-function(object)
object$coefficients
(b) vcov(): 提取变量-协变量矩阵。再次从上面的连续性泊松样本:
vcov.normal <-function(object)
object$variance
2. 另一方法,你可以编写你自己的param.contrib()函数。当下列情况中这是恰当的:
(a) 你的模型有辅助参数,如在正态分布情况下有σ。
(b) 你的模型对系数或变量-协变量矩阵执行某些类型的校正系数,而它不能在coef.contrib()或vcov.contrib()函数中执行。
(c) 你的模型对对数似然不依赖于渐近近似。对于Bayesian Markov-chain monte carlo模型,例如,param.contrib()函数
(param.MCMCzelig()在这种情况下)仅仅提取模型参数来模拟适合模型的函数。
连续性正态样本
param.normal <-function(object, num = NULL, bootstrap = FALSE,terms = NULL) {<br />
if (!bootstrap) { <br />
par <-mvrnorm(num, mu = coef(object), Sigma = vcov(object)) <br />
Beta <-parse.par(par, terms = terms, eqn = "mu") <br />
sigma2 <-exp(parse.par(par, terms = terms, eqn = "sigma2")) <br />
res <-cbind(Beta, sigma2)<br />
}<br />
else {<br />
par <-coef(object)<br />
Beta <-parse.par(par, terms = terms, eqn = "mu")<br />
sigma2 <-exp(parse.par(par, terms = terms, eqn = "sigma2"))<br />
res <-c(coef, sigma2)<br />
}<br />
res<br />
}
计算“感兴趣的统计量”
对于计算来自模拟参数的“感兴趣的统计量”,所有模型要求指定模型方法。对归类的模型,恰当的qi()函数是qi.contrib()。在没有最小值时,这个函数将计算下列“感兴趣的统计量”:
-ev: 期望值,计算来自对期望值的分析解,作为函数的的系统成分和辅助参数。
-pr: 预测值,从预测值定义的分布提取。对你的函数,如果R没有内部的随机数生成器,你可以从均匀分布随机抽取,使用CDF反置方法来计算预测值。
-fd: 在期望值中的首要差异(均数差),通过提取预期值来计算从给定x1的期望值来考虑指定的x。
-ate.ev: 使用期望值ev计算平均处理效应。简易方式,y -ev,对每个观测值全局模拟的平均。
-ate.pr: 使用预测值pr计算平均处理效应。简易方式,y -pr, 对每个观测值全局模拟的平均。
qi()函数要求的参量:
-object: zelig输出对象
-par: 模拟参数
-x: 解释变量的矩阵(用setx()建立)
-x1: 对于首要差异(均数差,也是用setx()建立)备择值的可选项矩阵。如果首要差异不合适你的模型,如果x1不是NULL,你应该放入一条warning() or stop()。
-y: 因变量的矩阵或向量的可选项(计算平均处理效应)。如果平均处理效应不适合于你的模型,如果在setx()步骤中的条件预测被选择了,你应当放入一条warning() or stop() 。
从上面的连续性正态回归样本,适合的qi.normal()函数如下:
qi.normal <-function(object, par, x, x1 = NULL, y = NULL) { <br />
Beta <-parse.par(par, eqn = "mu") # [1] <br />
sigma2 <-parse.par(par, eqn = "sigma2") # [2] <br />
ev <-Beta %*% t(x) # [3a] <br />
pr <-matrix(NA, ncol = ncol(ev), nrow = nrow(ev)) <br />
for (i in 1:ncol(ev)) <br />
pr[,i] <-rnorm(length(ev[,i]), mean = ev[,i], # [4] <br />
sigma = sd(sigma2[i]))<br />
qi <-list(ev = ev, pr = pr)<br />
qi.name <-list(ev = "Expected Values: E(Y|X)",<br />
pr = "Predicted Values: Y|X") <br />
if (!is.null(x1)){<br />
ev1 <-par %*% t(x1) # [3b]<br />
qi$fd <-ev1 -ev<br />
qi.name$fd <-"First Differences in Expected Values: E(Y|X1)-E(Y|X)"<br />
} <br />
<br />
if (!is.null(y)) {<br />
yvar <-matrix(rep(y, nrow(par)), nrow = nrow(par), byrow = TRUE)<br />
tmp.ev <-yvar -qi$ev<br />
tmp.pr <-yvar -qi$pr<br />
qi$ate.ev <-matrix(apply(tmp.ev, 1, mean), nrow = nrow(par))<br />
qi$ate.pr <-matrix(apply(tmp.pr, 1, mean), nrow = nrow(par))<br />
qi.name$ate.ev <-"Average Treatment Effect: Y -EV"<br />
qi.name$ate.pr <-"Average Treatment Effect: Y -PR"<br />
} <br />
list(qi=qi, qi.name=qi.name) <br />
}
上面有五行有编码注释。以下面的四种方式来改变这五行,你可以编写qi()函数来几乎对任何模型都适合:
1. 用你的系统参数的下标名来提取任意系统参数(系统参数在describe.mymodel()定义)。
2. 用他们此处的名字下标名来提取任意辅助参数(在describe.mymodel()定义)
3. 使用反置连接函数和η=Xβ计算期望值。(对于正态模型,这是线性的)。在注释[3a]和[3b],你需要在这两个位置进行改变。
4. 用函数从你的模型的随机成分中随机抽取来替换rnorm()。
<未完待续>