Pacote R para combinar níveis de fator para datamining?

10

Querendo saber se alguém já se deparou com um pacote / função em R que combinará níveis de um fator cuja proporção de todos os níveis de um fator seja menor que algum limite? Especificamente, uma das primeiras etapas na preparação de dados que eu conduzo é recolher níveis esparsos de fatores (digamos, em um nível chamado 'Outro') que não constituam pelo menos 2% do total. Isso é feito sem supervisão e é feito quando o objetivo é modelar alguma atividade de marketing (não a detecção de fraudes, onde essas ocorrências muito pequenas podem ser extremamente importantes). Eu estou procurando por uma função que reduza os níveis até que alguma proporção de limite seja atingida.

ATUALIZAR:

Graças a essas ótimas sugestões, escrevi uma função com bastante facilidade. Percebi, porém, que era possível reduzir os níveis com proporção <o mínimo e ainda assim esse nível recodificado ser <o mínimo, exigindo a adição do nível mais baixo com proporção> o mínimo. Provavelmente pode ser mais eficiente, mas parece funcionar. O próximo aprimoramento seria descobrir como capturar as "regras" para aplicar a lógica de recolhimento a novos dados (um conjunto de validação ou dados futuros).

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
fonte
Para outra abordagem: stats.stackexchange.com/questions/227125/…
kjetil b halvorsen

Respostas:

11

Parece que é apenas uma questão de "relevância" do fator; não é necessário calcular somas parciais ou fazer uma cópia do vetor original. Por exemplo,

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"

Aqui, os níveis dos fatores originais são distribuídos da seguinte maneira:

 A  B  C  D  E 
18 23 35 36 38 

e então se torna

Other     C     D     E 
   41    35    36    38 

Pode ser convenientemente envolvido em uma função. Há uma combine_factor()função no pacote remodelar , então acho que poderia ser útil também.

Além disso, como você parece interessado em mineração de dados, pode dar uma olhada no pacote de intercalação . Possui muitos recursos úteis para pré-processamento de dados, incluindo funções como nearZeroVar()essa que permitem sinalizar preditores com distribuição muito desequilibrada dos valores observados (consulte a vinheta, dados de exemplo, funções de pré-processamento, visualizações e outras funções , p. 5, por exemplo de uso).

chl
fonte
@ CHI Obrigado. Estudei o pacote de intercalação e usei-o para ajustar os meta-parâmetros. muito útil!.
B_Miner
@ chl +1, bom. Eu escrevi minha função apenas porque o código a [níveis (a)% em% lf] <- "Outro" não funciona, então eu assumi que a alteração do nível de fator é um assunto complicado. Como de costume, descobriu-se que R não é complicado, eu sou :)
mpiktas 21/12/2010
@mpiktas Thx. Você pode trabalhar no nível vetorial com, por exemplo a[as.character(a) %in% lf] <- lf[1]; a <- factor(droplevels(a), labels=c("Other",LETTERS[3:5])),.
chl
+1. a [níveis (a)% em% lf] <- "Outros" certamente salva uma tonelada de linhas de código. Inteligente e eficiente!
Christopher Aden
Mas observe que um [a == "a"] <- "Outro" não funcionará, o que para mim é bastante natural supor que deveria. Especialmente porque um [a == "a"] é perfeitamente válido.
mpiktas
5

O único problema com a resposta de Christopher é que ela misturará a ordem original do fator. Aqui está a minha correção:

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

Onde change.levelsestá a seguinte função. Eu o escrevi há algum tempo, então suspeito que possa haver melhores maneiras de conseguir o que faz.

 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
fonte
4

Eu escrevi uma função rápida que cumprirá esse objetivo. Eu sou um usuário R iniciante, por isso pode ser lento com tabelas grandes.

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

Como um exemplo disso em ação:

> 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
fonte
Obrigado pela observação, John. Eu mudei um pouco para torná-lo um fator. Tudo o que fiz foi refazer o vetor original da tabela; portanto, se houver uma maneira de pular essa etapa, isso será mais rápido.
Christopher Aden
Obrigado a todos que responderam. Meu R é fraco, mas a capacidade de fazer isso com tão poucas linhas de código é uma prova de quão poderoso é e me faz querer aprender.
B_Miner