Пакет R для объединения уровней факторов для обработки данных?

10

Хотите знать, сталкивался ли кто-нибудь с пакетом / функцией в R, которая объединит уровни фактора, доля всех уровней которого меньше некоторого порога? В частности, одним из первых шагов в подготовке данных, которые я выполняю, является объединение разреженных уровней факторов вместе (скажем, в уровень, называемый «Другой»), которые не составляют, по крайней мере, скажем, 2% от общего количества. Это делается без присмотра и делается, когда цель состоит в том, чтобы смоделировать какую-либо деятельность в маркетинге (не обнаружение мошенничества, где эти очень малые случаи могут быть чрезвычайно важны). Я ищу функцию, которая будет сворачивать уровни, пока не будет достигнута некоторая пороговая пропорция.

ОБНОВИТЬ:

Благодаря этим замечательным предложениям я довольно легко написал функцию. Я действительно осознал, что было возможно свернуть уровни с пропорцией <минимум и все же иметь этот перекодированный уровень как <минимум, требующий добавления самого низкого уровня с пропорцией> минимум. Вероятно, может быть более эффективным, но, похоже, работает. Следующим улучшением будет выяснение того, как собрать «правила» применения логики свертывания к новым данным (проверочный набор или будущие данные).

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
источник
Для другого подхода: stats.stackexchange.com/questions/227125/…
kjetil b halvorsen

Ответы:

11

Кажется, это просто вопрос «перераспределения» фактора; нет необходимости вычислять частичные суммы или делать копию исходного вектора. Например,

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"

Здесь исходные уровни факторов распределены следующим образом:

 A  B  C  D  E 
18 23 35 36 38 

а потом становится

Other     C     D     E 
   41    35    36    38 

Это может быть удобно заключено в функцию. В пакете reshape есть combine_factor()функция , так что, думаю, она тоже может быть полезной.

Кроме того, поскольку вы, похоже, заинтересованы в извлечении данных, вы можете взглянуть на пакет Caret . Он имеет много полезных функций для предварительной обработки данных, включая такие функции, nearZeroVar()которые позволяют помечать предикторы с очень несбалансированным распределением наблюдаемых значений (см., Например, виньетка, пример данных, функции предварительной обработки, визуализации и другие функции , стр. 5 использования).

хл
источник
@ CHI Спасибо. Я изучил пакет caret и использовал его для настройки мета параметров. очень полезный!.
B_Miner
@chl +1, хороший. Я написал свою функцию исключительно потому, что код a [levels (a)% в% lf] <- «Other» не работает, поэтому я предположил, что изменение уровня фактора является сложным делом. Как обычно выяснилось, что R не сложно, я :)
mpiktas
@mpiktas Thx. Вы можете работать на векторном уровне, например, с a[as.character(a) %in% lf] <- lf[1]; a <- factor(droplevels(a), labels=c("Other",LETTERS[3:5])).
ЧЛ
+1. a [levels (a)% in% lf] <- "Other", безусловно, экономит тонну строк кода. Умно и эффективно!
Кристофер Аден
Но обратите внимание, что a [a == "a"] <- "Other" не сработает, что для меня вполне естественно предположить, что должно. Тем более, что [a == "a"] совершенно верно.
mpiktas
5

Единственная проблема с ответом Кристофера состоит в том, что он перепутает первоначальное упорядочение фактора. Вот мое исправление:

 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.levelsследующая функция. Я написал это некоторое время назад, так что я подозреваю, что могут быть лучшие способы достижения того, что он делает.

 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
источник
4

Я написал быструю функцию, которая достигнет этой цели. Я начинающий пользователь R, поэтому он может быть медленным с большими таблицами.

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 <- 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"
Кристофер Аден
источник
Спасибо за наблюдение, Джон. Я немного изменил это, чтобы сделать это фактором. Все, что я сделал, это переделал исходный вектор из таблицы, поэтому, если есть способ пропустить этот шаг, это будет быстрее.
Кристофер Аден
Спасибо всем кто ответил. Мой R слаб, но способность делать это с таким небольшим количеством строк кода является свидетельством того, насколько он силен и заставляет меня хотеть учиться.
B_Miner