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

Відповіді:


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 

Це може бути зручно зафіксовано у функції. У пакеті переформатування є combine_factor()функція , тому, мабуть, вона може бути корисною.

Крім того, як вам здається, що цікавляться обробкою даних, ви можете ознайомитись з пакетом карет . Він має багато корисних функцій для попередньої обробки даних, включаючи такі функції, nearZeroVar()які дозволяють позначити передбачувачів із дуже незбалансованим розподілом спостережуваних значень (див., Наприклад , віньєтку, приклади даних, функції попередньої обробки, візуалізації та інші функції , стор. 5, наприклад, 5). використання).


@CHI Дякую Я вивчив пакет карет і використовував його для налаштування мета-параметрів. дуже корисний!.
B_Miner

@chl +1, приємний. Я написав свою функцію виключно тому, що код a [рівнів (a)% у% lf] <- "Інше" не працює, тому я припустив, що зміна рівня фактора є складною справою. Як завжди виявилося, що R не є складним, я :)
mpiktas

@mpiktas Thx. Ви можете працювати на рівні вектора з , наприклад, a[as.character(a) %in% lf] <- lf[1]; a <- factor(droplevels(a), labels=c("Other",LETTERS[3:5])).
chl

+1. a [рівні (a)% у% lf] <- "Інше" обов'язково економить тонну рядків коду. Розумний та ефективний!
Крістофер Аден

Але зауважте, що a [a == "a"] <- "Other" не спрацює, що для мене цілком природно вважати, що воно повинно. Тим більше, що a [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)
}

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.