J'ai un cadre de données contenant un facteur. Lorsque je crée un sous-ensemble de ce cadre de données à l'aide de subset()
ou d'une autre fonction d'indexation, un nouveau cadre de données est créé. Cependant, la variable facteur conserve tous ses niveaux d'origine, même s'ils n'existent pas dans le nouveau bloc de données.
Cela crée des maux de tête lors du traçage par facettes ou de l'utilisation de fonctions qui reposent sur des niveaux de facteurs.
Quel est le moyen le plus succinct de supprimer des niveaux d’un facteur dans mon nouveau bloc 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"
Tout ce que vous avez à faire est d’appliquer facteur () à votre variable après avoir défini le sous-ensemble:
> 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
À partir de l'exemple de page de facteur:
factor(ff) # drops the levels that do not occur
Pour supprimer des niveaux de toutes les colonnes de facteur dans une image, vous pouvez utiliser:
subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)
Depuis la version R 2.12, il existe une fonction droplevels()
.
levels(droplevels(subdf$letters))
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. (reorder est votre ami pour les parcelles)
C’est un problème connu, et une solution possible est fournie par drop.levels()
dans le package gdata 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 existe également la fonction dropUnusedLevels
dans le package Hmisc . Cependant, cela ne fonctionne que si vous modifiez l'opérateur de sous-ensemble [
.
En corollaire, une approche directe colonne par colonne est une 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"
Une autre façon de faire la même chose mais avec dplyr
library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)
Modifier:
Travaille aussi! Merci à agenis
subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
Voici une autre façon, qui, à mon avis, équivaut à l'approche factor(..)
:
> 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"
Par souci d'exhaustivité, il y a maintenant aussi fct_drop
dans le package forcats
http://forcats.tidyverse.org/reference/fct_drop.html .
Il diffère de droplevels
dans la façon dont il traite NA
:
f <- factor(c("a", "b", NA), exclude = NULL)
droplevels(f)
# [1] a b <NA>
# Levels: a b <NA>
forcats::fct_drop(f)
# [1] a b <NA>
# Levels: a b
En regardant les droplevels
méthodes code dans la source R, vous pouvez voir il encapsule la fonction factor
Cela signifie que vous pouvez fondamentalement recréer la colonne avec la fonction factor
.
En dessous de la méthode data.table, pour supprimer les niveaux de toutes les colonnes de facteurs.
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"
voici une façon de le faire
varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
C'est odieux. Voici comment je le fais habituellement pour éviter de charger d'autres paquets:
levels(subdf$letters)<-c("a","b","c",NA,NA)
qui vous obtient:
> 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 (lettres subdf $), donc quelque chose comme:
levels(subdf$letters)<-c(NA,"a","c",NA,"b")
ne fonctionnera pas.
Ce n'est évidemment pas idéal lorsque vous avez beaucoup de niveaux, mais pour quelques-uns, c'est rapide et facile.
J'ai écrit des fonctions utilitaires pour le faire. Maintenant que je connais les drop.levels de gdata, le résultat est assez similaire. Les voici (de ici ):
present_levels <- function(x) intersect(levels(x), x)
trim_levels <- function(...) UseMethod("trim_levels")
trim_levels.factor <- function(x) factor(x, levels=present_levels(x))
trim_levels.data.frame <- function(x) {
for (n in names(x))
if (is.factor(x[,n]))
x[,n] = trim_levels(x[,n])
x
}
Fil très intéressant, j'ai particulièrement aimé l'idée de simplement factoriser à nouveau la sous-sélection. J'ai eu le même problème avant et je viens de convertir en personnage, puis de nouveau en factor.
df <- data.frame(letters=letters[1:5],numbers=seq(1:5))
levels(df$letters)
## [1] "a" "b" "c" "d" "e"
subdf <- df[df$numbers <= 3]
subdf$letters<-factor(as.character(subdf$letters))
Malheureusement, factor () ne semble pas fonctionner avec rxDataStep de RevoScaleR. Je le fais en deux étapes: 1) Convertir en caractère et stocker dans un cadre de données externe temporaire (.xdf) . 2) Convertir en facteur et stocker dans un cadre de données externe définitif. Ceci élimine les niveaux de facteur non utilisés, sans charger toutes les données en mémoire.
# Step 1) Converts to character, in temporary xdf file:
rxDataStep(inData = "input.xdf", outFile = "temp.xdf", transforms = list(VAR_X = as.character(VAR_X)), overwrite = T)
# Step 2) Converts back to factor:
rxDataStep(inData = "temp.xdf", outFile = "output.xdf", transforms = list(VAR_X = as.factor(VAR_X)), overwrite = T)