Estandarizar columnas de datos en R

209

Tengo un conjunto de datos llamado spamque contiene 58 columnas y aproximadamente 3500 filas de datos relacionados con mensajes de spam.

Planeo ejecutar alguna regresión lineal en este conjunto de datos en el futuro, pero me gustaría hacer un preprocesamiento de antemano y estandarizar las columnas para que tengan media cero y varianza unitaria.

Me han dicho que la mejor manera de hacerlo es con R, así que me gustaría preguntar cómo puedo lograr la normalización con R ? Ya tengo los datos cargados correctamente y solo estoy buscando algunos paquetes o métodos para realizar esta tarea.

Hoser
fuente

Respuestas:

533

Debo suponer que quería decir que quería una media de 0 y una desviación estándar de 1. Si sus datos están en un marco de datos y todas las columnas son numéricas, simplemente puede llamar a la scalefunción en los datos para hacer lo que quiera.

dat <- data.frame(x = rnorm(10, 30, .2), y = runif(10, 3, 5))
scaled.dat <- scale(dat)

# check that we get mean of 0 and sd of 1
colMeans(scaled.dat)  # faster version of apply(scaled.dat, 2, mean)
apply(scaled.dat, 2, sd)

El uso de funciones integradas es elegante. Como este gato:

ingrese la descripción de la imagen aquí

Razón
fuente
24
Sí, mi error quise decir 0 significa. Y ese es un gato bastante elegante
Hoser
8
+1 usando apply puede ser lento también como este gato gordo :) (colMeans here)
estudio el
1
@agstudy Bastante justo. Debería acostumbrarme a usar colMeans / colSums más. Supongo que no pienso en eso a menos que esté en una situación en la que realmente importa ...
Dason
137
este sitio necesita más gatos +1
LoveMeow
35
Advertencia: la escala también transforma el marco de datos en una matriz
Julian Karls
89

Al darme cuenta de que la pregunta es antigua y se acepta una respuesta, proporcionaré otra respuesta como referencia.

scaleestá limitado por el hecho de que escala todas las variables . La solución a continuación permite escalar solo nombres de variables específicos mientras conserva otras variables sin cambios (y los nombres de las variables podrían generarse dinámicamente):

library(dplyr)

set.seed(1234)
dat <- data.frame(x = rnorm(10, 30, .2), 
                  y = runif(10, 3, 5),
                  z = runif(10, 10, 20))
dat

dat2 <- dat %>% mutate_at(c("y", "z"), ~(scale(.) %>% as.vector))
dat2

lo que me da esto:

> dat
          x        y        z
1  29.75859 3.633225 14.56091
2  30.05549 3.605387 12.65187
3  30.21689 3.318092 13.04672
4  29.53086 3.079992 15.07307
5  30.08582 3.437599 11.81096
6  30.10121 4.621197 17.59671
7  29.88505 4.051395 12.01248
8  29.89067 4.829316 12.58810
9  29.88711 4.662690 19.92150
10 29.82199 3.091541 18.07352

y

> dat2 <- dat %>% mutate_at(c("y", "z"), ~(scale(.) %>% as.vector))
> dat2
          x          y           z
1  29.75859 -0.3004815 -0.06016029
2  30.05549 -0.3423437 -0.72529604
3  30.21689 -0.7743696 -0.58772361
4  29.53086 -1.1324181  0.11828039
5  30.08582 -0.5946582 -1.01827752
6  30.10121  1.1852038  0.99754666
7  29.88505  0.3283513 -0.94806607
8  29.89067  1.4981677 -0.74751378
9  29.88711  1.2475998  1.80753470
10 29.82199 -1.1150515  1.16367556

EDIT 1 (2016) : Abordó el comentario de Julian: la salida de scalees una matriz Nx1, por lo que idealmente deberíamos agregar un as.vectorpara convertir el tipo de matriz en un tipo de vector. Gracias julian!

EDIT 2 (2019) : Citando el comentario de Duccio A.: Para la última versión de dplyr (versión 0.8) necesita cambiar dplyr :: funcs con lista, comodat %>% mutate_each_(list(~scale(.) %>% as.vector), vars=c("y","z"))

