Il semble que je passe beaucoup de temps à créer un cadre de données à partir d'un fichier, d'une base de données ou quelque chose du genre, puis à convertir chaque colonne en un type (numérique, facteur, caractère, etc.). Est-il possible de faire cela en une seule étape, éventuellement en donnant un vecteur de types?
foo<-data.frame(x=c(1:10),
y=c("red", "red", "red", "blue", "blue",
"blue", "yellow", "yellow", "yellow",
"green"),
z=Sys.Date()+c(1:10))
foo$x<-as.character(foo$x)
foo$y<-as.character(foo$y)
foo$z<-as.numeric(foo$z)
au lieu des trois dernières commandes, j'aimerais faire quelque chose comme
foo<-convert.magic(foo, c(character, character, numeric))
Edit Voir this question connexe pour quelques simplifications et extensions sur cette idée de base.
Mon commentaire à la réponse de Brandon en utilisant switch
:
convert.magic <- function(obj,types){
for (i in 1:length(obj)){
FUN <- switch(types[i],character = as.character,
numeric = as.numeric,
factor = as.factor)
obj[,i] <- FUN(obj[,i])
}
obj
}
out <- convert.magic(foo,c('character','character','numeric'))
> str(out)
'data.frame': 10 obs. of 3 variables:
$ x: chr "1" "2" "3" "4" ...
$ y: chr "red" "red" "red" "blue" ...
$ z: num 15254 15255 15256 15257 15258 ...
Pour de très grands cadres de données, vous pouvez utiliser lapply
au lieu de la boucle for
:
convert.magic1 <- function(obj,types){
out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],character = as.character,numeric = as.numeric,factor = as.factor); FUN1(obj[,i])})
names(out) <- colnames(obj)
as.data.frame(out,stringsAsFactors = FALSE)
}
Ce faisant, soyez conscient de certaines subtilités des données de coercition de R. Par exemple, la conversion de facteur en numérique implique souvent as.numeric(as.character(...))
. Tenez également compte du comportement par défaut de data.frame()
et as.data.frame()
s lors de la conversion de caractère en facteur.
Si vous souhaitez détecter automatiquement le type de données des colonnes plutôt que de le spécifier manuellement (par exemple, après le classement des données, etc.), la fonction type.convert()
peut vous aider.
La fonction type.convert()
prend en compte un vecteur de caractère et tente de déterminer le type optimal pour tous les éléments (ce qui signifie qu'il doit être appliqué une fois par colonne).
df[] <- lapply(df, function(x) type.convert(as.character(x)))
Depuis que j'aime dplyr
, je préfère:
library(dplyr)
df <- df %>% mutate_all(funs(type.convert(as.character(.))))
Je trouve que je rencontre beaucoup cela aussi. Ceci concerne la manière dont vous importez des données. Toutes les fonctions read ... () ont un type d’option pour spécifier de ne pas convertir les chaînes de caractères en facteur. Cela signifie que les chaînes de texte resteront des caractères et que les éléments qui ressemblent à des nombres resteront sous forme de nombres. Un problème survient lorsque vous avez des éléments vides et non NA. Mais encore une fois, na.strings = c ("", ...) devrait également résoudre ce problème. Je commencerais par examiner de près votre processus d'importation et de l'ajuster en conséquence.
Mais vous pouvez toujours créer une fonction et pousser cette chaîne à travers.
convert.magic <- function(x, y=NA) {
for(i in 1:length(y)) {
if (y[i] == "numeric") {
x[i] <- as.numeric(x[[i]])
}
if (y[i] == "character")
x[i] <- as.character(x[[i]])
}
return(x)
}
foo <- convert.magic(foo, c("character", "character", "numeric"))
> str(foo)
'data.frame': 10 obs. of 3 variables:
$ x: chr "1" "2" "3" "4" ...
$ y: chr "red" "red" "red" "blue" ...
$ z: num 15254 15255 15256 15257 15258 ...
Je sais que je suis assez en retard pour répondre, mais utiliser une boucle avec la fonction d'attributs est une solution simple à votre problème.
names <- c("x", "y", "z")
chclass <- c("character", "character", "numeric")
for (i in (1:length(names))) {
attributes(foo[, names[i]])$class <- chclass[i]
}
Je viens de rencontrer quelque chose comme ceci avec la méthode d'extraction RSQLite ... les résultats sont renvoyés sous forme de types de données atomiques. Dans mon cas, c’était un horodatage qui me causait de la frustration ..__ J'ai trouvé que la fonction setAs
est très utile pour aider à faire fonctionner as
comme prévu. Voici mon petit exemple.
##data.frame conversion function
convert.magic2 <- function(df,classes){
out <- lapply(1:length(classes),
FUN = function(classIndex){as(df[,classIndex],classes[classIndex])})
names(out) <- colnames(df)
return(data.frame(out))
}
##small example case
tmp.df <- data.frame('dt'=c("2013-09-02 09:35:06", "2013-09-02 09:38:24", "2013-09-02 09:38:42", "2013-09-02 09:38:42"),
'v'=c('1','2','3','4'),
stringsAsFactors=FALSE)
classes=c('POSIXct','numeric')
str(tmp.df)
#confirm that it has character datatype columns
## 'data.frame': 4 obs. of 2 variables:
## $ dt: chr "2013-09-02 09:35:06" "2013-09-02 09:38:24" "2013-09-02 09:38:42" "2013-09-02 09:38:42"
## $ v : chr "1" "2" "3" "4"
##is the dt column coerceable to POSIXct?
canCoerce(tmp.df$dt,"POSIXct")
## [1] FALSE
##and the conver.magic2 function fails also:
tmp.df.n <- convert.magic2(tmp.df,classes)
## Error in as(df[, classIndex], classes[classIndex]) :
## no method or default for coercing “character” to “POSIXct”
##ittle reading reveals the setAS function
setAs('character', 'POSIXct', function(from){return(as.POSIXct(from))})
##better answer for canCoerce
canCoerce(tmp.df$dt,"POSIXct")
## [1] TRUE
##better answer from conver.magic2
tmp.df.n <- convert.magic2(tmp.df,classes)
##column datatypes converted as I would like them!
str(tmp.df.n)
## 'data.frame': 4 obs. of 2 variables:
## $ dt: POSIXct, format: "2013-09-02 09:35:06" "2013-09-02 09:38:24" "2013-09-02 09:38:42" "2013-09-02 09:38:42"
## $ v : num 1 2 3 4
Une solution un peu simple de data.table, bien que quelques étapes soient nécessaires si vous passez à beaucoup de types de colonnes différents.
dt <- data.table( x=c(1:10), y=c(10:20), z=c(10:20), name=letters[1:10])
dt <- dt[, lapply(.SD, as.numeric), by= name]
Ceci changera toutes les colonnes sauf celles spécifiées dans by
en numérique (ou tout ce que vous avez défini dans lapply
)
Ajout à la réponse de @ joran, dans laquelle convert.magic
ne conserverait pas les valeurs numériques dans la conversion facteur-à-numérique:
convert.magic <- function(obj,types){
out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],
character = as.character,numeric = as.numeric,factor = as.factor); FUN1(obj[,i])})
names(out) <- colnames(obj)
as.data.frame(out,stringsAsFactors = FALSE)
}
foo<-data.frame(x=c(1:10),
y=c("red", "red", "red", "blue", "blue",
"blue", "yellow", "yellow", "yellow",
"green"),
z=Sys.Date()+c(1:10))
foo$x<-as.character(foo$x)
foo$y<-as.character(foo$y)
foo$z<-as.numeric(foo$z)
str(foo)
# 'data.frame': 10 obs. of 3 variables:
# $ x: chr "1" "2" "3" "4" ...
# $ y: chr "red" "red" "red" "blue" ...
# $ z: num 16777 16778 16779 16780 16781 ...
foo.factors <- convert.magic(foo, rep("factor", 3))
str(foo.factors) # all factors
foo.numeric.not.preserved <- convert.magic(foo.factors, c("numeric", "character", "numeric"))
str(foo.numeric.not.preserved)
# 'data.frame': 10 obs. of 3 variables:
# $ x: num 1 3 4 5 6 7 8 9 10 2
# $ y: chr "red" "red" "red" "blue" ...
# $ z: num 1 2 3 4 5 6 7 8 9 10
# z comes out as 1 2 3...
Les éléments suivants doivent conserver les valeurs numériques:
## as.numeric function that preserves numeric values when converting factor to numeric
as.numeric.mod <- function(x) {
if(is.factor(x))
as.numeric(levels(x))[x]
else
as.numeric(x)
}
## The same than in @joran's answer, except for as.numeric.mod
convert.magic <- function(obj,types){
out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],
character = as.character,numeric = as.numeric.mod, factor = as.factor); FUN1(obj[,i])})
names(out) <- colnames(obj)
as.data.frame(out,stringsAsFactors = FALSE)
}
foo.numeric <- convert.magic(foo.factors, c("numeric", "character", "numeric"))
str(foo.numeric)
# 'data.frame': 10 obs. of 3 variables:
# $ x: num 1 2 3 4 5 6 7 8 9 10
# $ y: chr "red" "red" "red" "blue" ...
# $ z: num 16777 16778 16779 16780 16781 ...
# z comes out with the correct numeric values
La transformation est ce que vous semblez décrire:
foo <- transform(foo, x=as.character(x), y=as.character(y), z=as.numeric(z))