Question Abaisser les niveaux de facteur dans une trame de données subsettée


J'ai une trame de données contenant un facteur. Lorsque je crée un sous-ensemble de ce bloc de données en utilisant subset() ou une autre fonction d'indexation, un nouveau bloc de données est créé. Cependant, la variable factor conserve tous ses niveaux d'origine, même s'ils n'existent pas dans la nouvelle trame de données.

Cela crée des maux de tête lors de la réalisation de diagrammes à facettes ou de l'utilisation de fonctions qui dépendent des niveaux de facteur.

Quelle est la manière la plus succincte de supprimer les niveaux d'un facteur dans ma nouvelle trame de données?

Voici mon exemple:

df <- data.frame(letters=letters[1:5],
                    numbers=seq(1:5))

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

subdf <- subset(df, numbers <= 3)
##   letters numbers
## 1       a       1
## 2       b       2
## 3       c       3    

## but the levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"

433
2017-07-28 18:21


origine


Réponses:


Tout ce que vous devez avoir à faire est d'appliquer de nouveau factor () à votre variable après avoir sous-défini:

> subdf$letters
[1] a b c
Levels: a b c d e
subdf$letters <- factor(subdf$letters)
> subdf$letters
[1] a b c
Levels: a b c

MODIFIER

De l'exemple de la page de facteur:

factor(ff)      # drops the levels that do not occur

Pour supprimer des niveaux de toutes les colonnes de facteur dans une base de données, vous pouvez utiliser:

subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)

333
2017-07-28 22:41



Depuis R version 2.12, il y a un droplevels() fonction.

levels(droplevels(subdf$letters))

454
2017-11-26 11:37



Si vous ne voulez pas ce comportement, n'utilisez pas de facteurs, utilisez plutôt des vecteurs de caractères. Je pense que cela a plus de sens que de réparer les choses par la suite. Essayez ce qui suit avant de charger vos données avec read.table ou read.csv:

options(stringsAsFactors = FALSE)

L'inconvénient est que vous êtes limité à l'ordre alphabétique. (Réorganiser est votre ami pour les parcelles)


35
2017-07-28 23:53



C'est un problème connu, et une solution possible est fournie par drop.levels() dans le gdata paquet où votre exemple devient

> drop.levels(subdf)
  letters numbers
1       a       1
2       b       2
3       c       3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"

Il y a aussi dropUnusedLevels fonctionner dans le Hmisc paquet. Cependant, cela ne fonctionne qu'en modifiant l'opérateur du sous-ensemble [ et n'est pas applicable ici.

En corollaire, une approche directe par colonne est simple as.factor(as.character(data)):

> levels(subdf$letters)
[1] "a" "b" "c" "d" "e"
> subdf$letters <- as.factor(as.character(subdf$letters))
> levels(subdf$letters)
[1] "a" "b" "c"

33
2017-07-28 18:37



Une autre façon de faire la même chose mais avec dplyr

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)

Modifier: 

Fonctionne aussi! Grâce à agenis

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)

14
2017-07-15 11:14



Voici une autre manière, qui je crois est équivalente à la factor(..) approche:

> df <- data.frame(let=letters[1:5], num=1:5)
> subdf <- df[df$num <= 3, ]

> subdf$let <- subdf$let[ , drop=TRUE]

> levels(subdf$let)
[1] "a" "b" "c"

10
2017-07-29 03:40



C'est odieux. C'est comme ça que je le fais habituellement, pour éviter de charger d'autres paquets:

levels(subdf$letters)<-c("a","b","c",NA,NA)

ce qui vous amène:

> subdf$letters
[1] a b c
Levels: a b c

Notez que les nouveaux niveaux remplaceront tout ce qui occupe leur index dans les anciens niveaux (sous-lettres $ $), donc quelque chose comme:

levels(subdf$letters)<-c(NA,"a","c",NA,"b")

ne fonctionnera pas.

Ce n'est évidemment pas idéal quand vous avez beaucoup de niveaux, mais pour quelques-uns, c'est rapide et facile.


6
2017-07-28 18:44



voici une façon de le faire

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]

6
2018-01-31 03:25



En regardant le droplevels méthodes code dans la source R, vous pouvez voir ça enveloppe à factor fonction. Cela signifie que vous pouvez essentiellement recréer la colonne avec factor fonction.
Ci-dessous la méthode data.table pour supprimer les niveaux de toutes les colonnes de facteur.

library(data.table)
dt = data.table(letters=factor(letters[1:5]), numbers=seq(1:5))
levels(dt$letters)
#[1] "a" "b" "c" "d" "e"
subdt = dt[numbers <= 3]
levels(subdt$letters)
#[1] "a" "b" "c" "d" "e"

upd.cols = sapply(subdt, is.factor)
subdt[, names(subdt)[upd.cols] := lapply(.SD, factor), .SDcols = upd.cols]
levels(subdt$letters)
#[1] "a" "b" "c"

6
2017-12-09 14:56