EDIT 3 (2020) : Gracias a @mj_whales: la solución anterior está en desuso y ahora tenemos que usarla mutate_at.

akhmed
fuente
Este método funciona perfectamente, especialmente cuando tengo una combinación de variables categóricas y numéricas. Solo tengo una pregunta, ¿qué significa este operador "%>%"?
nooshinha
9
@ weber85, es un operador "pipe" (de programación funcional). En lugar de escribir f(g(x)), se vería mejor si uno escribe x %>% g %>% f. En otras palabras, dat %>% mutate_each_(funs(scale),vars=c("y","z"))es justo mutate_each_(dat,funs(scale),vars=c("y","z")). El operador ayuda mucho cuando una cadena es muy larga ya que f(g(h(i(j(x)))))puede ser muy difícil de leer.
akhmed
Con este enfoque, las columnas en las que se aplica la escala se transfieren del vector (clase numérica) a las matrices Nx1. Esto podría (y en mi caso lo hizo) causar algunos errores en los paquetes que suponen que cada columna de un data.frame es un vector.
Julian Karls
2
Para la última dplyr(versión 0.8) que necesita para el cambio dplyr::funcscon list, comodat %>% mutate_each_(list(~scale(.) %>% as.vector), vars=c("y","z"))
Duccio A
2
mutate_each_()ahora está en desuso. Puedes usar mutate_at()en su lugar. La nueva forma de hacerlo sería:dat2 <- dat %>% mutate_at(c("y", "z"), scale)
60

Esto tiene 3 años. Aún así, siento que tengo que agregar lo siguiente:

La normalización más común es la transformación z , donde se resta la media y se divide por la desviación estándar de su variable. El resultado tendrá media = 0 y sd = 1.

Para eso, no necesitas ningún paquete.

zVar <- (myVar - mean(myVar)) / sd(myVar)

Eso es.

fmb
fuente
Una forma totalmente simple de realizar esto. Gracias
Pedro Neves
Y hace que el uso que dplyr mucho más fácil: mutate(var = (var - mean(var))/sd(var)).
RobertMyles
Pero, ¿se puede usar para obtener la puntuación z para dos variables?
lf_araujo
para desnormalizar myVar <- (zVar * sd(zVar)) + mean(zVar), ¿verdad?
Artur_Indio
44
Casi @Artur_Indio: newVar <- (zVar * sd(myVar)) + mean(myVar). Tienes que usar la media / sd original. A medida que lo escribiste, multiplicarás sd(zVar)=1y agregarás mean(zVar)=0, para que nada cambie :)
random_forest_fanatic
24

El paquete 'Caret' proporciona métodos para preprocesar datos (por ejemplo, centrado y escalado). También puede usar el siguiente código:

library(caret)
# Assuming goal class is column 10
preObj <- preProcess(data[, -10], method=c("center", "scale"))
newData <- predict(preObj, data[, -10])

Más detalles: http://www.inside-r.org/node/86978

DaniM
fuente
17

Cuando utilicé la solución establecida por Dason, en lugar de obtener un marco de datos como resultado, obtuve un vector de números (los valores escalados de mi df).

En caso de que alguien tenga el mismo problema, debe agregar as.data.frame () al código, así:

df.scaled <- as.data.frame(scale(df))

¡Espero que esto sea útil para las personas que tienen el mismo problema!

Diego
fuente
Buena solución! En caso de que alguien quiera excluir una columna de la escala, puede hacerlo así: train_dt[-24] <- scale(train_dt[-24]) donde "24" es el número de columna que se excluirá
NetEmmanuel
13

Puede normalizar fácilmente los datos también utilizando datos. Función de normalización en el paquete clusterSim. Proporciona diferentes métodos de normalización de datos.

    data.Normalization (x,type="n0",normalization="column")

Argumentos

x tipo de normalización
tipo vector, matriz o conjunto
de datos: n0 - sin normalización

n1 - estandarización ((x-mean) / sd)

n2 - estandarización posicional ((x-mediana) / mad)

n3 - unitización ((media x) / rango)

