Pacchetto R per combinare i livelli dei fattori per il datamining?


10

Ti chiedi se qualcuno ha incontrato un pacchetto / funzione in R che combinerà i livelli di un fattore la cui proporzione di tutti i livelli in un fattore è inferiore a qualche soglia? In particolare, uno dei primi passi nella preparazione dei dati che conduco è il collasso di livelli sparsi di fattori (diciamo in un livello chiamato "Altro") che non costituiscono almeno, diciamo, il 2% del totale. Questo viene fatto senza supervisione e viene fatto quando l'obiettivo è quello di modellare alcune attività di marketing (non il rilevamento di frodi, in cui eventi molto piccoli potrebbero essere estremamente importanti). Sto cercando una funzione che crollerà i livelli fino a quando non verrà raggiunta una certa soglia.

AGGIORNARE:

Grazie a questi fantastici suggerimenti ho scritto una funzione abbastanza facilmente. Mi sono reso conto però che era possibile far crollare i livelli con proporzione <il minimo e avere comunque quel livello ricodificato <il minimo, richiedendo l'aggiunta del livello più basso con proporzione> il minimo. Probabilmente può essere più efficiente ma sembra funzionare. Il prossimo miglioramento sarebbe capire come catturare le "regole" per applicare la logica di compressione a nuovi dati (un set di validazione o dati futuri).

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

Risposte:


11

Sembra che sia solo una questione di "pertinenza" del fattore; non è necessario calcolare somme parziali o fare una copia del vettore originale. Per esempio,

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"

Qui, i livelli dei fattori originali sono distribuiti come segue:

 A  B  C  D  E 
18 23 35 36 38 

e poi diventa

Other     C     D     E 
   41    35    36    38 

Può essere convenientemente avvolto in una funzione. C'è una combine_factor()funzione nel pacchetto reshape , quindi immagino possa essere utile anche.

Inoltre, come si sembrano interessati a data mining, si potrebbe avere uno sguardo al punto di inserimento del pacchetto. Ha molte funzioni utili per la preelaborazione dei dati, tra cui funzioni nearZeroVar()che consentono di contrassegnare i predittori con una distribuzione molto squilibrata dei valori osservati (vedere la vignetta, dati di esempio, funzioni di pre-elaborazione, visualizzazioni e altre funzioni , p. 5, ad esempio d'uso).


@CHI Grazie. Ho studiato il pacchetto del cursore e l'ho usato per ottimizzare i meta parametri. molto utile!.
B_Miner,

@chl +1, bello. Ho scritto la mia funzione solo perché il codice a [livelli (a)% in% lf] <- "Altro" non funziona, quindi ho ipotizzato che la modifica del livello dei fattori sia un affare complicato. Come al solito si è scoperto che R non è complicato, lo sono :)
mpiktas,

@mpiktas Thx. Puoi lavorare a livello vettoriale con ad es a[as.character(a) %in% lf] <- lf[1]; a <- factor(droplevels(a), labels=c("Other",LETTERS[3:5])).
chl

+1. a [livelli (a)% in% lf] <- "Altro" salva sicuramente una tonnellata di righe di codice. Intelligente ed efficiente!
Christopher Aden,

Ma nota che a [a == "a"] <- "Altro" non funzionerà, il che per me è del tutto naturale supporre che dovrebbe. Soprattutto perché un [a == "a"] è perfettamente valido.
mpiktas,

5

L'unico problema con la risposta di Christopher è che mescolerà l'ordine originale del fattore. Ecco la mia soluzione:

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

dov'è change.levelsla seguente funzione. L'ho scritto qualche tempo fa, quindi sospetto che potrebbero esserci modi migliori per ottenere ciò che fa.

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

4

Ho scritto una funzione rapida che raggiungerà questo obiettivo. Sono un utente R alle prime armi, quindi potrebbe essere lento con tavoli di grandi dimensioni.

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

A titolo di esempio in azione:

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

Grazie per l'osservazione, John. L'ho cambiato un po 'per renderlo un fattore. Tutto ciò che ho fatto è stato rifare il vettore originale dalla tabella, quindi se c'è un modo per saltare quel passaggio, questo sarà più veloce.
Christopher Aden,

Grazie a tutti coloro che hanno risposto. La mia R è debole ma la capacità di farlo con così poche righe di codice testimonia quanto sia potente e mi fa venire voglia di imparare.
B_Miner,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.