Standardizzare le colonne di dati in R


209

Ho un set di dati chiamato spamche contiene 58 colonne e circa 3500 righe di dati relativi ai messaggi di spam.

Ho intenzione di eseguire una regressione lineare su questo set di dati in futuro, ma mi piacerebbe fare un po 'di pre-elaborazione in anticipo e standardizzare le colonne per avere zero varianza media e unità.

Mi è stato detto che il modo migliore per farlo è con R, quindi mi piacerebbe chiedere come posso ottenere la normalizzazione con R ? Ho già caricato correttamente i dati e sto solo cercando alcuni pacchetti o metodi per eseguire questo compito.

Risposte:


533

Devo presumere che intendevi dire che volevi una media di 0 e una deviazione standard di 1. Se i tuoi dati sono in un frame di dati e tutte le colonne sono numeriche, puoi semplicemente chiamare la scalefunzione sui dati per fare quello che vuoi.

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)

L'uso delle funzioni integrate è di classe. Come questo gatto:

inserisci qui la descrizione dell'immagine


24
Sì, mio ​​errore, intendevo 0 media. E questo è piuttosto un gatto di classe
Hoser

8
L'utilizzo di +1 può essere lento anche come questo grasso gatto :) (colMeans qui)
agstudy

1
@agstudy Abbastanza giusto. Dovrei prendere l'abitudine di usare di più colMeans / colSums. Immagino di non pensarci a meno che non mi trovo in una situazione in cui conta davvero ...
Dason

137
questo sito ha bisogno di più gatti +1
LoveMeow,

35
Attenzione: la scala trasforma anche il frame di dati in una matrice
Julian Karls,

89

Comprendendo che la domanda è vecchia e una risposta è accettata, fornirò un'altra risposta come riferimento.

scaleè limitato dal fatto che ridimensiona tutte le variabili . La soluzione seguente consente di ridimensionare solo i nomi di variabili specifiche mantenendo invariate le altre variabili (e i nomi delle variabili potrebbero essere generati dinamicamente):

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

che mi dà questo:

> 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

e

> 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) : affrontato il commento di Julian: l'output di scaleè la matrice Nx1, quindi idealmente dovremmo aggiungere un as.vectorper riconvertire il tipo di matrice in un tipo vettoriale. Grazie Giuliano!

EDIT 2 (2019) : Citando il commento di Duccio A.: Per l'ultimo dplyr (versione 0.8) è necessario modificare dplyr :: funcs con elenco, comedat %>% mutate_each_(list(~scale(.) %>% as.vector), vars=c("y","z"))

EDIT 3 (2020) : Grazie a @mj_whales: la vecchia soluzione è obsoleta e ora dobbiamo usarla mutate_at.


Questo metodo funziona perfettamente, specialmente quando ho una combinazione di variabili categoriche e numeriche. Ho solo una domanda che cosa significa questo operatore "%>%"?
Nooshinha,

9
@ weber85, è un operatore "pipe" (dalla programmazione funzionale). Invece di scrivere f(g(x))sarebbe più bello se si scrive x %>% g %>% f. In altre parole, dat %>% mutate_each_(funs(scale),vars=c("y","z"))è giusto mutate_each_(dat,funs(scale),vars=c("y","z")). L'operatore aiuta molto quando una catena è molto lunga poiché f(g(h(i(j(x)))))può essere molto difficile da leggere.
Akhmed

Utilizzando questo approccio, le colonne su cui viene applicata la scala vengono trasferite dalle matrici vettoriale (numerico di classe) a Nx1. Ciò potrebbe (e nel mio caso lo ha fatto) causare alcuni errori nei pacchetti che presuppongono che ogni colonna di un data.frame sia un vettore.
Julian Karls,

2
Per l'ultima dplyr(versione 0.8) è necessario modificare dplyr::funcscon list, comedat %>% mutate_each_(list(~scale(.) %>% as.vector), vars=c("y","z"))
Duccio A

2
mutate_each_()è ora obsoleto. Puoi usare mutate_at()invece. Il nuovo modo di farlo sarebbe:dat2 <- dat %>% mutate_at(c("y", "z"), scale)

60

Questo ha 3 anni. Tuttavia, sento di dover aggiungere quanto segue:

La normalizzazione più comune è la trasformazione z , in cui sottrai la media e dividi per la deviazione standard della tua variabile. Il risultato avrà media = 0 e sd = 1.

Per questo, non è necessario alcun pacchetto.

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

Questo è tutto.


Totalmente un modo semplice per farlo. Grazie
Pedro Neves,

E rende l'utilizzo è molto più facile dplyr: mutate(var = (var - mean(var))/sd(var)).
RobertMyles,

Ma può essere usato per ottenere il punteggio z per due variabili?
lf_araujo,

denormalizzare myVar <- (zVar * sd(zVar)) + mean(zVar), giusto?
Artur_Indio,

4
@Artur_Indio Quasi: newVar <- (zVar * sd(myVar)) + mean(myVar). Devi usare la media / sd originale. Mentre lo hai scritto, ti moltiplichi sd(zVar)=1e aggiungi mean(zVar)=0, quindi nulla cambierà :)
random_forest_fanatic

24

