Изменение порядка уровней фактора без изменения порядка значений

124

У меня есть фрейм данных с некоторыми числовыми переменными и некоторыми категориальными factorпеременными. Порядок уровней для этих факторов не такой, как я хочу.

numbers <- 1:4
letters <- factor(c("a", "b", "c", "d"))
df <- data.frame(numbers, letters)
df
#   numbers letters
# 1       1       a
# 2       2       b
# 3       3       c
# 4       4       d

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

levels(df$letters) <- c("d", "c", "b", "a")
df
#   numbers letters
# 1       1       d
# 2       2       c
# 3       3       b
# 4       4       a

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

crangos
источник
1
Может ли кто-нибудь дать мне подсказку, почему присвоение уровням (...) изменяет порядок записей во фрейме данных, как показывает crangos в вопросе? Мне это кажется ужасно нелогичным и нежелательным. Сегодня я сам потратил некоторое время на отладку проблемы, вызванной этим. Я думаю, что может быть причина такого поведения, которую я не вижу, или, по крайней мере, разумное объяснение того, почему это происходит.
Антон

Ответы:

120

Используйте levelsаргумент factor:

df <- data.frame(f = 1:4, g = letters[1:4])
df
#   f g
# 1 1 a
# 2 2 b
# 3 3 c
# 4 4 d

levels(df$g)
# [1] "a" "b" "c" "d"

df$g <- factor(df$g, levels = letters[4:1])
# levels(df$g)
# [1] "d" "c" "b" "a"

df
#   f g
# 1 1 a
# 2 2 b
# 3 3 c
# 4 4 d
Джонатан Чанг
источник
1
Спасибо, это сработало. По какой-то странной причине ggplot теперь правильно меняет порядок в легенде, но не в сюжете. Weird.
Crangos 03
7
ggplot2 потребовал от меня изменить как порядок уровней (см. выше), так и порядок значений во фрейме данных. df <- df [nrow (df): 1,] # reverse
crangos 04
@crangos, я думаю, что ggplot использует алфавитный порядок уровней и иногда игнорирует пользовательские уровни факторов. Подтвердите и укажите номер версии.
smci
22

еще немного, для протокола

## reorder is a base function
df$letters <- reorder(df$letters, new.order=letters[4:1])

library(gdata)
df$letters <- reorder.factor(df$letters, letters[4:1])

Вы также можете найти полезные Relevel и comb_factor .

Джордж Донтас
источник
2
Ваш первый ответ мне не подходит. Но это работает:reorder(df$letters, seq(4,1))
Алекс Холкомб
1
У меня очень странная ситуация, когда «переупорядочивание» работает с одним набором данных, а не с другим. В другом наборе данных выдается ошибка «Ошибка в приложении (X = X, INDEX = x, FUN = FUN, ...): аргумент« X »отсутствует, значение по умолчанию отсутствует». Не знаю, как решить эту проблему. Я не могу найти существенной разницы между наборами данных.
CoderGuy123
10

Поскольку этот вопрос был активен в последний раз, Хэдли выпустил свой новый forcatsпакет для управления факторами, и я нахожу его чрезвычайно полезным. Примеры из фрейма данных OP:

levels(df$letters)
# [1] "a" "b" "c" "d"

Чтобы изменить уровни:

library(forcats)
fct_rev(df$letters) %>% levels
# [1] "d" "c" "b" "a"

Чтобы добавить больше уровней:

fct_expand(df$letters, "e") %>% levels
# [1] "a" "b" "c" "d" "e"

И еще много полезных fct_xxx()функций.

Джо
источник
Это все еще доступно?
Джошуа Розенберг
1
Вы хотите , чтобы написать такой код: df %>% mutate(letters = fct_rev(letters)).
jazzurro
9

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

df$letters = factor(df$letters, labels=c("d", "c", "b", "a"))

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

правила просты:

  • метки отображаются на уровни по значению индекса (т. е. значению на уровнях [2] присваивается метка, метка [2]);
  • уровни факторов могут быть установлены явно, передав их через аргумент уровней ; или
  • если для аргумента уровней не указано значение, используется значение по умолчанию, которое является результатом вызова unique для переданного вектора данных (для аргумента данных );
  • метки могут быть установлены явно через аргумент меток; или
  • если для аргумента меток не указано значение, используется значение по умолчанию, которое представляет собой просто вектор уровней
Дуг
источник
1
Я не знаю, почему за этот ответ проголосовали не так, как за принятый ответ. Это намного информативнее.
Рамбатино
12
Если вы воспользуетесь этим подходом, ваши данные будут неправильно маркированы.
Назер
4
на самом деле да, я не знаю, что с этим делать, ответ, кажется, намеревается неправильно пометить данные для построения графика? тьфу. откатился к оригиналу.
Осторожно,
7

