Apollo中为何无法将效用函数压缩为单个参数?
问题背景
我正在用Apollo构建有序logit模型,需要编写超100种模型设定,希望通过代码压缩减少工作量。目前通过合并向量的方式整理apollo_beta参数完全可行:
randstad <- c( #b_randstad_is = 0, b_randstad_not = 0, b_randstad_to = 0, b_randstad_from = 0 ) wdist <- c( b_weekdist_dec = 0, b_weekdist_inc = 0, b_weekdist_same = 0, #b_weekdist_nojob = 0, b_weekdist_lostjob = 0, b_weekdist_gotjob = 0, b_weekdist_alwayswfh = 0, b_weekdist_wfhtocommuting = 0, b_weekdist_commutingtowfh = 0, b_weekdist_differingaddresses = 0 ) spec1 <- c( randstad, wdist ) apollo_beta = c(spec1, tau_nautos_change_1 = 0, tau_nautos_change_2 = 1)
但尝试用quote()压缩效用函数并传入ol_settings时,出现以下错误:
Error in apollo_preprocess(inputs = ol_settings, modelType, functionality, : "utility" for model component "OL" needs to be a function, a scalar, or a vector/matrix/cube with one row per observation in the "database"
直接写入效用函数表达式则能正常运行,且调用uspec1(我用quote生成的效用表达式对象)能得到预期的表达式,请问为何效用函数无法通过这种方式压缩?
原因分析
Apollo的有序logit组件(OL)对utility参数的要求是:必须是可被求值为对应维度对象的表达式、函数,或者直接是符合要求的数值对象。
quote()的作用是返回一个未求值的语言对象(即代码的语法结构),而不是可直接执行的计算逻辑。虽然你调用uspec1能看到预期的表达式文本,但这个对象本身只是R的语法树节点,并没有被解析成能生成效用值的计算式。
当你直接写效用表达式时,R会将其作为可求值的表达式传入,Apollo在预处理阶段可以对其解析、求值,生成每个观测对应的效用向量;但用quote()包裹后,传入的是call类型的对象,Apollo的预处理函数无法自动对其进行求值,因此触发类型不匹配的报错。
解决办法
方法1:用eval()对quote对象求值后传入
将quote生成的表达式通过eval()求值,确保传入的是实际的效用计算结果:
# 假设uspec1是用quote生成的效用表达式 uspec1 <- quote( b_randstad_not * randstad_not + b_randstad_to * randstad_to + b_weekdist_dec * weekdist_dec + # 补充其他效用项... ) # 构建ol_settings时用eval求值 ol_settings <- list( modelName = "OL_model", choiceVar = nautos_change, utility = eval(uspec1), tau = c(tau_nautos_change_1, tau_nautos_change_2) )
注意:要确保eval()在包含所有beta参数和数据库变量的环境中执行(比如在Apollo的主函数环境内,或者用with()指定环境)。
方法2:将效用逻辑封装为函数
把效用计算写成一个接收beta和数据的函数,直接传入utility参数:
# 定义效用函数,接收beta列表和数据库数据 utility_fun <- function(beta, data) { with(data, beta$b_randstad_not * randstad_not + beta$b_randstad_to * randstad_to + beta$b_weekdist_dec * weekdist_dec + # 补充其他效用项... ) } # 传入函数作为utility ol_settings <- list( modelName = "OL_model", choiceVar = nautos_change, utility = utility_fun, tau = c(tau_nautos_change_1, tau_nautos_change_2) )
这种方式更灵活,适合批量生成不同模型设定的场景。
方法3:用bquote()构建带插值的可求值表达式
如果需要动态生成效用表达式(比如批量替换变量),可以用bquote()来构建带插值的表达式,它会自动替换指定的变量,返回可直接求值的表达式:
# 动态生成效用表达式,比如从变量列表拼接 randstad_terms <- paste0("b_randstad_", names(randstad), " * ", names(randstad)) wdist_terms <- paste0("b_weekdist_", names(wdist), " * ", names(wdist)) all_terms <- c(randstad_terms, wdist_terms) # 用bquote构建可求值的表达式 uspec1 <- bquote(.(parse(text = paste(all_terms, collapse = " + "))[[1]])) # 直接传入uspec1即可 ol_settings <- list( modelName = "OL_model", choiceVar = nautos_change, utility = uspec1, tau = c(tau_nautos_change_1, tau_nautos_change_2) )
内容的提问来源于stack exchange,提问作者Victor Nielsen

