web-dev-qa-db-fra.com

Changer la classe de factor en numérique de nombreuses colonnes dans un cadre de données

Quel est le moyen le plus rapide/le meilleur de modifier un grand nombre de colonnes en facteur numérique? 

J'ai utilisé le code suivant mais il semble avoir re-commandé mes données.

> head(stats[,1:2])
  rk                 team
1  1 Washington Capitals*
2  2     San Jose Sharks*
3  3  Chicago Blackhawks*
4  4     Phoenix Coyotes*
5  5   New Jersey Devils*
6  6   Vancouver Canucks*

for(i in c(1,3:ncol(stats))) {
    stats[,i] <- as.numeric(stats[,i])
}

> head(stats[,1:2])
  rk                 team
1  2 Washington Capitals*
2 13     San Jose Sharks*
3 24  Chicago Blackhawks*
4 26     Phoenix Coyotes*
5 27   New Jersey Devils*
6 28   Vancouver Canucks*

Quelle est la meilleure façon de faire, à moins de nommer chaque colonne de la manière suivante:

df$colname <- as.numeric(ds$colname)
67
Btibert3

Suite à la réponse de Ramnath, le comportement que vous rencontrez est dû au fait que as.numeric(x) renvoie la représentation numérique interne du facteur x au niveau R. Si vous voulez conserver les nombres correspondant aux niveaux du facteur (plutôt que leur représentation interne), vous devez d'abord convertir en caractère via as.character(), comme dans l'exemple de Ramnath.

Votre boucle for est aussi raisonnable qu'un appel apply et pourrait être un peu plus lisible quant à l'intention du code. Il suffit de changer cette ligne:

stats[,i] <- as.numeric(stats[,i])

lire

stats[,i] <- as.numeric(as.character(stats[,i]))

Ceci est FAQ 7.10 dans la FAQ R.

HTH

52
Gavin Simpson

Vous devez faire attention en changeant les facteurs en numérique. Voici une ligne de code qui changerait un ensemble de colonnes de factor en numérique. Je suppose ici que les colonnes à modifier en numérique sont 1, 3, 4 et 5 respectivement. Vous pouvez le changer en conséquence

cols = c(1, 3, 4, 5);    
df[,cols] = apply(df[,cols], 2, function(x) as.numeric(as.character(x)));
67
Ramnath

Cela peut être fait sur une seule ligne, il n’est pas nécessaire d’utiliser une boucle, que ce soit une boucle for ou une apply. Utilisez unlist () à la place:

# testdata
Df <- data.frame(
  x = as.factor(sample(1:5,30,r=TRUE)),
  y = as.factor(sample(1:5,30,r=TRUE)),
  z = as.factor(sample(1:5,30,r=TRUE)),
  w = as.factor(sample(1:5,30,r=TRUE))
)
##

Df[,c("y","w")] <- as.numeric(as.character(unlist(Df[,c("y","w")])))

str(Df)

Edit: pour votre code, cela devient:

id <- c(1,3:ncol(stats))) 
stats[,id] <- as.numeric(as.character(unlist(stats[,id])))

Évidemment, si vous avez un cadre de données d'une colonne et que vous ne souhaitez pas que la réduction automatique de la dimension de R le convertisse en vecteur, vous devrez ajouter l'argument drop=FALSE.

33
Joris Meys

Je sais que cette question est résolue depuis longtemps, mais j'ai récemment eu un problème similaire et je pense avoir trouvé une solution un peu plus élégante et fonctionnelle, bien qu'elle nécessite le package magrittr.

library(magrittr)
cols = c(1, 3, 4, 5)
df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))

L'opérateur %<>% canal et est réaffecté, ce qui est très utile pour simplifier le nettoyage et la transformation des données. Maintenant, la fonction list apply est beaucoup plus facile à lire, en spécifiant uniquement la fonction que vous souhaitez appliquer.

25
Dan

Je pense que ucfagls a trouvé pourquoi votre boucle ne fonctionne pas.

Si vous ne souhaitez toujours pas utiliser de boucle, voici la solution avec lapply:

factorToNumeric <- function(f) as.numeric(levels(f))[as.integer(f)] 
cols <- c(1, 3:ncol(stats))
stats[cols] <- lapply(stats[cols], factorToNumeric)

Modifier. J'ai trouvé une solution plus simple. Il semble que as.matrix se convertisse en personnage. Alors

stats[cols] <- as.numeric(as.matrix(stats[cols]))

devrait faire ce que vous voulez.

6
Marek

lapply est assez bien conçu pour cela

unfactorize<-c("colA","colB")
df[,unfactorize]<-lapply(unfactorize, function(x) as.numeric(as.character(df[,x])))
5
transcom

J'ai trouvé cette fonction sur un couple de threads en double et l'ai trouvé un moyen élégant et général de résoudre ce problème. Ce fil apparaît en premier sur la plupart des recherches sur ce sujet, je le partage donc ici pour faire gagner du temps aux gens. Je ne prends aucun crédit pour cela, alors consultez les messages originaux ici et ici pour plus de détails.