n3a - unitización posicional ((mediana de x) / rango)

n4 - unitización con cero mínimo ((x-min) / rango)

n5 - normalización en el rango <-1,1> ((x-mean) / max (abs (x-mean)))

n5a - normalización posicional en el rango <-1,1> ((x-mediana) / max (abs (x-mediana)))

n6 - transformación del cociente (x / sd)

n6a - transformación del cociente posicional (x / mad)

n7 - transformación del cociente (x / rango)

n8 - transformación del cociente (x / max)

n9 - transformación del cociente (x / media)

n9a - transformación del cociente posicional (x / mediana)

n10 - transformación del cociente (x / suma)

n11 - transformación del cociente (x / sqrt (SSQ))

n12 - normalización ((x-mean) / sqrt (sum ((x-mean) ^ 2)))

n12a - normalización posicional ((x-mediana) / sqrt (suma ((x-mediana) ^ 2)))

n13 - normalización con cero como punto central ((rango medio x) / (rango / 2))

normalización
"columna" - normalización por variable, "fila" - normalización por objeto

Samehmagd
fuente
este paquete no está disponible para R versión 3.4.3
JdP
11

Con dplyrv0.7.4 todas las variables se pueden escalar usando mutate_all():

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(tibble)

set.seed(1234)
dat <- tibble(x = rnorm(10, 30, .2), 
              y = runif(10, 3, 5),
              z = runif(10, 10, 20))

dat %>% mutate_all(scale)
#> # A tibble: 10 x 3
#>         x      y       z
#>     <dbl>  <dbl>   <dbl>
#>  1 -0.827 -0.300 -0.0602
#>  2  0.663 -0.342 -0.725 
#>  3  1.47  -0.774 -0.588 
#>  4 -1.97  -1.13   0.118 
#>  5  0.816 -0.595 -1.02  
#>  6  0.893  1.19   0.998 
#>  7 -0.192  0.328 -0.948 
#>  8 -0.164  1.50  -0.748 
#>  9 -0.182  1.25   1.81  
#> 10 -0.509 -1.12   1.16

Las variables específicas se pueden excluir usando mutate_at():

dat %>% mutate_at(scale, .vars = vars(-x))
#> # A tibble: 10 x 3
#>        x      y       z
#>    <dbl>  <dbl>   <dbl>
#>  1  29.8 -0.300 -0.0602
#>  2  30.1 -0.342 -0.725 
#>  3  30.2 -0.774 -0.588 
#>  4  29.5 -1.13   0.118 
#>  5  30.1 -0.595 -1.02  
#>  6  30.1  1.19   0.998 
#>  7  29.9  0.328 -0.948 
#>  8  29.9  1.50  -0.748 
#>  9  29.9  1.25   1.81  
#> 10  29.8 -1.12   1.16

Creado el 24/04/2018 por el paquete reprex (v0.2.0).

pat-s
fuente
9

Una vez más, a pesar de que esta es una vieja pregunta, ¡es muy relevante! Y he encontrado una manera simple de normalizar ciertas columnas sin la necesidad de ningún paquete:

normFunc <- function(x){(x-mean(x, na.rm = T))/sd(x, na.rm = T)}

Por ejemplo

x<-rnorm(10,14,2)
y<-rnorm(10,7,3)
z<-rnorm(10,18,5)
df<-data.frame(x,y,z)

df[2:3] <- apply(df[2:3], 2, normFunc)

Verá que las columnas y y z se han normalizado. No se necesitan paquetes :-)

BBKim
fuente
8

La escala se puede usar tanto para el marco de datos completo como para columnas específicas. Para columnas específicas, se puede usar el siguiente código:

trainingSet[, 3:7] = scale(trainingSet[, 3:7]) # For column 3 to 7
trainingSet[, 8] = scale(trainingSet[, 8]) # For column 8 

Marco de datos completo

trainingSet <- scale(trainingSet)
Amit
fuente
3

El dplyrpaquete tiene dos funciones que hacen esto.

> require(dplyr)

Para mutar columnas específicas de una tabla de datos, puede usar la función mutate_at(). Para mutar todas las columnas, puede usar mutate_all.