Я должен признать, что иметь дело с факторами в R - довольно своеобразная работа ... Меняя порядок уровней факторов, вы не меняете порядок базовых числовых значений. Вот небольшая демонстрация:

> numbers = 1:4
> letters = factor(letters[1:4])
> dtf <- data.frame(numbers, letters)
> dtf
  numbers letters
1       1       a
2       2       b
3       3       c
4       4       d
> sapply(dtf, class)
  numbers   letters 
"integer"  "factor" 

Теперь, если вы переведете этот коэффициент в числовой, вы получите:

# return underlying numerical values
1> with(dtf, as.numeric(letters))
[1] 1 2 3 4
# change levels
1> levels(dtf$letters) <- letters[4:1]
1> dtf
  numbers letters
1       1       d
2       2       c
3       3       b
4       4       a
# return numerical values once again
1> with(dtf, as.numeric(letters))
[1] 1 2 3 4

Как видите ... изменяя уровни, вы меняете только уровни (кто бы сказал, а?), А не числовые значения! Но когда вы используете factorфункцию, как предложил @Jonathan Chang, происходит нечто иное: вы меняете сами числовые значения.

Вы снова получаете ошибку, потому что вы делаете это, levelsа затем пытаетесь исправить ее с помощью factor. Не делай этого !!! Вы не используете , levelsили вы будете натворить (если вы точно не знаете , что вы делаете).

Одно небольшое предложение: избегайте называть свои объекты такими же именами, как объекты R ( dfэто функция плотности для распределения F, lettersдает буквы алфавита в нижнем регистре). В этом конкретном случае ваш код не будет ошибочным, но иногда это может быть ... но это может создать путаницу, а мы этого не хотим, не так ли?!? знак равно

Вместо этого используйте что-то вроде этого (я снова начну с самого начала):

> dtf <- data.frame(f = 1:4, g = factor(letters[1:4]))
> dtf
  f g
1 1 a
2 2 b
3 3 c
4 4 d
> with(dtf, as.numeric(g))
[1] 1 2 3 4
> dtf$g <- factor(dtf$g, levels = letters[4:1])
> dtf
  f g
1 1 a
2 2 b
3 3 c
4 4 d
> with(dtf, as.numeric(g))
[1] 4 3 2 1

Обратите внимание, что вы также можете назвать себя data.frameс помощью dfи lettersвместо g, и результат будет в порядке. Собственно, этот код идентичен тому, который вы опубликовали, только названия изменены. Эта часть factor(dtf$letter, levels = letters[4:1])не вызовет ошибки, но может сбивать с толку!

Внимательно прочтите ?factorруководство! В чем разница между factor(g, levels = letters[4:1])и factor(g, labels = letters[4:1])? Что похоже на levels(g) <- letters[4:1]и g <- factor(g, labels = letters[4:1])?

Вы можете использовать синтаксис ggplot, чтобы мы могли помочь вам в этом подробнее!

Ура !!!

Редактировать:

ggplot2на самом деле требуется изменить и уровни, и значения? Хм ... Я откопаю это ...

aL3xa
источник
3

Я хочу добавить еще один случай, когда уровни могут быть строками, содержащими числа вместе с некоторыми специальными символами: как в примере ниже

df <- data.frame(x = c("15-25", "0-4", "5-10", "11-14", "100+"))

Уровни по умолчанию x:

df$x
# [1] 15-25 0-4   5-10  11-14 100+ 
# Levels: 0-4 100+ 11-14 15-25 5-10

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

library(gtools)
df$x <- factor(df$x, levels = mixedsort(df$x))

df$x
# [1] 15-25 0-4   5-10  11-14 100+ 
# Levels: 0-4 5-10 11-14 15-25 100+
as.numeric(df$x)
# [1] 4 1 2 3 5

Надеюсь, это можно считать полезной информацией для будущих читателей.

joel.wilson
источник
0

Вот моя функция для изменения порядка факторов данного фрейма данных:

reorderFactors <- function(df, column = "my_column_name", 
                           desired_level_order = c("fac1", "fac2", "fac3")) {

  x = df[[column]]
  lvls_src = levels(x) 

  idxs_target <- vector(mode="numeric", length=0)
  for (target in desired_level_order) {
    idxs_target <- c(idxs_target, which(lvls_src == target))
  }

  x_new <- factor(x,levels(x)[idxs_target])

  df[[column]] <- x_new

  return (df)
}

Использование: reorderFactors(df, "my_col", desired_level_order = c("how","I","want"))

Boern
источник