Cambiar la clase de factor a numérico de muchas columnas en un marco de datos

82

¿Cuál es la forma más rápida / mejor de cambiar una gran cantidad de columnas a numéricas de factor?

Utilicé el siguiente código, pero parece haber reordenado mis datos.

> 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*

¿Cuál es la mejor manera, además de nombrar cada columna como en:

df$colname <- as.numeric(ds$colname)
Btibert3
fuente
4
¿No existe alguna solución genérica ?. Algunas de las soluciones propuestas aquí solo funcionan con factores, otras funcionan siempre excepto con factores, y así sucesivamente ...
skan

Respuestas:

56

Además de la respuesta de Ramnath, el comportamiento que está experimentando se debe a que as.numeric(x)devuelve la representación numérica interna del factor xen el nivel R. Si desea conservar los números que son los niveles del factor (en lugar de su representación interna), as.character()primero debe convertir al carácter vía según el ejemplo de Ramnath.

Su forbucle es tan razonable como una applyllamada y podría ser un poco más legible en cuanto a cuál es la intención del código. Simplemente cambie esta línea:

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

leer

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

Esta es la pregunta frecuente 7.10 en las preguntas frecuentes de R.

HTH

Gavin Simpson
fuente
2
No necesita ningún tipo de bucle. Simplemente use los índices y unlist (). Editar: agregué una respuesta que ilustra esto.
Joris Meys
Este enfoque solo funciona en este caso específico. Intenté usarlo para convertir columnas factory no funcionó. sapplyo mutate_ifparecen ser soluciones de aplicación más general.
Leo
@Leo Care para expandirse, porque sé con certeza que esto funciona. Es exactamente la misma solución que la de Ramnath a continuación, excepto que usa applypara ejecutar el bucle y el OP estaba usando un forbucle explícitamente. De hecho, todas las respuestas altamente votadas usan el as.numeric(as.character())idioma.
Gavin Simpson
Sí, funciona para cambiar la clase de varias columnas a numeric, pero no funciona a la inversa (para cambiar la clase de varias columnas a factor). Si usa índices que necesita unlist()y cuando se aplica a columnas con caracteres, elimina cada carácter, lo que hace que ya no funcione al volver a colocar la salida stats[,i]. Consulte la respuesta aquí: stackoverflow.com/questions/45713473/…
Leo
@Leo, ¡ por supuesto que no funciona a la inversa! ¿Qué diablos te dio la impresión de que así sería? Nunca fue diseñado y el OP nunca lo pidió. Es difícil responder a las preguntas que no se hacen. Si desea convertir a un factor, use as.factor()en lugar de as.numeric(as.character())aquí y funcionará bien. Por supuesto, si tiene una combinación de columnas, deberá elegir de forma iselectiva, pero eso también es trivial.
Gavin Simpson
73

Debe tener cuidado al cambiar los factores a numéricos. Aquí hay una línea de código que cambiaría un conjunto de columnas de factor a numérico. Supongo aquí que las columnas que se cambiarán a numéricas son 1, 3, 4 y 5 respectivamente. Podrías cambiarlo en consecuencia

cols = c(1, 3, 4, 5);    
df[,cols] = apply(df[,cols], 2, function(x) as.numeric(as.character(x)));
Ramnath
fuente
3
Esto no funcionará correctamente. Ejemplo: x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x). No creo que applysea ​​apropiado para este tipo de problemas.
Marek
1
Apply funciona perfectamente en estas situaciones. el error en mi código estaba usando margin = 1, en lugar de 2, ya que la función debe aplicarse en columnas. He editado mi respuesta en consecuencia.
Ramnath
Ahora funciona. Pero creo que se podría hacer sin él apply. Revisa mi edición.
Marek
2
... o Joris responde con unlist. Y as.characterla conversión en su solución no es necesaria causa applyconvertidos df[,cols]a charactermodo apply(df[,cols], 2, function(x) as.numeric(x))funcionará también.
Marek
@ Ramnath, ¿por qué lo usa =?, ¿por qué no <-?
kittygirl
40

Esto se puede hacer en una línea, no es necesario un bucle, ya sea un bucle for o una aplicación. Utilice unlist () en su lugar:

# 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)

Editar: para su código, esto se convierte en:

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

Obviamente, si tiene un marco de datos de una columna y no desea que la reducción de dimensión automática de R lo convierta en un vector, tendrá que agregar el drop=FALSEargumento.

Joris Meys
fuente
1
Una pequeña mejora podría ser la configuración recursivey los use.namesparámetros de unlistambos FALSE.
Marek
@Marek: cierto. Me encanta este juego :-)
Joris Meys
Solo voy a agregar para aquellos que buscan respuestas en el futuro, esto no es equivalente al método de op + gavin si el marco de datos es de una sola columna. En ese caso, se convertirá en un vector, mientras que las operaciones seguirán siendo un marco de datos.
themartinmcfly
1
para aquellos que trabajan con tidyverse: curiosamente, esto no parece funcionar cuando el objeto también es un tibble: el código falla despuésDf <- tibble::as_tibble(Df)
Tjebo
1
@Tjebo con las actualizaciones de tibble y la desviación entre tibbles y marcos de datos, este antiguo enfoque no es la mejor opción en tidyverse. Es mejor que utilice las funciones tidyselect en combinación con mutate_if. O cualquier nuevo enfoque que esté disponible en la próxima iteración de dplyr...
Joris Meys
30

