Pakiet R do łączenia poziomów czynników do analizy danych?

10

Zastanawiasz się, czy ktoś natknął się na pakiet / funkcję w R, która połączy poziomy współczynnika, którego proporcja wszystkich poziomów w współczynniku jest mniejsza niż pewien próg? Konkretnie, jednym z pierwszych kroków w przygotowaniu danych, które przeprowadzam, jest zwinięcie razem nielicznych poziomów czynników (powiedzmy na poziomie zwanym „Inne”), które nie stanowią co najmniej, powiedzmy, 2% całości. Odbywa się to bez nadzoru i ma miejsce, gdy celem jest modelowanie niektórych działań marketingowych (nie wykrywanie oszustw, w których te bardzo małe zdarzenia mogą być niezwykle ważne). Szukam funkcji, która zwija poziomy, dopóki nie zostanie osiągnięta pewna część progu.

AKTUALIZACJA:

Dzięki tym świetnym sugestiom dość łatwo napisałem funkcję. Zdałem sobie jednak sprawę, że możliwe było zawalenie poziomów z proporcją <minimum i nadal mieć ten przekodowany poziom <minimum, wymagający dodania najniższego poziomu z proporcją> minimum. Prawdopodobnie może być bardziej wydajny, ale wydaje się, że działa. Kolejnym ulepszeniem byłoby wymyślenie, jak uchwycić „reguły” stosowania logiki zwijania do nowych danych (zestaw sprawdzania poprawności lub dane przyszłe).

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
źródło
Inne podejście: stats.stackexchange.com/questions/227125/…
kjetil b halvorsen

Odpowiedzi:

11

Wydaje się, że to tylko kwestia „trafności” tego czynnika; nie trzeba obliczać sum częściowych ani wykonywać kopii oryginalnego wektora. Na przykład,

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"

Tutaj pierwotne poziomy współczynników są podzielone w następujący sposób:

 A  B  C  D  E 
18 23 35 36 38 

i wtedy staje się

Other     C     D     E 
   41    35    36    38 

Może być dogodnie zawinięty w funkcję. W pakiecie przekształcającym jest combine_factor()funkcja , więc myślę, że może być również przydatna.

Ponadto, jeśli wydajesz się zainteresowany eksploracją danych, możesz rzucić okiem na pakiet Caret . Ma wiele przydatnych funkcji do wstępnego przetwarzania danych, w tym takie funkcje, nearZeroVar()które pozwalają na oznaczanie predyktorów z bardzo niezrównoważonym rozkładem obserwowanych wartości (patrz na przykład winieta, dane, funkcje przetwarzania wstępnego, wizualizacje i inne funkcje , s. 5) użytkowania).

chl
źródło
@CHI Dzięki. Przestudiowałem pakiet karetki i wykorzystałem go do dostrojenia meta parametrów. bardzo przydatne!.
B_Miner
@chl +1, fajny. Napisałem swoją funkcję wyłącznie dlatego, że kod a [poziomy (a)% w% lf] <- „Inne” nie działa, więc założyłem, że zmiana poziomu czynnika jest skomplikowaną sprawą. Jak zwykle okazało się, że R nie jest skomplikowane, jestem :)
mpiktas
@mpiktas Thx. Możesz pracować na poziomie wektorowym, np a[as.character(a) %in% lf] <- lf[1]; a <- factor(droplevels(a), labels=c("Other",LETTERS[3:5])).
chl
+1. a [poziomy (a)% w% lf] <- „Inne” z pewnością oszczędzają mnóstwo linii kodu. Sprytny i wydajny!
Christopher Aden
Ale zauważ, że [a == "a"] <- „Other” nie będzie działać, co dla mnie jest całkiem naturalne zakładać, że powinno. Zwłaszcza, że ​​[a == "a"] jest całkowicie poprawny.
mpiktas,
5

Jedyny problem z odpowiedzią Christophera polega na tym, że pomieszane zostanie oryginalne uporządkowanie tego czynnika. Oto moja poprawka:

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

gdzie change.levelsjest następująca funkcja. Napisałem to jakiś czas temu, więc podejrzewam, że mogą istnieć lepsze sposoby na osiągnięcie tego, co robi.

 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
źródło
4

Napisałem szybką funkcję, która osiągnie ten cel. Jestem początkującym użytkownikiem R, więc może być powolny przy dużych tabelach.

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

Jako przykład tego w akcji:

> 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
źródło
Dzięki za obserwację, John. Zmieniłem to trochę, aby było to czynnikiem. Wszystko, co zrobiłem, to przerobienie oryginalnego wektora ze stołu, więc jeśli istnieje sposób na pominięcie tego kroku, będzie to szybsze.
Christopher Aden
Dziękujemy wszystkim, którzy odpowiedzieli. Moje R jest słabe, ale możliwość zrobienia tego przy tak małej liczbie wierszy kodu świadczy o jego mocy i sprawia, że ​​chcę się uczyć.
B_Miner