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


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

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


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 тепер правильно змінив порядок в легенді, але не в сюжеті. Дивно.
crangos

7
ggplot2 вимагав від мене змінити як порядок рівнів (див. вище), так і порядок значень кадру даних. df <- df [nrow (df): 1,] # зворотний
crangos

@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пакет для маніпулювання факторами, і я вважаю це надзвичайно корисним. Приклади з даних даних ОП:

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)).
джазурро

9

так що ви хочете, в R лексиконі, щоб змінити тільки мітки для даного змінного фактора (тобто, залиште дані, а також фактор рівнів , без змін).

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

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

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

  • мітки відображаються на рівні за значенням індексу (тобто значення на рівнях [2] задається міткою, міткою [2]);
  • рівні факторів можна встановити явно, передаючи їх через аргумент рівнів ; або
  • якщо для аргументу рівнів не вказано значення, використовується значення за замовчуванням, яке є результатом виклику унікальним у переданому векторі даних (для аргументу даних );
  • мітки можна встановити явно за допомогою аргументу міток; або
  • якщо для аргументу міток не вказано значення, використовується значення за замовчуванням, яке є просто рівнем вектора

1
Я не знаю, чому це не так проголосовано, як прийнята відповідь. Це набагато інформативніше.
Рамбатіно

12
Якщо ви використовуєте такий підхід, ваші дані неправильно позначаються.
Назер

4
насправді так, я не знаю, що з цим робити, але, здається, відповідь має намір помилково позначити ці дані заради створення? тьфу. повернуто до оригіналу. користувачі будьте обережні
rawr

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або ви будете накоїти (якщо ви точно не знаєте , що ви робите).

Одна пропозиція lil: уникайте називати ваші об'єкти ідентичною назвою об'єктів 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насправді вимагає змінити і рівні, і значення? Гм ... я це викопаю ...


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

Сподіваюсь, це можна вважати корисною інформацією для майбутніх читачів.


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"))

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