Преобразование имен коэффициентов в формулу в R - PullRequest
9 голосов
/ 26 ноября 2010

При использовании формул, имеющих факторы, в подогнанных моделях называются коэффициенты XY, где X - это имя фактора, а Y - его конкретный уровень. Я хочу иметь возможность создать формулу из имен этих коэффициентов.

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

require("MatrixModels")
require("glmnet")
set.seed(1)
n <- 200
Z <- data.frame(letter=factor(sample(letters,n,replace=T),letters),
                x=sample(1:20,200,replace=T))
f <- ~ letter + x:letter + I(x>5):letter
X <- sparse.model.matrix(f, Z)
beta <- matrix(rnorm(dim(X)[2],0,5),dim(X)[2],1)
y <- X %*% beta + rnorm(n)

myfit <- glmnet(X,as.vector(y),lambda=.05)
fnew <- rownames(myfit$beta)[which(myfit$beta != 0)]
 [1] "letterb"              "letterc"              "lettere"             
 [4] "letterf"              "letterg"              "letterh"             
 [7] "letterj"              "letterm"              "lettern"             
[10] "lettero"              "letterp"              "letterr"             
[13] "letters"              "lettert"              "letteru"             
[16] "letterw"              "lettery"              "letterz"             
[19] "lettera:x"            "letterb:x"            "letterc:x"           
[22] "letterd:x"            "lettere:x"            "letterf:x"           
[25] "letterg:x"            "letterh:x"            "letteri:x"           
[28] "letterj:x"            "letterk:x"            "letterl:x"           
[31] "letterm:x"            "lettern:x"            "lettero:x"           
[34] "letterp:x"            "letterq:x"            "letterr:x"           
[37] "letters:x"            "lettert:x"            "letteru:x"           
[40] "letterv:x"            "letterw:x"            "letterx:x"           
[43] "lettery:x"            "letterz:x"            "letterb:I(x > 5)TRUE"
[46] "letterc:I(x > 5)TRUE" "letterd:I(x > 5)TRUE" "lettere:I(x > 5)TRUE"
[49] "letteri:I(x > 5)TRUE" "letterj:I(x > 5)TRUE" "letterl:I(x > 5)TRUE"
[52] "letterm:I(x > 5)TRUE" "letterp:I(x > 5)TRUE" "letterq:I(x > 5)TRUE"
[55] "letterr:I(x > 5)TRUE" "letteru:I(x > 5)TRUE" "letterv:I(x > 5)TRUE"
[58] "letterx:I(x > 5)TRUE" "lettery:I(x > 5)TRUE" "letterz:I(x > 5)TRUE"

Из этого я хотел бы получить формулу

~ I(letter=="d") + I(letter=="e") + ...(etc)

Я проверил формулы () и all.vars () безрезультатно. Кроме того, написание функции для анализа этого представляет собой небольшую боль из-за различных типов терминов, которые могут возникнуть. Например, для x: буква, когда x - это числовое значение, а буква - фактор, или I (x> 5): буква как еще один раздражающий случай.

Так я не знаю какую-нибудь функцию для преобразования между формулой и ее символьным представлением и обратно?

Ответы [ 2 ]

3 голосов
/ 26 ноября 2010

Когда я запускал код, я получал что-то немного другое, так как set.seed () не был указан. Вместо того, чтобы использовать переменную name «letter», я использовал «letter_» в качестве удобного маркера разделения:

> fnew <- rownames(myfit$beta)[which(myfit$beta != 0)]

> fnew
 [1] "letter_c" "letter_d" "letter_e" "letter_f" "letter_h" "letter_k" "letter_l"
 [8] "letter_o" "letter_q" "letter_r" "letter_s" "letter_t" "letter_u" "letter_v"
[15] "letter_w"

Затем сделали разбиение и упаковали в матрицу символов:

> fnewmtx <- cbind( lapply(sapply(fnew, strsplit, split="_"), "[[", 2),
+ lapply(sapply(fnew, strsplit, split="_"), "[[", 1))

fnewmtx [, 1] [, 2]
letter_c "c" "letter" letter_d "d" "letter" буква "е" "буква" letter_f "f" "letter" отрубил остальные

