Reordenar níveis de um fator sem alterar a ordem dos valores


124

Eu tenho um quadro de dados com algumas variáveis ​​numéricas e algumas factorvariáveis categóricas . A ordem dos níveis para esses fatores não é como eu quero que eles sejam.

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

Se eu mudar a ordem dos níveis, as letras não estarão mais com seus números correspondentes (meus dados são totalmente absurdos a partir deste ponto).

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

Eu simplesmente quero alterar a ordem dos níveis , portanto, ao plotar, as barras são mostradas na ordem desejada - o que pode diferir da ordem alfabética padrão.


1
Alguém poderia me dar uma dica de por que a atribuição a níveis (...) altera a ordem das entradas no quadro de dados, como mostram os arranjos na pergunta? Parece terrivelmente pouco intuitivo e indesejado para mim. Passei algum tempo depurando um problema causado por isso hoje. Eu estou pensando que pode haver uma razão para esse comportamento que eu não consigo ver, ou pelo menos uma explicação razoável para o motivo.
Anton

Respostas:


120

Use o levelsargumento de 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
Obrigado, isso funcionou. Por alguma estranha razão, o ggplot agora mudou corretamente a ordem na legenda, mas não na trama. Esquisito.
Cravos 03/03

7
O ggplot2 exigiu que eu alterasse a ordem dos níveis (veja acima) e a ordem dos valores do quadro de dados. df <- df [nrow (df): 1,] # reverse
crangos

@rangos, acho que o ggplot usa a ordem alfabética dos níveis e, às vezes, ignora os níveis de fator personalizados. Confirme e inclua o número da versão.
SMCI

22

um pouco mais, só para constar

## 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])

Você também pode achar útil Relevel e combine_factor .


2
Sua primeira resposta não funciona para mim. Mas isso funciona:reorder(df$letters, seq(4,1))
Alex Holcombe

1
Eu tenho uma situação muito estranha em que o 'pedido' funciona em um conjunto de dados, não em outro. No outro conjunto de dados, ele gera um erro "Erro na aplicação (X = X, INDEX = x, FUN = FUN, ...): o argumento" X "está ausente, sem padrão". Não tenho certeza qual é a solução para esse problema. Não consigo encontrar nenhuma diferença relevante entre os conjuntos de dados.
CoderGuy123

10

Desde que essa pergunta foi ativada pela última vez, Hadley lançou seu novo forcatspacote para manipulação de fatores e estou achando isso extremamente útil. Exemplos do quadro de dados do OP:

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

Para reverter níveis:

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

Para adicionar mais níveis:

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

E muitas outras fct_xxx()funções úteis .


Isso ainda está disponível?
Joshua Rosenberg #

1
Você quer escrever um código como este: df %>% mutate(letters = fct_rev(letters)).
jazzurro

9

portanto, o que você deseja, no léxico R, é alterar apenas os rótulos de uma determinada variável de fator (ou seja, deixar os dados e os níveis de fator inalterados).

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

considerando que você deseja alterar apenas o mapeamento de ponto de dados para rótulo e não os dados ou o esquema de fatores (como os pontos de dados são agrupados em posições individuais ou valores de fatores, pode ser útil saber como o mapeamento é originalmente definido quando você cria inicialmente o fator.

as regras são simples:

  • os rótulos são mapeados para os níveis pelo valor do índice (ou seja, o valor nos níveis [2] recebe o rótulo, rótulo [2]);
  • os níveis dos fatores podem ser definidos explicitamente passando-os através do argumento dos níveis ; ou
  • se nenhum valor for fornecido para o argumento de níveis, será usado o valor padrão, que é o resultado que chama exclusivamente no vetor de dados passado (para o argumento de dados );
  • os rótulos podem ser definidos explicitamente através do argumento labels; ou
  • se nenhum valor for fornecido para o argumento labels, será usado o valor padrão, que é apenas o vetor de níveis

1
Não sei por que isso não é tão votado quanto a resposta aceita. Isso é muito mais informativo.
Rambatino 12/04

12
Se você usar essa abordagem, seus dados serão etiquetados incorretamente.
Nazer

4
na verdade, sim, eu não sei o que fazer com isso, a resposta parece pretender rotular incorretamente os dados para fins de plotagem? ugh. revertida ao original. usuários cuidado
rawr

7

Lidar com fatores em R é um trabalho bastante peculiar, devo admitir ... Ao reordenar os níveis dos fatores, você não está reordenando os valores numéricos subjacentes. Aqui está uma pequena demonstração:

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

Agora, se você converter esse fator para numérico, obterá:

# 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

Como você pode ver ... alterando os níveis, você altera apenas os níveis (quem diria, hein?), Não os valores numéricos! Mas, quando você usa a factorfunção como sugerido por Jonathan Chang, algo diferente acontece: você mesmo altera os valores numéricos.

Você está recebendo erro mais uma vez, porque o faz levelse, em seguida, tente identificá-lo factor. Não faça !!! Você não usarlevels ou você vai bagunçar as coisas (a menos que você saiba exatamente o que está fazendo).

Uma sugestão: evite nomear seus objetos com um nome idêntico aos objetos de R ( dfé a função de densidade da distribuição F, lettersfornece letras minúsculas do alfabeto). Nesse caso em particular, seu código não seria defeituoso, mas às vezes pode ser ... mas isso pode criar confusão, e nós não queremos isso, queremos?!? =)

Em vez disso, use algo assim (voltarei desde o início mais uma vez):

> 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

Observe que você também pode nomear você data.framecom dfe em lettersvez de g, e o resultado será OK. Na verdade, esse código é idêntico ao que você postou, apenas os nomes são alterados. Esta partefactor(dtf$letter, levels = letters[4:1]) não geraria um erro, mas pode ser confusa!

Leia o ?factormanual completamente! Qual é a diferença entre factor(g, levels = letters[4:1])e factor(g, labels = letters[4:1])? O que há de semelhante em levels(g) <- letters[4:1]e g <- factor(g, labels = letters[4:1])?

Você pode colocar a sintaxe do ggplot, para que possamos ajudá-lo mais nesta questão!

Felicidades!!!

Editar:

ggplot2realmente requer a alteração de níveis e valores? Hm ... eu vou desenterrar este ...


3

Gostaria de acrescentar outro caso em que os níveis poderiam ser cadeias carregando números junto com alguns caracteres especiais: como exemplo abaixo

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

Os níveis padrão de xé:

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

Aqui, se quisermos reordenar os níveis dos fatores de acordo com o valor numérico, sem escrever explicitamente os níveis, o que poderíamos fazer é

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

Espero que isso possa ser considerado uma informação útil para futuros leitores.


0

Aqui está minha função para reordenar fatores de um determinado quadro de dados:

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

Uso: reorderFactors(df, "my_col", desired_level_order = c("how","I","want"))

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.