Подтвердить что ты не робот

Уровни уровня очистки (сведение нескольких уровней/меток)

Каков наиболее эффективный (то есть эффективный/подходящий) способ очистки фактора, содержащего несколько уровней, которые необходимо свернуть? То есть, как объединить два или более факторов в один.

Вот пример, где два уровня "Да" и "Y" должны быть свернуты до "Да", а "Нет" и "N" свернуты до "Нет":

## Given: 
x <- c("Y", "Y", "Yes", "N", "No", "H")   # The 'H' should be treated as NA

## expectedOutput
[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No  # <~~ NOTICE ONLY **TWO** LEVELS

Одним из вариантов является, конечно, очистка строк перед использованием с помощью sub и друзей.

Другой метод - разрешить дублируемую метку, а затем опустить их

## Duplicate levels ==> "Warning: deprecated"
x.f <- factor(x, levels=c("Y", "Yes", "No", "N"), labels=c("Yes", "Yes", "No", "No"))

## the above line can be wrapped in either of the next two lines
factor(x.f)      
droplevels(x.f) 

Однако есть более эффективный способ?


Хотя я знаю, что аргументы levels и labels должны быть векторами, я экспериментировал со списками и списками имен и названными векторами, чтобы увидеть, что происходит Излишне говорить, что ни одно из следующего не приблизило меня к моей цели.

  factor(x, levels=list(c("Yes", "Y"), c("No", "N")), labels=c("Yes", "No"))
  factor(x, levels=c("Yes", "No"), labels=list(c("Yes", "Y"), c("No", "N")))

  factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
  factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Yes="Y", Yes="Yes", No="No", No="N"))
  factor(x, levels=c("Yes", "No"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
4b9b3361

Ответ 1

ОБНОВЛЕНИЕ 2: см. ответ уве, в котором показан новый способ "затормозить", который быстро становится стандартом.

ОБНОВЛЕНИЕ 1: Дублированные ярлыки (но не уровни!) Теперь действительно разрешены (согласно моему комментарию выше); см. тим ответ.

ОРИГИНАЛЬНЫЙ ОТВЕТ, НО ПОЭТОМУ ПОЛЕЗНО И ИНТЕРЕСНО: Для этой цели есть малоизвестная опция передачи именованного списка в функцию levels. Имена списка должны быть желаемыми именами уровней, а элементы должны быть текущими именами, которые должны быть переименованы. Некоторые (в том числе ОП, см. комментарий Рикардо к ответу Тима) предпочитают это для удобства чтения.

x <- c("Y", "Y", "Yes", "N", "No", "H", NA)
x <- factor(x)
levels(x) <- list("Yes"=c("Y", "Yes"), "No"=c("N", "No"))
x
## [1] Yes  Yes  Yes  No   No   <NA>  <NA>
## Levels: Yes No

Как упомянуто в документации levels; также посмотрите примеры там.

значение: для метода "фактор",           вектор символьных строк длиной не менее числа           уровней "х", или именованный список, указывающий, как переименовать           уровни.

Это также можно сделать в одну строку, как это делает Марек: fooobar.com/questions/57284/...; здесь описывается колдовство levels<- fooobar.com/questions/57281/....

> 'levels<-'(factor(x), list(Yes=c("Y", "Yes"), No=c("N", "No")))
[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No

Ответ 2

В качестве вопроса под названием "Очистка уровней фактора (сведение нескольких уровней/меток), пакет forcats следует упомянуть здесь также, для полноты. forcats появился на CRAN в августе 2016 года.

Существует несколько удобных функций для очистки уровней факторов:

x <- c("Y", "Y", "Yes", "N", "No", "H") 

library(forcats)

Уровни коэффициента сглаживания в группы, определенные вручную

fct_collapse(x, Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes

Измените уровни факторов вручную

fct_recode(x, Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes

Автоматически перераспределять уровни факторов, сворачивать по мере необходимости

fun <- function(z) {
  z[z == "Y"] <- "Yes"
  z[z == "N"] <- "No"
  z[!(z %in% c("Yes", "No"))] <- NA
  z
}
fct_relabel(factor(x), fun)
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes

Обратите внимание, что fct_relabel() работает с уровнями факторов, поэтому он ожидает в качестве первого аргумента фактора. Две другие функции, fct_collapse() и fct_recode(), принимают также вектор символов, который является недокументированной функцией.

Уровень фактора переупорядочения с помощью первого вида

Ожидаемый выход, заданный OP, равен

[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No

Здесь уровни упорядочиваются по мере их появления в x, который отличается от значения по умолчанию (?factor: уровни по умолчанию сортируются по умолчанию).

Чтобы соответствовать ожидаемому результату, это может быть достигнуто с помощью fct_inorder() перед свертыванием уровней:

fct_collapse(fct_inorder(x), Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
fct_recode(fct_inorder(x), Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")

Оба возвращают ожидаемый результат с уровнями в том же порядке, теперь.

Ответ 3

Возможно, именованный вектор в качестве ключа может быть полезен:

> factor(unname(c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA)[x]))
[1] Yes  Yes  Yes  No   No   <NA>
Levels: No Yes

Это выглядит очень похоже на вашу последнюю попытку... но это работает: -)

Ответ 4

Другой способ - создать таблицу, содержащую отображение:

# stacking the list from Aaron answer
fmap = stack(list(Yes = c("Y", "Yes"), No = c("N", "No")))

fmap$ind[ match(x, fmap$values) ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

# or...

library(data.table)
setDT(fmap)[x, on=.(values), ind ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

Я предпочитаю этот путь, поскольку он оставляет за собой легко проверенный объект, суммирующий карту; и код data.table выглядит так же, как и любое другое соединение в этом синтаксисе.


Конечно, если вы не хотите, чтобы объект, подобный fmap, суммировал изменение, он может быть "однострочным":

library(data.table)
setDT(stack(list(Yes = c("Y", "Yes"), No = c("N", "No"))))[x, on=.(values), ind ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

Ответ 5

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

levels(df$var1)
# "0" "1" "Z"
summary(df$var1)
#    0    1    Z 
# 7012 2507    8 
levels(df$var1) <- list("0"=c("Z", "0"), "1"=c("1"))
levels(df$var1)
# "0" "1"
summary(df$var1)
#    0    1 
# 7020 2507

Ответ 6

Начиная с версии 3.5.0 (2018-04-23) вы можете сделать это в одной простой и понятной строке:

x = c("Y", "Y", "Yes", "N", "No", "H") # The 'H' should be treated as NA

tmp = factor(x, levels= c("Y", "Yes", "N", "No"), labels= c("Yes", "Yes", "No", "No"))
tmp
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: Yes No

1 строка, отображает несколько значений на один и тот же уровень, устанавливает NA для отсутствующих уровней "- h/t @Aaron

Ответ 7

Я не знаю вашего реального случая использования, но strtrim будет здесь полезен...

factor( strtrim( x , 1 ) , levels = c("Y" , "N" ) , labels = c("Yes" , "No" ) )
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: Yes No

Ответ 8

Подобно подходу @Aaron, но немного проще:

x <- c("Y", "Y", "Yes", "N", "No", "H")
x <- factor(x)
# levels(x)  
# [1] "H"   "N"   "No"  "Y"   "Yes"
# NB: the offending levels are 1, 2, & 4
levels(x)[c(1,2,4)] <- c(NA, "No", "Yes")
x
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

Ответ 9

Вы можете использовать следующую функцию для объединения/сбрасывания нескольких факторов:

combofactor <- function(pattern_vector,
         replacement_vector,
         data) {
 levels <- levels(data)
 for (i in 1:length(pattern_vector))
      levels[which(pattern_vector[i] == levels)] <-
        replacement_vector[i]
 levels(data) <- levels
  data
}

Пример:

Инициализировать x

x <- factor(c(rep("Y",20),rep("N",20),rep("y",20),
rep("yes",20),rep("Yes",20),rep("No",20)))

Проверьте структуру

str(x)
# Factor w/ 6 levels "N","No","y","Y",..: 4 4 4 4 4 4 4 4 4 4 ...

Используйте функцию:

x_new <- combofactor(c("Y","N","y","yes"),c("Yes","No","Yes","Yes"),x)

Повторно проверьте структуру:

str(x_new)
# Factor w/ 2 levels "No","Yes": 2 2 2 2 2 2 2 2 2 2 ...

Ответ 10

Прежде всего отметим, что в этом конкретном случае мы можем использовать частичное совпадение:

x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c("Yes","No")
x <- factor(y[pmatch(x,y,duplicates.ok = TRUE)])
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

В более общем случае я бы пошел с dplyr::recode:

library(dplyr)
x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c(Y="Yes",N="No")
x <- recode(x,!!!y)
x <- factor(x,y)
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: Yes No

Немного изменен, если исходная точка является фактором:

x <- factor(c("Y", "Y", "Yes", "N", "No", "H"))
y <- c(Y="Yes",N="No")
x <- recode_factor(x,!!!y)
x <- factor(x,y)
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: Yes No