Package R pour combiner les niveaux de facteurs pour le datamining?

10

Vous vous demandez si quelqu'un a traversé un package / une fonction dans R qui combinera les niveaux d'un facteur dont la proportion de tous les niveaux d'un facteur est inférieure à un certain seuil? Plus précisément, l'une des premières étapes de la préparation des données que je mène est de regrouper des niveaux clairsemés de facteurs (par exemple en un niveau appelé «Autre») qui ne constituent pas au moins, disons, 2% du total. Cela se fait sans surveillance et se fait lorsque l'objectif est de modéliser une certaine activité dans le marketing (pas la détection de fraude, où ces très petits événements pourraient être extrêmement importants). Je recherche une fonction qui réduira les niveaux jusqu'à ce qu'une certaine proportion seuil soit atteinte.

METTRE À JOUR:

Grâce à ces excellentes suggestions, j'ai écrit une fonction assez facilement. J'ai réalisé cependant qu'il était possible de réduire les niveaux avec une proportion <le minimum et que ce niveau recodé soit toujours <le minimum, ce qui nécessitait l'ajout du niveau le plus bas avec la proportion> le minimum. Probablement peut être plus efficace mais cela semble fonctionner. La prochaine amélioration consisterait à comprendre comment capturer les "règles" pour appliquer la logique d'effondrement à de nouvelles données (un ensemble de validation ou des données futures).

collapseFactors<- function(tableName,minPercent=5,fillIn ="RECODED" )
{
    for (i in 1:ncol(tableName))
        {   

            if(is.factor(tableName[,i]) == TRUE) #process just factors
            {


                sortedTable<-sort(prop.table(table(tableName[,i])))
                numberToCollapse<-length(sortedTable[sortedTable<(minPercent/100)])

                if (sum(sortedTable[1:numberToCollapse])<(minPercent/100))
                    {
                        numberToCollapse=numberToCollapse+1 #add next level if < minPercent
                    }

                if(numberToCollapse>1) #if not >1 then nothing to collapse
                {
                    lf <- names(sortedTable[1:numberToCollapse])
                    levels(tableName[,i])[levels(tableName[,i]) %in% lf] <- fillIn
                }
            }#end if a factor


        }#end for loop

    return(tableName)

}#end function
B_Miner
la source
Pour une autre approche: stats.stackexchange.com/questions/227125/…
kjetil b halvorsen

Réponses:

11

Il semble que ce soit juste une question de «relancer» le facteur; pas besoin de calculer des sommes partielles ou de faire une copie du vecteur d'origine. Par exemple,

set.seed(101)
a <- factor(LETTERS[sample(5, 150, replace=TRUE, 
                           prob=c(.1, .15, rep(.75/3,3)))])
p <- 1/5
lf <- names(which(prop.table(table(a)) < p))
levels(a)[levels(a) %in% lf] <- "Other"

Ici, les niveaux de facteur d'origine sont répartis comme suit:

 A  B  C  D  E 
18 23 35 36 38 

puis ça devient

Other     C     D     E 
   41    35    36    38 

Il peut être commodément enveloppé dans une fonction. Il y a une combine_factor()fonction dans le package de remodelage , donc je suppose que cela pourrait aussi être utile.

De plus, comme vous semblez intéressé par l'exploration de données, vous pouvez jeter un œil au package caret . Il possède de nombreuses fonctionnalités utiles pour le prétraitement des données, y compris des fonctions telles nearZeroVar()que celles qui permettent de signaler les prédicteurs avec une distribution très déséquilibrée des valeurs observées (voir la vignette, les exemples de données, les fonctions de prétraitement, les visualisations et d'autres fonctions , p. 5, par exemple utile).

chl
la source
@CHI Merci. J'ai étudié le package caret et l'ai utilisé pour régler les méta-paramètres. très utile!.
B_Miner
@chl +1, chouette. J'ai écrit ma fonction uniquement parce que le code a [niveaux (a)% dans% lf] <- "Autre" ne fonctionne pas, j'ai donc supposé que le changement de niveau de facteur est une affaire compliquée. Comme d'habitude, il s'est avéré que R n'est pas compliqué, je le suis :)
mpiktas
@mpiktas Thx. Vous pouvez travailler au niveau vectoriel avec par exemple a[as.character(a) %in% lf] <- lf[1]; a <- factor(droplevels(a), labels=c("Other",LETTERS[3:5])).
chl
+1. a [niveaux (a)% dans% lf] <- "Autre" enregistre bien sûr une tonne de lignes de code. Intelligent et efficace!
Christopher Aden
Mais notez qu'un a [a == "a"] <- "Autre" ne fonctionnera pas, ce qui pour moi est tout à fait naturel de supposer qu'il le devrait. D'autant plus qu'un a [a == "a"] est parfaitement valide.
mpiktas
5

Le seul problème avec la réponse de Christopher est qu'elle va mélanger l'ordre d'origine du facteur. Voici ma solution:

 Merge.factors <- function(x, p) {
     t <- table(x)
     levt <- cbind(names(t), names(t)) 
     levt[t/sum(t)<p, 2] <- "Other"
     change.levels(x, levt)
 }

change.levelsest la fonction suivante. Je l'ai écrit il y a quelque temps, donc je pense qu'il pourrait y avoir de meilleures façons de réaliser ce qu'il fait.

 change.levels <- function(f, levt) {
     ##Change the the names of the factor f levels from
     ##substitution table levt.
     ## In the first column there are the original levels, in
     ## the second column -- the substitutes
     lv <- levels(f)
     if(sum(sort(lv) != sort(levt[, 1]))>0)
     stop ("The names from substitution table does not match given level names")
     res <- rep(NA, length(f))

     for(i in lv) {
          res[f==i] <- as.character(levt[levt[, 1]==i, 2])
     }
     factor(res)
}
mpiktas
la source
4

J'ai écrit une fonction rapide qui permettra d'atteindre cet objectif. Je suis un utilisateur novice de R, donc cela peut être lent avec de grandes tables.

Merge.factors <- function(x, p) { 
    #Combines factor levels in x that are less than a specified proportion, p.
    t <- table(x)
    y <- subset(t, prop.table(t) < p)
    z <- subset(t, prop.table(t) >= p)
    other <- rep("Other", sum(y))
    new.table <- c(z, table(other))
    new.x <- as.factor(rep(names(new.table), new.table))
    return(new.x)
}

À titre d'exemple en action:

> a <- rep("a", 100)
> b <- rep("b", 1000)
> c <- rep("c", 1000)
> d <- rep("d", 1000)
> e <- rep("e", 400)
> f <- rep("f", 100)
> x <- factor(c(a, b, c, d, e, f))
> summary(x)
   a    b    c    d    e    f 
 100 1000 1000 1000  400  100 
> prop.table(table(x))
x
         a          b          c          d          e          f 
0.02777778 0.27777778 0.27777778 0.27777778 0.11111111 0.02777778 
> 
> w <- Merge.factors(x, .05)
> summary(w)
    b     c     d     e Other 
 1000  1000  1000   400   200 
> class(w)
[1] "factor"
Christopher Aden
la source
Merci pour l'observation, John. Je l'ai un peu changé pour en faire un facteur. Tout ce que j'ai fait, c'est de refaire le vecteur d'origine de la table, donc s'il y a un moyen de sauter cette étape, ce sera plus rapide.
Christopher Aden
Merci à tous ceux qui ont répondu. Mon R est faible mais la capacité de le faire avec si peu de lignes de code témoigne de sa puissance et me donne envie d'apprendre.
B_Miner