El siguiente es un breve ejemplo para usar estas funciones para estandarizar datos.

Mutar columnas específicas:

dt = data.table(a = runif(3500), b = runif(3500), c = runif(3500))
dt = data.table(dt %>% mutate_at(vars("a", "c"), scale)) # can also index columns by number, e.g., vars(c(1,3))

> apply(dt, 2, mean)
            a             b             c 
 1.783137e-16  5.064855e-01 -5.245395e-17 

> apply(dt, 2, sd)
        a         b         c 
1.0000000 0.2906622 1.0000000 

Mutar todas las columnas:

dt = data.table(a = runif(3500), b = runif(3500), c = runif(3500))
dt = data.table(dt %>% mutate_all(scale))

> apply(dt, 2, mean)
            a             b             c 
-1.728266e-16  9.291994e-17  1.683551e-16 

> apply(dt, 2, sd)
a b c 
1 1 1 
Jack
fuente
1

Antes de encontrar este hilo, tuve el mismo problema. Tenía tipos de columnas dependientes del usuario, así que escribí un forbucle que los revisaba y obtenía las columnas necesarias scale'd. Probablemente hay mejores formas de hacerlo, pero esto resolvió el problema perfectamente:

 for(i in 1:length(colnames(df))) {
        if(class(df[,i]) == "numeric" || class(df[,i]) == "integer") {
            df[,i] <- as.vector(scale(df[,i])) }
        }

as.vectores una parte necesaria, porque resultó que scalehace una rownames x 1matriz que generalmente no es lo que quieres tener en tu data.frame.

Claud H
fuente
0

Utiliza el paquete "recomenderlab". Descargue e instale el paquete. Este paquete tiene un comando "Normalizar" incorporado. También le permite elegir uno de los muchos métodos de normalización, a saber, 'centro' o 'puntaje Z'. Siga el siguiente ejemplo:

## create a matrix with ratings
m <- matrix(sample(c(NA,0:5),50, replace=TRUE, prob=c(.5,rep(.5/6,6))),nrow=5, ncol=10, dimnames = list(users=paste('u', 1:5, sep=&rdquo;), items=paste('i', 1:10, sep=&rdquo;)))

## do normalization
r <- as(m, "realRatingMatrix")
#here, 'centre' is the default method
r_n1 <- normalize(r) 
#here "Z-score" is the used method used
r_n2 <- normalize(r, method="Z-score")

r
r_n1
r_n2

## show normalized data
image(r, main="Raw Data")
image(r_n1, main="Centered")
image(r_n2, main="Z-Score Normalization")
user3601993
fuente
1
Esta respuesta no responde a la pregunta.
f0nzie
0

La función de normalización del paquete BBMisc fue la herramienta adecuada para mí, ya que puede manejar los valores de NA.

Aquí está cómo usarlo:

Dado el siguiente conjunto de datos,

    ASR_API     <- c("CV",  "F",    "IER",  "LS-c", "LS-o")
    Human       <- c(NA,    5.8,    12.7,   NA, NA)
    Google      <- c(23.2,  24.2,   16.6,   12.1,   28.8)
    GoogleCloud <- c(23.3,  26.3,   18.3,   12.3,   27.3)
    IBM     <- c(21.8,  47.6,   24.0,   9.8,    25.3)
    Microsoft   <- c(29.1,  28.1,   23.1,   18.8,   35.9)
    Speechmatics    <- c(19.1,  38.4,   21.4,   7.3,    19.4)
    Wit_ai      <- c(35.6,  54.2,   37.4,   19.2,   41.7)
    dt     <- data.table(ASR_API,Human, Google, GoogleCloud, IBM, Microsoft, Speechmatics, Wit_ai)
> dt
   ASR_API Human Google GoogleCloud  IBM Microsoft Speechmatics Wit_ai
1:      CV    NA   23.2        23.3 21.8      29.1         19.1   35.6
2:       F   5.8   24.2        26.3 47.6      28.1         38.4   54.2
3:     IER  12.7   16.6        18.3 24.0      23.1         21.4   37.4
4:    LS-c    NA   12.1        12.3  9.8      18.8          7.3   19.2
5:    LS-o    NA   28.8        27.3 25.3      35.9         19.4   41.7