df <- data.frame(x = 1:10,
                 y = rep(1:2, 5),
                 k = rnorm(10, 5,2),
                 z = rep(c(2010, 2012, 2011, 2010, 1999), 2),
                 j = c(rep(c("a", "b", "c"), 3), "d"))

convert.magic <- function(obj, type){
  FUN1 <- switch(type,
                 character = as.character,
                 numeric = as.numeric,
                 factor = as.factor)
  out <- lapply(obj, FUN1)
  as.data.frame(out)
}

str(df)
str(convert.magic(df, "character"))
str(convert.magic(df, "factor"))
df[, c("x", "y")] <- convert.magic(df[, c("x", "y")], "factor")
2
Electioneer

J'aime ce code parce qu'il est très pratique:

  data[] <- lapply(data, function(x) type.convert(as.character(x), as.is = TRUE)) #change all vars to their best fitting data type

Ce n'est pas exactement ce qui était demandé (convertir en numérique), mais dans de nombreux cas, même plus approprié.

1
SDahm

vous pouvez utiliser la fonction unfactor() à partir du package "varhandle" CRAN:

library("varhandle")

my_iris <- data.frame(Sepal.Length = factor(iris$Sepal.Length),
                      sample_id = factor(1:nrow(iris)))

my_iris <- unfactor(my_iris)
1
Mehrad Mahmoudian

J'aimerais souligner que si vous avez des AN dans une colonne, utiliser simplement des indices ne fonctionnera pas. S'il y a des NA dans le facteur, vous devez utiliser le script apply fourni par Ramnath.

Par exemple.

Df <- data.frame(
  x = c(NA,as.factor(sample(1:5,30,r=T))),
  y = c(NA,as.factor(sample(1:5,30,r=T))),
  z = c(NA,as.factor(sample(1:5,30,r=T))),
  w = c(NA,as.factor(sample(1:5,30,r=T)))
)

Df[,c(1:4)] <- as.numeric(as.character(Df[,c(1:4)]))

Renvoie ce qui suit:

Warning message:
NAs introduced by coercion 

    > head(Df)
       x  y  z  w
    1 NA NA NA NA
    2 NA NA NA NA
    3 NA NA NA NA
    4 NA NA NA NA
    5 NA NA NA NA
    6 NA NA NA NA

Mais:

Df[,c(1:4)]= apply(Df[,c(1:4)], 2, function(x) as.numeric(as.character(x)))

Résultats:

> head(Df)
   x  y  z  w
1 NA NA NA NA
2  2  3  4  1
3  1  5  3  4
4  2  3  4  1
5  5  3  5  5
6  4  2  4  4
1
Elizabeth

Voici quelques options dplyr:

# by column type:
df %>% 
  mutate_if(is.factor, ~as.numeric(as.character(.)))

# by specific columns:
df %>% 
  mutate_at(vars(x, y, z), ~as.numeric(as.character(.))) 

# all columns:
df %>% 
  mutate_all(~as.numeric(as.character(.))) 
1
sbha

J'ai eu des problèmes pour convertir toutes les colonnes en numérique avec un appel apply():

apply(data, 2, as.numeric)

Le problème s’explique par le fait que certaines chaînes contiennent une virgule - par exemple. "1 024,63" au lieu de "1024,63" - et R n'aime pas cette façon de formater les nombres. Alors je les ai enlevés puis j'ai lancé as.numeric():

data = as.data.frame(apply(data, 2, function(x) {
  y = str_replace_all(x, ",", "") #remove commas
  return(as.numeric(y)) #then convert
}))

Notez que cela nécessite que le package stringr soit chargé.

0
Deleet

D'après la réponse de @ SDahm, il s'agissait d'une solution "optimale" pour ma tibble:

data %<>% lapply(type.convert) %>% as.data.table()

Cela nécessite dplyr et magrittr.

0
James Hirschorn

df$colname <- as.numeric(df$colname)

J'ai essayé cette méthode pour changer un type de colonne et je pense que c'est mieux que beaucoup d'autres versions, si vous n'allez pas changer tous les types de colonne

df$colname <- as.character(df$colname)

pour l'inverse.

0
huseyn rahimov

J'ai essayé plusieurs d'entre elles sur un problème similaire et j'ai continué à avoir des NA. La base R a des comportements de coercition vraiment irritants, qui sont généralement résolus dans les paquets Tidyverse. J'avais l'habitude de les éviter parce que je ne voulais pas créer de dépendances, mais elles facilitent la vie tellement plus facilement que maintenant je ne me donne même plus la peine d'essayer de comprendre la solution Base R la plupart du temps.

Voici la solution Tidyverse, extrêmement simple et élégante:

library(purrr)

mydf <- data.frame(
  x1 = factor(c(3, 5, 4, 2, 1)),
  x2 = factor(c("A", "C", "B", "D", "E")),
  x3 = c(10, 8, 6, 4, 2))

map_df(mydf, as.numeric)
0
Aaron Cooley