Sé que esta pregunta se resolvió hace mucho tiempo, pero recientemente tuve un problema similar y creo que encontré una solución un poco más elegante y funcional, aunque requiere el paquete magrittr.

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

El %<>%operador canaliza y reasigna, lo cual es muy útil para simplificar la limpieza y transformación de datos. Ahora la función de aplicación de lista es mucho más fácil de leer, especificando solo la función que desea aplicar.

Dan
fuente
2
solución ordenada. olvidó un corchete, pero no puedo hacer esta edición porque es demasiado corto:df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
epo3
1
No creo que necesites envolver eso en lappy df[,cols] %<>% as.numeric(as.character(.))funciona igual
Nate
cuando intento este comandoError in [.data.table(Results, , cols) : j (the 2nd argument inside [...]) is a single symbol but column name 'cols' is not found. Perhaps you intended DT[,..cols] or DT[,cols,with=FALSE]. This difference to data.frame is deliberate and explained in FAQ 1.1.
aparece
El código es como:cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
Urvah Shabbir
Soporte ahora agregado.
Joe
9

Aquí tienes algunas dplyropciones:

# 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(.))) 
sbha
fuente
6

Creo que ucfagls descubrió por qué su bucle no funciona.

En caso de que aún no quiera usar un bucle, aquí hay una solución con lapply:

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

Editar. Encontré una solución más simple. Parece que se as.matrixconvierte en personaje. Entonces

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

debe hacer lo que quiera.

Marek
fuente
5

lapply está diseñado para esto

unfactorize<-c("colA","colB")
df[,unfactorize]<-lapply(unfactorize, function(x) as.numeric(as.character(df[,x])))
transcom
fuente
Hola @transcom, y bienvenido a stackoverflow. Tenga en cuenta que esta pregunta se trata de convertir a una representación numérica de un factor, no al revés. Vea la solución de Marek.
Aaron dejó Stack Overflow
@ Aaron, entendido. Publiqué esta respuesta debido a la ambigüedad del título del OP, operando bajo el supuesto de que otros pueden aterrizar aquí buscando una forma de convertir múltiples columnas fácilmente, independientemente de la clase. De todos modos, he editado mi respuesta para abordar la pregunta de manera más apropiada :)
transcom
2

Encontré esta función en un par de otros hilos duplicados y la he encontrado como una forma elegante y general de resolver este problema. Este hilo aparece primero en la mayoría de las búsquedas sobre este tema, así que lo comparto aquí para ahorrarles algo de tiempo. No me atribuyo ningún mérito por esto, así que consulte las publicaciones originales aquí y aquí para obtener más detalles.

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")
Hacer la campaña electoral
fuente
1

Me gustaría señalar que si tiene NA en cualquier columna, simplemente usar subíndices no funcionará. Si hay NA en el factor, debe utilizar el script de aplicación proporcionado por Ramnath.

P.ej

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)]))

Devuelve lo siguiente:

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

Pero:

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

Devoluciones:

> 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
Elizabeth
fuente
1

puede usar la unfactor()función del paquete "varhandle" del formulario CRAN:

library("varhandle")

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

my_iris <- unfactor(my_iris)
Mehrad Mahmoudian
fuente
1

Me gusta este código porque es bastante útil:

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

No es exactamente lo que se pidió (convertir a numérico), pero en muchos casos es incluso más apropiado.

SDahm
fuente
1

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

Intenté de esta manera cambiar un tipo de columna y creo que es mejor que muchas otras versiones, si no va a cambiar todos los tipos de columna

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

por lo contrario.

Huseyn Rahimov
fuente
0

Tuve problemas para convertir todas las columnas a numéricas con una apply()llamada:

apply(data, 2, as.numeric)

El problema resulta ser porque algunas de las cadenas tenían una coma, por ejemplo, "1.024,63" en lugar de "1024,63", ya R no le gusta esta forma de formatear los números. Así que los eliminé y luego ejecuté as.numeric():

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

Tenga en cuenta que esto requiere que se cargue el paquete stringr.

CoderGuy123
fuente
0

Eso es lo que funcionó para mí. La apply()función intenta coaccionar df a matrix y devuelve NA's.

numeric.df <- as.data.frame(sapply(df, 2, as.numeric))

Alina Shabatov
fuente
0

Según la respuesta de @ SDahm, esta fue una solución "óptima" para mi tibble:

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

Esto requiere dplyry magrittr.

James Hirschorn
fuente
0

Probé un montón de estos en un problema similar y seguí obteniendo NA. Base R tiene algunos comportamientos de coerción realmente irritantes, que generalmente se corrigen en los paquetes de Tidyverse. Solía ​​evitarlos porque no quería crear dependencias, pero hacen la vida mucho más fácil que ahora ni siquiera me molesto en intentar descubrir la solución Base R la mayor parte del tiempo.

Aquí está la solución Tidyverse, que es extremadamente simple y elegante:

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)
Aaron Cooley
fuente
La mayoría de las respuestas (al menos todas las respuestas principales) asegúrese de hacer la as.numeric(as.character())conversión para evitar la conversión demasiado común de niveles enteros en lugar de valores a numéricos. Con mucho gusto votaría a favor de esta respuesta si muestra esa opción.
Gregor Thomas