Los valores normalizados se pueden obtener así:

> dtn <- normalize(dt, method = "standardize", range = c(0, 1), margin = 1L, on.constant = "quiet")
> dtn
   ASR_API      Human     Google GoogleCloud         IBM  Microsoft Speechmatics      Wit_ai
1:      CV         NA  0.3361245   0.2893457 -0.28468670  0.3247336  -0.18127203 -0.16032655
2:       F -0.7071068  0.4875320   0.7715885  1.59862532  0.1700986   1.55068347  1.31594762
3:     IER  0.7071068 -0.6631646  -0.5143923 -0.12409420 -0.6030768   0.02512682 -0.01746131
4:    LS-c         NA -1.3444981  -1.4788780 -1.16064578 -1.2680075  -1.24018782 -1.46198764
5:    LS-o         NA  1.1840062   0.9323361 -0.02919864  1.3762521  -0.15435044  0.32382788

donde el método calculado a mano simplemente ignora las colmuns que contienen NA:

> dt %>% mutate(normalizedHuman = (Human - mean(Human))/sd(Human)) %>% 
+ mutate(normalizedGoogle = (Google - mean(Google))/sd(Google)) %>% 
+ mutate(normalizedGoogleCloud = (GoogleCloud - mean(GoogleCloud))/sd(GoogleCloud)) %>% 
+ mutate(normalizedIBM = (IBM - mean(IBM))/sd(IBM)) %>% 
+ mutate(normalizedMicrosoft = (Microsoft - mean(Microsoft))/sd(Microsoft)) %>% 
+ mutate(normalizedSpeechmatics = (Speechmatics - mean(Speechmatics))/sd(Speechmatics)) %>% 
+ mutate(normalizedWit_ai = (Wit_ai - mean(Wit_ai))/sd(Wit_ai))
  ASR_API Human Google GoogleCloud  IBM Microsoft Speechmatics Wit_ai normalizedHuman normalizedGoogle
1      CV    NA   23.2        23.3 21.8      29.1         19.1   35.6              NA        0.3361245
2       F   5.8   24.2        26.3 47.6      28.1         38.4   54.2              NA        0.4875320
3     IER  12.7   16.6        18.3 24.0      23.1         21.4   37.4              NA       -0.6631646
4    LS-c    NA   12.1        12.3  9.8      18.8          7.3   19.2              NA       -1.3444981
5    LS-o    NA   28.8        27.3 25.3      35.9         19.4   41.7              NA        1.1840062
  normalizedGoogleCloud normalizedIBM normalizedMicrosoft normalizedSpeechmatics normalizedWit_ai
1             0.2893457   -0.28468670           0.3247336            -0.18127203      -0.16032655
2             0.7715885    1.59862532           0.1700986             1.55068347       1.31594762
3            -0.5143923   -0.12409420          -0.6030768             0.02512682      -0.01746131
4            -1.4788780   -1.16064578          -1.2680075            -1.24018782      -1.46198764
5             0.9323361   -0.02919864           1.3762521            -0.15435044       0.32382788

(NormalizedHuman se hace una lista de NA ...)

Con respecto a la selección de columnas específicas para el cálculo, se puede emplear un método genérico como este:

data_vars <- df_full %>% dplyr::select(-ASR_API,-otherVarNotToBeUsed)
meta_vars <- df_full %>% dplyr::select(ASR_API,otherVarNotToBeUsed)
data_varsn <- normalize(data_vars, method = "standardize", range = c(0, 1), margin = 1L, on.constant = "quiet")
dtn <- cbind(meta_vars,data_varsn)
usuario1767316
fuente
0

@BBKim dio la mejor respuesta, pero se puede hacer más corto. Me sorprende que a nadie se le haya ocurrido todavía.

dat <- data.frame(x = rnorm(10, 30, .2), y = runif(10, 3, 5)) dat <- apply(dat, 2, function(x) (x - mean(x)) / sd(x))

Ian
fuente