Определение инфиксного оператора для использования в формуле

Я пытаюсь создать более экономную версию этого решения, которое влечет за собой указание RHS формулы в форме d1 + d1:d2.

Учитывая, что * в контексте формулы является надежным условием полного взаимодействия (т.е. d1 * d2 дает d1 + d2 + d1:d2), мой подход состоял в том, чтобы попытаться определить альтернативный оператор, скажем %+:% используя подход infix, к которому я привык в других приложениях, a la:

"%+:%" <- function(d1,d2) d1 + d2 + d1:d2

Однако это предсказуемо терпит неудачу, потому что я не был осторожен в оценке; давайте представим пример для иллюстрации моего прогресса:

set.seed(1029)
v1 <- runif(1000)
v2 <- runif(1000)
y <- .8*(v1 < .3) + .2 * (v2 > .25 & v2 < .8) - 
  .4 * (v2 > .8) + .1 * (v1 > .3 & v2 > .8)

В этом примере, надеюсь, ясно, почему простое изложение двух терминов может быть нежелательным:

y ~ cut(v2, breaks = c(0, .25, .8, 1)) +
  cut(v2, breaks = c(0, .25, .8, 1)):I(v1 < .3)

Одно решение, близкое к моему желаемому результату, состоит в том, чтобы определить всю формулу как функцию:

plus.times <- function(outvar, d1, d2){
  as.formula(paste0(quote(outvar), "~", quote(d1),
                    "+", quote(d1), ":", quote(d2)))
}

Это дает ожидаемые коэффициенты при передаче в lm, но с именами, которые сложнее интерпретировать напрямую (особенно в реальных данных, где мы позаботимся дать описательные имена d1 и d2, в отличие от этого общий пример):

out1 <- lm(y ~ cut(v2, breaks = c(0, .25, .8, 1)) +
             cut(v2, breaks = c(0, .25, .8, 1)):I(v1 < .3))
out2 <- lm(plus.times(y, cut(v2, breaks = c(0, .25, .8, 1)), I(v1 < .3)))
any(out1$coefficients != out2$coefficients)
# [1] FALSE
names(out2$coefficients)
# [1] "(Intercept)"         "d1(0.25,0.8]"        "d1(0.8,1]"           "d1(0,0.25]:d2TRUE"  
# [5] "d1(0.25,0.8]:d2TRUE" "d1(0.8,1]:d2TRUE"

Так что это меньше оптимального.

Есть ли способ определить настройку кода так, чтобы оператор инфикса, о котором я говорил выше, работает так, как ожидалось? Как насчет изменения формы plus.times, чтобы переменные не были переименованы?

Я выкалывал (?formula, ?"~", ?":", getAnywhere(formula.default), этот ответ и т.д.), но не видно, как именно R интерпретирует *, когда он встречается в формуле, чтобы я мог внести желаемые незначительные корректировки.

Ответ 1

В этом случае вам не нужно определять новый оператор: в формуле d1/d2 расширяется до d1 + d1:d2. Другими словами d1/d2 указывает, что d2 вложен в d1. Продолжая ваш пример:

out3 <- lm(y ~ cut(v2,breaks=c(0,.25,.8,1))/I(v1 < .3))
all.equal(coef(out1), coef(out3))
# [1] TRUE

Дополнительные комментарии

Факторы могут быть пересечены или вложены. Два фактора пересекаются, если можно наблюдать каждую комбинацию уровней двух факторов, например. пола и лечения, температуры и рН и т.д. Фактор вложен в другой, если каждый уровень этого фактора можно наблюдать только на одном из уровней другого фактора, например. города и страны, сотрудника и магазина и т.д.

Эти отношения отражены в параметризации модели. Для скрещенных множителей мы используем d1*d2 или d1 + d2 + d1:d2, чтобы дать основной эффект каждого фактора плюс взаимодействие. Для вложенных факторов мы используем d1/d2 или d1 + d1:d2, чтобы дать отдельную подмодуль формы 1 + d2 для каждого уровня d1.

Идея гнездования не ограничивается факторами, например, мы можем использовать sex/x для установки отдельной линейной регрессии на x для мужчин и женщин.

В формуле %in% эквивалентна :, но ее можно использовать для выделения вложенной или иерархической структуры данных/модели. Например, a + b %in% a совпадает с a + a:b, но чтение его как "плюс b в пределах" дает лучшее описание устанавливаемой модели. Тем не менее использование / имеет преимущество упрощения формулы модели в то же время, что и подчеркивание структуры.