J'ai un cadre de données. Appelons-le bob
:
> head(bob)
phenotype exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
Je voudrais concaténer les lignes de ce cadre de données (ce sera une autre question). Mais regarde:
> class(bob$phenotype)
[1] "factor"
Les colonnes de Bob
sont des facteurs. Donc, par exemple:
> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)" "c(3, 3, 3, 3, 3, 3)"
[3] "c(29, 29, 29, 30, 30, 30)"
Je ne commence pas à comprendre cela, mais je suppose que ce sont des indices dans les niveaux des facteurs des colonnes (de la cour du roi caractacus) de bob
? Pas ce dont j'ai besoin.
Étrangement, je peux parcourir les colonnes de bob
à la main et faire
bob$phenotype <- as.character(bob$phenotype)
qui fonctionne bien. Et, après quelques saisies, je peux obtenir un nom data.frame dont les colonnes sont des caractères plutôt que des facteurs. Ma question est donc: comment puis-je faire cela automatiquement? Comment convertir un data.frame avec des colonnes factorielles en un data.frame avec des colonnes de caractères sans devoir parcourir manuellement chaque colonne?
Question bonus: pourquoi l'approche manuelle fonctionne-t-elle?
Je ne fais que suivre Matt et Dirk. Si vous souhaitez recréer votre cadre de données existant sans modifier l'option globale, vous pouvez le recréer avec une instruction apply:
bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)
Ceci convertira toutes les variables en classe "character", si vous voulez uniquement convertir des facteurs, voir solution de Marek ci-dessous .
Comme @hadley le souligne, ce qui suit est plus concis.
bob[] <- lapply(bob, as.character)
Dans les deux cas, lapply
génère une liste; toutefois, en raison des propriétés magiques de R, l’utilisation de []
dans le second cas conserve la classe data.frame de l’objet bob
, éliminant ainsi la nécessité de reconvertir en data.frame avec as.data.frame
avec l'argument stringsAsFactors = FALSE
.
Pour remplacer uniquement les facteurs:
i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)
Dans le package dplyr dans la version 0.5.0, nouvelle fonction mutate_if
a été introduite :
library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob
Le paquet purrr de RStudio donne une autre alternative:
library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_data_frame -> bob
(gardez à l'esprit que c'est un nouveau paquet)
L'option globale
stringsAsFactors: paramètre par défaut des arguments de data.frame et de read.table.
peut être quelque chose que vous voulez définir sur FALSE
dans vos fichiers de démarrage (par exemple, ~/.Rprofile). Veuillez voir help(options)
.
Si vous comprenez comment les facteurs sont stockés, vous pouvez éviter d'utiliser des fonctions basées sur une application pour accomplir cela. Ce qui ne veut pas dire que les solutions à appliquer ne fonctionnent pas bien.
Les facteurs sont structurés sous forme d'indices numériques liés à une liste de "niveaux". Cela se voit si vous convertissez un facteur en numérique. Alors:
> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d
> as.numeric(fact)
[1] 1 2 1 3
Les nombres retournés dans la dernière ligne correspondent aux niveaux du facteur.
> levels(fact)
[1] "a" "b" "d"
Notez que levels()
renvoie un tableau de caractères. Vous pouvez utiliser ce fait pour convertir facilement et de manière compacte des facteurs en chaînes ou en chiffres comme ceci:
> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"
Cela fonctionne également pour les valeurs numériques, à condition que vous envelopper votre expression dans as.numeric()
.
> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4
Si vous voulez un nouveau bloc de données bobc
où chaque vecteur facteur dans bobf
est converti en vecteur de caractère, essayez ceci:
bobc <- rapply(bobf, as.character, classes="factor", how="replace")
Si vous souhaitez ensuite le reconvertir, vous pouvez créer un vecteur logique dont les colonnes sont des facteurs et l'utiliser pour appliquer de manière sélective le facteur.
f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
Je réalise généralement cette fonction en dehors de tous mes projets. Rapide et facile.
unfactorize <- function(df){
for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
return(df)
}
Une autre façon est de le convertir en appliquant
bob2 <- apply(bob,2,as.character)
Et un meilleur (le précédent est de classe 'matrice')
bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
Ou vous pouvez essayer transform
:
newbob <- transform(bob, phenotype = as.character(phenotype))
Assurez-vous simplement de mettre tous les facteurs que vous souhaitez convertir en caractère.
Ou vous pouvez faire quelque chose comme ça et tuer tous les parasites d'un seul coup:
newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)
C'est pas bonne idée de déplacer les données dans le code comme ceci, je pourrais faire la partie sapply
séparément ( en fait, c'est beaucoup plus facile de le faire comme ça), mais vous comprenez le point ... Je n'ai pas vérifié le code, parce que je ne suis pas à la maison, alors j'espère que ça marche! =)
Cependant, cette approche a un inconvénient ... vous devez réorganiser les colonnes par la suite, alors qu'avec transform
vous pouvez faire ce que vous voulez, mais au prix de "code de style piéton -écriture " ...
Alors là ... =)
Mise à jour: voici un exemple de quelque chose qui ne fonctionne pas. Je pensais que ce serait le cas, mais je pense que l'option stringsAsFactors ne fonctionne que sur les chaînes de caractères - elle laisse les facteurs seuls.
Essaye ça:
bob2 <- data.frame(bob, stringsAsFactors = FALSE)
De manière générale, lorsque vous rencontrez des problèmes avec des facteurs qui doivent être des caractères, il existe un paramètre stringsAsFactors
pour vous aider (y compris un paramètre global).
Au début de votre trame de données, incluez stringsAsFactors = FALSE
pour ignorer tous les malentendus.
Si vous utilisez le package data.table
pour les opérations sur data.frame, le problème n’est pas présent.
library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
# col1 col2
#"character" "integer"
Si vous avez déjà un facteur de colonnes dans votre jeu de données et que vous souhaitez les convertir en caractères, vous pouvez procéder comme suit.
library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
# col1 col2
# "factor" "integer"
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
# col1 col2
#"character" "integer"
Cette fonction fait l'affaire
df <- stacomirtools::killfactor(df)
Cela fonctionne pour moi - j'ai finalement pensé à un one liner
df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)
Vous devez utiliser convert
dans hablar
qui donne une syntaxe lisible compatible avec les tubes tidyverse
:
library(dplyr)
library(hablar)
df <- tibble(a = factor(c(1, 2, 3, 4)),
b = factor(c(5, 6, 7, 8)))
df %>% convert(chr(a:b))
ce qui vous donne:
a b
<chr> <chr>
1 1 5
2 2 6
3 3 7
4 4 8
Peut-être une nouvelle option?
library("tidyverse")
bob <- bob %>% group_by_if(is.factor, as.character)
Cela fonctionne en transformant tout en caractère, puis le numérique en numérique:
makenumcols<-function(df){
df<-as.data.frame(df)
df[] <- lapply(df, as.character)
cond <- apply(df, 2, function(x) {
x <- x[!is.na(x)]
all(suppressWarnings(!is.na(as.numeric(x))))
})
numeric_cols <- names(df)[cond]
df[,numeric_cols] <- sapply(df[,numeric_cols], as.numeric)
return(df)
}
Adapté de: Obtenir automatiquement les types de colonne de la feuille Excel