Il pacchetto 'Caret' fornisce metodi per la preelaborazione dei dati (ad es. Centraggio e ridimensionamento). È inoltre possibile utilizzare il seguente codice:

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

Maggiori dettagli: http://www.inside-r.org/node/86978


17

Quando ho usato la soluzione dichiarata da Dason, invece di ottenere un frame di dati di conseguenza, ho ottenuto un vettore di numeri (i valori in scala del mio df).

Nel caso in cui qualcuno abbia lo stesso problema, devi aggiungere as.data.frame () al codice, in questo modo:

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

Spero che questo sia utile per ppl avendo lo stesso problema!


Bella soluzione! Nel caso in cui qualcuno desideri escludere una colonna dal ridimensionamento, puoi farlo in questo modo: train_dt[-24] <- scale(train_dt[-24]) dove "24" è il numero di colonna da escludere
NetEmmanuel

13

Puoi facilmente normalizzare i dati anche usando data.Normalizzazione della funzione nel pacchetto clusterSim. Fornisce diversi metodi di normalizzazione dei dati.

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

argomenti

x tipo di normalizzazione
vettore, matrice o set di dati
: n0 - senza normalizzazione

n1 - standardizzazione ((x-mean) / sd)

n2 - standardizzazione posizionale ((x-mediana) / pazza)

n3 - unitizzazione ((x-mean) / range)

n3a - unità di posizione ((x-mediana) / intervallo)

n4 - unitizzazione con zero minimo ((x-min) / intervallo)

n5 - normalizzazione nell'intervallo <-1,1> ((x-mean) / max (abs (x-mean)))

n5a - normalizzazione posizionale nell'intervallo <-1,1> ((x-mediana) / max (abs (x-mediana)))

n6 - trasformazione quoziente (x / sd)

n6a - trasformazione del quoziente posizionale (x / mad)

n7 - trasformazione quoziente (x / intervallo)

n8 - trasformazione quoziente (x / max)

n9 - trasformazione quoziente (x / media)

n9a - trasformazione del quoziente posizionale (x / mediana)

n10 - trasformazione quoziente (x / somma)

n11 - trasformazione quoziente (x / sqrt (SSQ))

n12 - normalizzazione ((x-mean) / sqrt (sum ((x-mean) ^ 2)))

n12a - normalizzazione posizionale ((x-mediana) / sqrt (somma ((x-mediana) ^ 2)))

n13 - normalizzazione con zero come punto centrale ((x-midrange) / (range / 2))

normalizzazione
"colonna" - normalizzazione da variabile, "riga" - normalizzazione per oggetto


questo pacchetto non è disponibile per R versione 3.4.3
JdP

11

Con dplyrv0.7.4 è possibile ridimensionare tutte le variabili utilizzando 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

Variabili specifiche possono essere escluse 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

Creato il 24-04-2018 dal pacchetto reprex (v0.2.0).


9

Ancora una volta, anche se questa è una vecchia domanda, è molto rilevante! E ho trovato un modo semplice per normalizzare alcune colonne senza la necessità di alcun pacchetto:

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

Per esempio

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)

Vedrai che le colonne ye z sono state normalizzate. Nessun pacchetto necessario :-)


8

La scala può essere utilizzata sia per il frame di dati completo sia per colonne specifiche. Per colonne specifiche, è possibile utilizzare il seguente codice:

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

Frame di dati completo

trainingSet <- scale(trainingSet)

3

Il dplyrpacchetto ha due funzioni che lo fanno.

> require(dplyr)

Per mutare specifiche colonne di una tabella di dati, è possibile utilizzare la funzione mutate_at(). Per mutare tutte le colonne, puoi usaremutate_all .

Di seguito è riportato un breve esempio per l'utilizzo di queste funzioni per standardizzare i dati.

Mutare colonne specifiche:

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 

Muta tutte le colonne:

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 

1

Prima di trovare questo thread, ho avuto lo stesso problema. Avevo tipi di colonna dipendenti dall'utente, quindi ho scritto un forciclo che li attraversava e otteneva le colonne necessarie scale'd. Probabilmente ci sono modi migliori per farlo, ma questo ha risolto il problema bene:

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

as.vectorè una parte necessaria, perché si è scoperto che scalefa rownames x 1matrice che di solito non è quello che vuoi avere nel tuo data.frame.


0

Utilizzare il pacchetto "recommenderlab". Scarica e installa il pacchetto. Questo pacchetto ha un comando "Normalizza" incorporato. Consente inoltre di scegliere uno dei molti metodi per la normalizzazione, ovvero "centro" o "punteggio Z". Seguire il seguente esempio:

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

1
Questa risposta non risponde alla domanda.
f0nzie,

0

Il normalizzare funzione di del pacchetto BBMisc è stata lo strumento giusto per me poiché può gestire i valori NA.

Ecco come usarlo:

Dato il seguente set di dati,

    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

i valori normalizzati possono essere ottenuti in questo modo:

> 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

dove il metodo calcolato a mano ignora solo i colmuns contenenti 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 è composto da un elenco di NA ...)

per quanto riguarda la selezione di colonne specifiche per il calcolo, un metodo generico può essere impiegato come questo:

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)

0

@BBKim ha praticamente dato la risposta migliore, ma può essere fatto più brevemente. Sono sorpreso che nessuno l'abbia ancora inventato.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.