2010-11-25 50 views
9

Lors de l'utilisation de formules comportant des facteurs, les modèles ajustés appellent les coefficients XY, X étant le nom du facteur et Y un niveau particulier. Je veux être capable de créer une formule à partir des noms de ces coefficients. La raison: Si j'ajuste un lasso à une matrice de conception clairsemée (comme je le fais ci-dessous), je voudrais créer un nouvel objet de formule qui ne contienne que des termes pour les coefficients non nuls.Conversion de noms de coefficients en une formule dans R

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" 

De cela, je voudrais avoir une formule

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

J'ai vérifié la formule() et all.vars() en vain. En outre, écrire une fonction pour analyser cela est un peu pénible à cause des différents types de termes qui peuvent apparaître. Par exemple, pour x: lettre quand x est une valeur numérique et lettre est un facteur, ou I (x> 5): lettre comme un autre cas ennuyeux. Donc, je ne suis pas au courant de certaines fonctions pour convertir entre la formule et la représentation de son caractère et vice versa?

+0

Ce n'est pas une formule que je reconnais dans R –

+1

Peut-être que je me méprends, mais vous semblez ne pas grep complètement les formules du modèle de R. Vous n'incluez pas dans la formule les bits XY, vous incluez le X et 'model.matrix()' et 'model.frame()' font leur truc pour étendre les niveaux du X aux colonnes pertinentes de la matrice du modèle, le XY. –

+0

Pourriez-vous expliquer pourquoi vous voulez la formule? Quelle est l'utilisation finale? –

Répondre

3

Lorsque j'ai exécuté le code, j'ai obtenu quelque chose d'un peu différent, puisque set.seed() n'avait pas été spécifié. Au lieu d'utiliser le nom de la variable "lettre", je "letter_" comme marqueur de séparation pratique:

> 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" 

a ensuite fait la scission et empaqueté dans une matrice de caractères:

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

fnewmtx [1] [2]
letter_c "c" "lettre" letter_d "d" "lettre" letter_e "e" "lettre" letter_f "f" "lettre" coupé le reste

Et enveloppé la fonction de sortie (s) de sortie dans as.formula() qui est la moitié de la réponse à la façon de "convertir entre la formule et sa représentation de caractères et retour." L'autre moitié est as.character()

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

Et la sortie est maintenant un objet de classe appropriée:

> 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") 

Je trouve intéressant que la conversion de as.formula a fait les guillemets simples autour de la lettres entre guillemets. Editer: Maintenant que le problème a une dimension supplémentaire ou deux, ma suggestion est d'ignorer la recréation de la formule. Notez que les rownames de MyFit beta $ sont exactement les mêmes que les noms de colonnes de X, donc au lieu d'utiliser les rownames non nuls comme indices pour sélectionner des colonnes dans la matrice X:

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

Merci DWin! C'est proche, mais pas tout à fait une solution générale. J'ai mis à jour mon exemple. Y a-t-il une solution rapide qui étend votre solution? –

+0

De même, lors de la création de fnewmtx, le strsplit ne devrait-il pas utiliser split = "_" au lieu de split = ""? –

+0

Tout à fait raison. Je vais éditer. Je vois si je peux faire face à la complexité supplémentaire d'une formule d'interaction, mais ne vous attendez pas à une réponse rapide ... update: essaye de modifier mais les underscores sont dans les segments de code de l'original et ne sont pas affichés sur la page SO. Re- "codé" et peut-être mieux maintenant. –

2

Christopher, ce que vous demandez pour apparaît, après quelques considérations et l'examen de sparse.model.matrix etc, être un peu impliqué. Vous n'avez pas expliqué pourquoi vous ne voulez pas former la matrice du modèle complet vide pour X_test, il est donc difficile de conseiller une voie à suivre autre que les deux options ci-dessous.

Si vous avez un grand nombre d'observations dans X_test et donc ne voulez pas produire la matrice clairsemée complète pour une utilisation dans predict() pour des raisons de calcul, il pourrait être plus opportun de diviser X_test en deux morceaux ou plus d'échantillons et de forme les matrices de modèles clairsemés pour chacun à son tour, en les rejetant après utilisation. A défaut, vous devrez étudier le code de Matrix en détail. Commencez par sparse.model.matrix et notez qu'il appelle ensuite Matrix:::model.spmatrix et localise les appels au Matrix:::fac2Sparse dans cette fonction. Vous devrez probablement co-opter le code de ces fonctions, mais utilisez un fac2Sparse modifié pour atteindre ce que vous voulez atteindre.

Désolé, je ne peux pas fournir un script standard pour ce faire, mais c'est une tâche de codage substantielle. Si vous descendez cette route, consultez la vignette Sparse Model Matrices dans le paquet Matrix et récupérez les sources du paquet (à partir de CRAN) pour voir si les fonctions que je mentionne sont mieux documentées dans le code source (il n'y a pas de fichiers Rd pour fac2Sparse par exemple). Vous pouvez également demander conseil aux auteurs de Matrix (Martin Maechler et Doug Bates), mais notez que ces deux types ont une charge d'enseignement particulièrement lourde.

Bonne chance!