我在尝试使用 mlogit::mlogit()
来拟合条件逻辑回归模型,应用于 MOB 算法 partykit::mob()
生成的树的末端叶子节点。显然,无法直接使用 partykit::mob()
函数来实现(以下是我的尝试)。然而,我找到了 LORET 算法,但我找不到任何带有示例的文档,因此我试图从源代码中猜测需要哪个函数,但不幸的是,我无法让它工作。
你知道(1)在哪里可以找到 LORET 库的示例,以及(2)是否有可能将 partykit:mob()
函数与 mlogit::mlogit
一起使用吗?提前感谢。
示例数据
为了说明,请考虑以下数据。数据来自 5 个个体(id_ind
),他们在 3 个选择中进行选择(altern
)。每个个体选择了三次,因此我们有 15 个选择情况(id_choice
)。每个选择由两个通用属性(x1
和 x2
)表示,选择记录在 y
中(如果选择为 1
,否则为 0
)。最后,z1
是一个候选分区变量。
df <- read.table(header = TRUE, text = "id_ind id_choice altern x1 x2 y1 1 1 1 1.586788801 0.11887832 12 1 1 2 -0.937965347 1.15742493 03 1 1 3 -0.511504401 -1.90667519 04 1 2 1 1.079365680 -0.37267925 05 1 2 2 -0.009203032 1.65150370 16 1 2 3 0.870474033 -0.82558651 07 1 3 1 -0.638604013 -0.09459502 08 1 3 2 -0.071679538 1.56879334 09 1 3 3 0.398263302 1.45735788 110 2 4 1 0.291413453 -0.09107974 011 2 4 2 1.632831160 0.92925495 012 2 4 3 -1.193272276 0.77092623 113 2 5 1 1.967624379 -0.16373709 114 2 5 2 -0.479859282 -0.67042130 015 2 5 3 1.109780885 0.60348187 016 2 6 1 -0.025834772 -0.44004183 017 2 6 2 -1.255129594 1.10928280 018 2 6 3 1.309493274 1.84247199 119 3 7 1 1.593558740 -0.08952151 020 3 7 2 1.778701074 1.44483791 121 3 7 3 0.643191170 -0.24761157 022 3 8 1 1.738820924 -0.96793288 023 3 8 2 -1.151429915 -0.08581901 024 3 8 3 0.606695064 1.06524268 125 3 9 1 0.673866953 -0.26136206 026 3 9 2 1.176959443 0.85005871 127 3 9 3 -1.568225496 -0.40002252 028 4 10 1 0.516456176 -1.02081089 129 4 10 2 -1.752854918 -1.71728381 030 4 10 3 -1.176101700 -1.60213536 031 4 11 1 -1.497779616 -1.66301234 032 4 11 2 -0.931117325 1.50128532 133 4 11 3 -0.455543630 -0.64370825 034 4 12 1 0.894843784 -0.69859139 035 4 12 2 -0.354902281 1.02834859 036 4 12 3 1.283785176 -1.18923098 137 5 13 1 -1.293772990 -0.73491317 038 5 13 2 0.748091387 0.07453705 139 5 13 3 -0.463585127 0.64802031 040 5 14 1 -1.946438667 1.35776140 041 5 14 2 -0.470448172 -0.61326604 142 5 14 3 1.478763383 -0.66490028 043 5 15 1 0.588240775 0.84448489 144 5 15 2 1.131731049 -1.51323232 045 5 15 3 0.212145247 -1.01804594 0")df$z1 <- rnorm(n= nrow(df),mean = 0,sd = 1)
mlogit
+ partykit::mob()
library(mlogit)library(partykit)mo <- mlogit(formula = y ~ x1 + x2 , data = df, idx = c("id_choice", "altern"))# Coefficients:# (Intercept):2 (Intercept):3 x1 x2 # 0.036497 0.293254 0.821173 1.062794mlogit_function <- function(y, x, offset = NULL, ...){ mlogit(y ~ x , data = df)}formula <- y ~ x1 + x2 | z1 mob(formula = formula, data = df, fit = mlogit_function, control = mob_control(minsize = 10, alpha = 0.01))# Error in mob(formula = formula, data = df, fit = mlogit_function, control = mob_control(minsize = 10, # no suitable fitting function specified
mlogit
+ loret::multinomtree()
这个函数可以运行树,但这不是我想要的,因为缺少了选项 2 的常数项。
loret::multinomtree(formula = formula, data = df)# Model-based recursive partitioning (NULL)# Model formula:# y ~ x1 + x2 | z1# # Fitted party:# [1] root: n = 45# 1:(Intercept) 1:x1 1:x2 # -1.1046317 0.7663315 1.0418296 # # Number of inner nodes: 0# Number of terminal nodes: 1# Number of parameters per node: 3# Objective function: 22.62393
mlogit
+ loret::clmtree()
loret::clmtree(formula = formula, data = df)# Error in clm.fit.default(y = c(1L, 0L, 0L, 0L, 1L, 0L, 0L, 0L, 1L, 0L, : # is.list(y) is not TRUE
回答:
我没有完整的解决方案,但希望提供的反馈能让你开始:
-
重要的是要区分所谓的“条件逻辑”模型与替代特定变量(您感兴趣的)和经典的“多项逻辑模型”与仅主体特定(或个体特定)变量。
mlogit::mlogit()
可以拟合两者(以及混合版本),而nnet::multinom()
仅支持后者。 -
对于拟合条件逻辑模型,您可以将数据设置为长格式或宽格式。您以长格式指定了数据,并且还有一个替代特定的分割变量
z1
。这意味着来自同一个个体的数据可能会出现在树的不同节点,这将相当尴尬。 -
相反,最好将数据设置为宽格式,使每一行对应一个个体,然后您可以只考虑个体特定的变量进行分割。这也符合已拟合的
mlogit
对象的$gradient
元素的视图,它提供了个体特定的梯度贡献。(这是sandwich::estfun()
提取的信息,它反过来是partykit::mob()
的基本信息。) -
基于替代特定变量进行合理的递归分区也可能是可行的,但我很难看出这会产生什么样的模型以及这些模型的意义。在任何情况下,您都必须编写自己的代码来从拟合模型对象中提取
estfun
,它提供了完全分解的替代特定梯度贡献。 -
loret
包有些未完成,并且已经有一段时间没有更新了。因此,我不建议目前在“生产”中使用它。此外,nnet::multinom()
(基础loret::multinomtree()
)不适合您需要的模型(如上所述),而ordinal::clm()
(基础loret::clmtree()
)是用于完全不同的模型的。 -
我们希望在
loret
中构建的一个特定方面,但尚未完成的,是在逻辑模型中自动检测(准)完全分离,并在树中适当处理它。 -
您的
mlogit
+partykit::mob()
方法不起作用,因为拟合函数没有正确的接口(您被正确告知)。请参阅vignette("mob", package = "partykit")
以了解支持的两种接口。 -
为了编写适当的接口,您需要确保在每个子集中都有您需要的所有变量。请注意,响应变量加上回归矩阵是不够的,您还需要索引变量!我建议通过
partykit::mob()
指定的公式的y
变量或x
变量来包含这些变量。然后在mob_control()
中,您可以设置ytype = "data.frame"
和xtype = "data.frame"
。然后,y
和x
都作为data.frame
对象提供,并且可以在调用mlogit::mlogit()
之前再次组合。在下面的示例中,我已经对formula
和idx
参数进行了硬编码。
基于您的示例的说明:您可以设置一个模型拟合函数 my_fit()
,它期望 y
和 x
是数据框,然后使用 formula = y ~ x1 + x2
和 idx = c("id_choice", "altern")
来拟合 mlogit()
模型。
my_fit <- function(y, x = NULL, start = NULL, weights = NULL, offset = NULL, ...) { mlogit::mlogit(formula = y ~ x1 + x2, data = cbind(y, x), idx = c("id_choice", "altern"))}
为了指定树,您需要通过 mob()
公式的 x
变量传递 mlogit()
模型所需的所有变量:
tr <- mob(y ~ x1 + x2 + id_choice + altern | z1, data = df, fit = my_fit, control = mob_control(ytype = "data.frame", xtype = "data.frame"))
表面上看,这是可行的。至少它在根节点中拟合了所需的模型,您可以通过检查 coef(tree)
来查看。然而,它之所以工作,唯一的原因是它甚至没有尝试进行任何分区,因为与参数数量相比,样本大小被认为太小了。
但是,当您在 mob_control()
中额外设置 minsize = 10
时,分区过程将失败。其原因是分割变量的长度为 45,但 mlogit()
的梯度/estfun 的长度只有 15。这是长替代特定格式与短个体特定格式的区别。
因此,为了使事情正常工作,您必须使用宽格式的数据,然后相应地调整 mob()
公式和 my_fit()
中的 mlogit()
调用。