И обернул выходные данные функции вставки в as.formula (), что является половиной ответа на вопрос, как «преобразовать формулу и ее символьное представление и обратно». Другая половина - as.character ()

form <- as.formula( paste("~", 
             paste( 
               paste(" I(", fnewmtx[,2], "_ ==", "'",fnewmtx[,1],"') ", sep="") , 
             sep="", collapse="+")
                 ) 
           )  # edit: needed to add back the underscore

А теперь выводом является соответствующий объект класса:

> class(form)
[1] "formula"
> form
~I(letter_ == "c") + I(letter_ == "d") + I(letter_ == "e") + 
    I(letter_ == "f") + I(letter_ == "h") + I(letter_ == "k") + 
    I(letter_ == "l") + I(letter_ == "o") + I(letter_ == "q") + 
    I(letter_ == "r") + I(letter_ == "s") + I(letter_ == "t") + 
    I(letter_ == "u") + I(letter_ == "v") + I(letter_ == "w")

Мне интересно, что преобразование as.formula превратило одинарные кавычки вокруг букв в двойные.

Редактировать: Теперь, когда у проблемы есть дополнительное измерение или два, я предлагаю пропустить воссоздание формулы. Обратите внимание, что имена строк myfit $ beta в точности совпадают с именами столбцов X, поэтому вместо выбора ненулевых имен строк в качестве индексов выберите столбцы в матрице X:

> str(X[ , which( colnames(X) %in% rownames(myfit$beta)[which(myfit$beta != 0)] )] )
Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
  ..@ i       : int [1:429] 9 54 91 157 166 37 55 68 117 131 ...
  ..@ p       : int [1:61] 0 5 13 20 28 36 42 50 60 68 ...
  ..@ Dim     : int [1:2] 200 60
  ..@ Dimnames:List of 2
  .. ..$ : chr [1:200] "1" "2" "3" "4" ...
  .. ..$ : chr [1:60] "letter_b" "letter_c" "letter_e" "letter_f" ...
  ..@ x       : num [1:429] 1 1 1 1 1 1 1 1 1 1 ...
  ..@ factors : list()

> myfit2 <- glmnet(X[ , which( colnames(X) %in% rownames(myfit$beta)[which(myfit$beta != 0)] )] ,as.vector(y),lambda=.05)
> myfit2

Call:  glmnet(x = X[, which(colnames(X) %in% rownames(myfit$beta)[
                                           which(myfit$beta != 0)])], 
              y = as.vector(y), lambda = 0.05) 

     Df   %Dev Lambda
[1,] 60 0.9996   0.05
2 голосов
/ 01 декабря 2010

Кристофер, то, что вы просите, кажется, после некоторого рассмотрения и изучения sparse.model.matrix и т. Д., Быть несколько вовлеченным. Вы не объяснили , почему вы не хотите формировать полную разреженную матрицу модели для X_test, поэтому трудно посоветовать дальнейший путь, кроме двух вариантов ниже.

Если у вас есть большое количество наблюдений в X_test и, следовательно, вы не хотите создавать полную разреженную матрицу для использования в predict() по вычислительным соображениям, может оказаться более целесообразным разделить X_test на два или более фрагменты образцов и формируют матрицы разреженных моделей для каждого из них по очереди, отбрасывая их после использования.

В противном случае вам нужно будет детально изучить код из пакета Matrix. Начните с sparse.model.matrix и обратите внимание, что затем он вызывает Matrix:::model.spmatrix и локализует вызовы к Matrix:::fac2Sparse в этой функции. Вам, вероятно, придется использовать код из этих функций, но использовать модифицированный fac2Sparse для достижения того, чего вы хотите достичь.

Извините, я не могу предоставить готовый скрипт для этого, но это существенная задача кодирования. Если вы пойдете по этому пути, посмотрите виньетка Sparse Model Matrices в пакете Matrix и получите исходные коды пакета (из CRAN), чтобы увидеть, лучше ли документированы упомянутые мной функции в исходном коде (есть например, нет файлов Rd для fac2Sparse). Вы также можете обратиться за советом к авторам «Матрицы» (Мартину Мачлеру и Дугу Бейтсу), хотя учтите, что в этом семестре оба преподавателя имели особенно большую учебную нагрузку.

Удачи!

...