Factors são... Números?

A utilização de objetos da classe factor no R pode surpreender usuários novos, uma vez que a representação e manipulação destes sugere que são similares a manipulação de strings, porém muitas das vezes o usuário acaba tendo uma surpresa desagradável, assim como um comportamento não esperado de objetos deste tipo. Mas antes de tudo, vamos esclarecer uma coisa: Factors não são e nem devem ser vistos como strings e sim como objetos numéricos (para ser mais preciso, inteiros).

Antes de dissertarmos sobre a polêmica afirmação, vamos fazer uma revisão sobre os objetos da classe factor. Factors são vetores que podem conter apenas valores pré-definidos e são utilizados para a representação de dados categóricos.



f = factor(c('a', 'b', 'a', 'a', 'b'))
> f
[1] a b a a b
Levels: a b

Se tentarmos inserir um elemento que não seja 'a' ou 'b', uma mensagem de warning será exibida, e o valor atribuído será NA.

> f[6] <- 'c'
Mensagens de aviso perdidas:
In `[<-.factor`(`*tmp*`, 6, value = "c") :
  invalid factor level, NA generated
> f
[1] a    b    a    a    b    <NA>
Levels: a b

Então podemos dizer que factors podem ser muito úteis quando sabemos todos os valores que uma variável pode assumir, mesmo que nem todos estes valores sejam observados no conjunto de dados. Se agruparmos um vetor de strings e um de factors, o resultado é diferente.

> genero <- c('f', 'f', 'f')
> genero_fator <- factor(genero, levels=c('m', 'f'))
> table(genero)
genero
f 
3 
> table(genero_fator)
genero_fator
m f 
0 3 

Mesmo que o gênero masculino não apareça, a classe factor sabe que ele existe e na hora da construção da tabela ele é levado em consideração, diferente da classe character, que considera apenas o que é observado. Factors são construídos em cima dos inteiros considerando dois atributos: class como factor e levels como os valores que podem ser assumidos. Podemos inspecionar melhor o objeto genero_fator.

> attributes(genero_fator)
$levels
[1] "m" "f"

$class
[1] "factor"

> str(genero_fator)
 Factor w/ 2 levels "m","f": 2 2 2

> typeof(genero_fator)
[1] "integer"


E com isso, se adicionarmos mais uma observação e vermos a estrutura do objeto, podemos ver sua real representação. Na verdade o que está por trás do genero_fator não é o vetor ['f', 'f', 'f'] e sim [2, 2, 2]. Se adicionarmos um elemento, poderemos ver este comportamento de forma mais clara.

> genero_fator[4] <- 'm'
> str(genero_fator)
 Factor w/ 2 levels "m","f": 2 2 2 1

Outro problema recorrente com a utilização dos fatores é na leitura de arquivos de texto. A opção default do R é carregar cada variável codificada como texto na forma de factors, a ao manipular este conjunto de dados os desavisados podem encontrar algum tipo de problema. Para ler um dataset transformando as variáveis de texto em characters, basta adicionar stringsAsFactors=FALSE como argumento da sua função de leitura. Sobre a importação destas variáveis como factor, pode acontecer também de valores faltantes (vulgo NA) estarem codificados de alguma forma especial, como por exemplo "-" ou ".". Então mesmo que a coluna tenha apenas números, será importada como factor. Inclusive se fizer as.numeric() você não terá problemas, só que esta transformação irá obedecer a ordem hierarquica dos fatos, não o número real.

Portanto, tenham muito cuidado ao manipular factors, pois embora pareçam com characters, na verdade eles são inteiros. Muitas das funções que podem ser utilizadas em strings não funcionam para factors, e caso funcione, existe a chance de o objeto retornar um valor inesperado.

Nenhum comentário:

Postar um comentário