R: уровни факторов, перекодировать остальное в «другое»

Я использую факторы несколько нечасто и в целом нахожу их понятными, но я часто не уверен в деталях конкретных операций. В настоящее время я кодирую / сворачиваю категории с несколькими наблюдениями в «другие» и ищу быстрый способ сделать это - у меня, возможно, 20 уровней переменной, но я заинтересован в свертывании нескольких из них в один.

data <- data.frame(employees = sample.int(1000,500),
                   naics = sample(c('621111','621112','621210','621310','621320','621330','621340','621391','621399','621410','621420','621491','621492','621493','621498','621511','621512','621610','621910','621991','621999'),
                                  100, replace=T))

Вот мои уровни интереса и их обозначения в отдельных векторах.

#levels and labels
top8 <-c('621111','621210','621399','621610','621330',
         '621310','621511','621420','621320')
top8_desc <- c('Offices of physicians',
               'Offices of dentists',
               'Offices of all other miscellaneous health practitioners',
               'Home health care services',
               'Offices of Mental Health Practitioners',
               'Offices of chiropractors',
               'Medical Laboratories',
               'Outpatient Mental Health and Substance Abuse Centers',
               'Offices of optometrists')

Я мог бы использовать вызов factor(), перечислить их все, классифицируя как «другие» каждый раз, когда в категории было мало наблюдений.

Предполагая, что top8 и top8_desc, указанные выше, являются фактическими восьмёрками, как лучше всего объявить data$naics как факторную переменную, чтобы значения в top8 были правильно закодированы, а все остальное перекодировано как other?


person ako    schedule 20.03.2013    source источник


Ответы (4)


Я думаю, что самый простой способ - переименовать все наики, не входящие в восьмерку лучших, на особое значение.

data$naics[!(data$naics %in% top8)] = -99

Затем вы можете использовать опцию «исключить» при превращении его в фактор

factor(data$naics, exclude=-99)
person kith    schedule 20.03.2013
comment
Хм, это включает в себя фактическое отбрасывание данных, а не изменение категоризации, но, вероятно, именно это кодирование как фактор делает в первую очередь. Полагаю, это не имеет большого значения. - person ako; 21.03.2013
comment
Вы всегда можете сделать дополнительный столбец в фрейме данных с преобразованными кодами. - person kith; 21.03.2013
comment
Я пробовал этот вариант вашего ответа: levels(data$naics)[which(!levels(data$naics)%in%top8)] <- "other" - person ako; 21.03.2013

Вы можете использовать forcats::fct_other():

library(forcats)
data$naics <- fct_other(data$naics, keep = top8, other_level = 'other')

Или используя fct_other() как часть dplyr::mutate():

library(dplyr)
data <- mutate(data, naics = fct_other(naics, keep = top8, other_level = 'other')) 

data %>% head(10)
   employees  naics
1        420  other
2        264  other
3        189  other
4        157 621610
5        376 621610
6        236  other
7        658 621320
8        959 621320
9        216  other
10       156  other

Обратите внимание, что если аргумент other_level не установлен, для других уровней по умолчанию используется «Другой» (заглавная буква «O»).

И наоборот, если у вас есть только несколько факторов, которые вы хотите преобразовать в «другое», вы можете вместо этого использовать аргумент drop:

data %>%  
  mutate(keep_fct = fct_other(naics, keep = top8, other_level = 'other'),
         drop_fct = fct_other(naics, drop = top8, other_level = 'other')) %>% 
  head(10)

   employees  naics keep_fct drop_fct
1        474 621491    other   621491
2        805 621111   621111    other
3        434 621910    other   621910
4        845 621111   621111    other
5        243 621340    other   621340
6        466 621493    other   621493
7        369 621111   621111    other
8         57 621493    other   621493
9        144 621491    other   621491
10       786 621910    other   621910

dpylr также имеет recode_factor(), где вы можете установить аргумент .default на другой, но с большим количеством уровней для перекодирования, как в этом примере, может быть утомительно:

data %>% 
   mutate(naices = recode_factor(naics, `621111` = '621111', `621210` = '621210', `621399` = '621399', `621610` = '621610', `621330` = '621330', `621310` = '621310', `621511` = '621511', `621420` = '621420', `621320` = '621320', .default = 'other'))
person sbha    schedule 06.04.2018

Поздний вход

Вот оболочка для plyr::mapvalues, которая позволяет аргумент a remaining (ваш other)

library(plyr)

Mapvalues <- function(x, from, to, warn_missing= TRUE, remaining = NULL){
  if(!is.null(remaining)){
    therest <- setdiff(x, from)
    from <- c(from, therest)
    to <- c(to, rep_len(remaining, length(therest)))
  }
  mapvalues(x, from, to, warn_missing)
}
# replace the remaining values with "other"
Mapvalues(data$naics, top8, top8_desc,remaining = 'other')
# leave the remaining values alone
Mapvalues(data$naics, top8, top8_desc)
person mnel    schedule 21.08.2013

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

ds - это имеющийся набор данных типа data.frame, я делаю это для всех столбцов, которые появляются в cat_var_names как факторы.

cat_var_names <- names(clean_base[sapply(clean_base, is.factor)])

recodeLevels <- function (ds = clean_base, var_list = cat_var_names, mp = 0.01, ml = 25) {
  # remove less frequent levels in factor
  # 
  n <- nrow(ds)
  # keep levels with more then mp percent of cases
  for (i in var_list){
    keep <- levels(ds[[i]])[table(ds[[i]]) > mp * n]
    levels(ds[[i]])[which(!levels(ds[[i]])%in%keep)] <- "other"
  }

  # keep top ml levels
  for (i in var_list){
    keep <- names(sort(table(ds[i]),decreasing=TRUE)[1:ml])
    levels(ds[[i]])[which(!levels(ds[[i]])%in%keep)] <- "other"
  }
  return(ds)
}
person Hugo Koopmans    schedule 20.08.2013
comment
Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением - вы всегда можете комментировать свои собственные сообщения, и как только у вас будет достаточно репутации, вы сможете комментировать любое сообщение. - person Sliq; 20.08.2013