Standardiser les colonnes de données dans R

209

J'ai un ensemble de données appelé spamqui contient 58 colonnes et environ 3500 lignes de données liées aux messages de spam.

Je prévois d'exécuter une régression linéaire sur cet ensemble de données à l'avenir, mais j'aimerais faire un prétraitement à l'avance et normaliser les colonnes pour avoir une moyenne et une variance d'unité nulles.

On m'a dit que la meilleure façon de procéder est avec R, donc je voudrais demander comment puis-je réaliser la normalisation avec R ? J'ai déjà les données correctement chargées et je cherche juste des packages ou des méthodes pour effectuer cette tâche.

Hoser
la source

Réponses:

533

Je dois supposer que vous vouliez dire que vous vouliez une moyenne de 0 et un écart-type de 1. Si vos données sont dans un cadre de données et que toutes les colonnes sont numériques, vous pouvez simplement appeler la scalefonction sur les données pour faire ce que vous voulez.

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'utilisation de fonctions intégrées est élégante. Comme ce chat:

entrez la description de l'image ici

Dason
la source
24
Oui mon erreur, je voulais dire 0 signifie. Et c'est un chat assez classe
Hoser
8
+1 en utilisant appliquer peut être lent aussi comme ce gros chat :) (colMeans here)
agstudy
1
@agstudy Assez juste. Je devrais prendre l'habitude d'utiliser davantage colMeans / colSums. Je suppose que je n'y pense pas à moins d'être dans une situation où cela compte vraiment ...
Dason
137
ce site a besoin de plus de chats +1
LoveMeow
35
Avertissement: l'échelle transforme également la trame de données en matrice
Julian Karls
89

Réalisant que la question est ancienne et qu'une réponse est acceptée, je fournirai une autre réponse pour référence.

scale est limité par le fait qu'il évolue toutes les variables . La solution ci-dessous permet de mettre à l'échelle uniquement des noms de variables spécifiques tout en préservant les autres variables inchangées (et les noms de variables pourraient être générés dynamiquement):

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

ce qui me donne ceci:

> 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

et

> 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) : Commentaire de Julian: la sortie de scaleest une matrice Nx1, donc idéalement, nous devrions ajouter un as.vectorpour reconvertir le type de matrice en un type vectoriel. Merci Julian!

EDIT 2 (2019) : Citant le commentaire de Duccio A.: Pour la dernière version de dplyr (version 0.8), vous devez changer dplyr :: funcs avec list, commedat %>% mutate_each_(list(~scale(.) %>% as.vector), vars=c("y","z"))

EDIT 3 (2020) : Merci à @mj_whales: l'ancienne solution est obsolète et maintenant nous devons l'utiliser mutate_at.

akhmed
la source
Cette méthode fonctionne parfaitement, surtout lorsque j'ai une combinaison de variables catégorielles et numériques. J'ai juste une question que signifie cet opérateur "%>%"?
nooshinha
9
@ weber85, c'est un opérateur "pipe" (issu de la programmation fonctionnelle). Au lieu d'écrire, f(g(x))ce serait plus joli si on écrivait x %>% g %>% f. En d'autres termes, dat %>% mutate_each_(funs(scale),vars=c("y","z"))c'est juste mutate_each_(dat,funs(scale),vars=c("y","z")). L'opérateur aide beaucoup quand une chaîne est très longue car elle f(g(h(i(j(x)))))peut être très difficile à lire.
akhmed
En utilisant cette approche, les colonnes sur lesquelles l'échelle est appliquée sont transférées des matrices vectorielles (classe numérique) aux matrices Nx1. Cela pourrait (et dans mon cas) provoquer des erreurs dans les packages qui supposent que chaque colonne d'un data.frame est un vecteur.
Julian Karls
2
Pour la dernière dplyr(version 0.8), vous devez changer dplyr::funcsavec list, commedat %>% mutate_each_(list(~scale(.) %>% as.vector), vars=c("y","z"))
Duccio A
2
mutate_each_()est désormais obsolète. Vous pouvez utiliser à la mutate_at()place. La nouvelle façon de procéder serait:dat2 <- dat %>% mutate_at(c("y", "z"), scale)
60

C'est 3 ans. Pourtant, je pense que je dois ajouter ce qui suit:

La normalisation la plus courante est la transformation z , où vous soustrayez la moyenne et divisez par l'écart-type de votre variable. Le résultat aura une moyenne = 0 et sd = 1.

Pour cela, vous n'avez besoin d'aucun package.

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

C'est tout.

fmb
la source
Une façon tout à fait simple de réaliser cela. Merci
Pedro Neves
Et rend l' utilisation dplyr beaucoup plus facile: mutate(var = (var - mean(var))/sd(var)).
RobertMyles
Mais cela peut-il être utilisé pour obtenir le z-score pour deux variables?
lf_araujo
à dénormaliser myVar <- (zVar * sd(zVar)) + mean(zVar), non?
Artur_Indio
4
Presque @Artur_Indio: newVar <- (zVar * sd(myVar)) + mean(myVar). Vous devez utiliser la moyenne / sd d'origine. Comme vous l'avez écrit, vous multipliez sd(zVar)=1et ajoutez mean(zVar)=0, donc rien ne changera :)
random_forest_fanatic
24

Le package «Caret» fournit des méthodes de prétraitement des données (par exemple, centrage et mise à l'échelle). Vous pouvez également utiliser le code suivant:

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

Plus de détails: http://www.inside-r.org/node/86978

DaniM
la source
17

Lorsque j'ai utilisé la solution indiquée par Dason, au lieu d'obtenir une trame de données en conséquence, j'ai obtenu un vecteur de nombres (les valeurs mises à l'échelle de mon df).

Si quelqu'un rencontre le même problème, vous devez ajouter as.data.frame () au code, comme ceci:

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

J'espère que ce sera utile pour ppl ayant le même problème!

Diego
la source
Bonne solution! Si quelqu'un veut exclure une colonne de sa mise à l'échelle, vous pouvez le faire comme ceci: train_dt[-24] <- scale(train_dt[-24]) où "24" est le numéro de colonne à exclure
NetEmmanuel
13

Vous pouvez facilement normaliser les données en utilisant également la fonction data.Normalization dans le package clusterSim. Il fournit différentes méthodes de normalisation des données.

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

Arguments

x type de normalisation de type
vecteur, matrice ou ensemble de données
: n0 - sans normalisation

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

n2 - standardisation positionnelle ((x-médiane) / folle)

n3 - unité ((x-moyenne) / plage)

n3a - unification positionnelle ((x-médiane) / plage)

n4 - unité avec zéro minimum ((x-min) / plage)

n5 - normalisation dans la plage <-1,1> ((x-moyenne) / max (abs (x-moyenne))))

n5a - normalisation positionnelle dans la plage <-1,1> ((x-médiane) / max (abs (x-médiane)))

n6 - transformation de quotient (x / sd)

n6a - transformation de quotient positionnel (x / mad)

n7 - transformation de quotient (x / plage)

n8 - transformation de quotient (x / max)

n9 - transformation de quotient (x / moyenne)

n9a - transformation de quotient positionnel (x / médiane)

n10 - transformation de quotient (x / somme)

n11 - transformation de quotient (x / sqrt (SSQ))

n12 - normalisation ((x-moyenne) / sqrt (somme ((x-moyenne) ^ 2)))

n12a - normalisation positionnelle ((x-médiane) / sqrt (somme ((x-médiane) ^ 2)))

n13 - normalisation avec zéro étant le point central ((x-milieu de gamme) / (plage / 2))

normalisation
"colonne" - normalisation par variable, "ligne" - normalisation par objet

Samehmagd
la source
ce package n'est pas disponible pour R version 3.4.3
JdP
11

Avec la version dplyr0.7.4, toutes les variables peuvent être mises à l'échelle en utilisant 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

Des variables spécifiques peuvent être exclues en utilisant 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

Créé le 2018-04-24 par le package reprex (v0.2.0).

pat-s
la source
9

Encore une fois, même s'il s'agit d'une vieille question, elle est très pertinente! Et j'ai trouvé un moyen simple de normaliser certaines colonnes sans avoir besoin de packages:

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

Par exemple

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)

Vous verrez que les colonnes y et z ont été normalisées. Aucun package requis :-)

BBKim
la source
8

L'échelle peut être utilisée pour le bloc de données complet et des colonnes spécifiques. Pour des colonnes spécifiques, le code suivant peut être utilisé:

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

Trame de données complète

trainingSet <- scale(trainingSet)
Amit
la source
3

Le dplyrpackage a deux fonctions qui font cela.

> require(dplyr)

Pour muter des colonnes spécifiques d'une table de données, vous pouvez utiliser la fonction mutate_at(). Pour muter toutes les colonnes, vous pouvez utilisermutate_all .

Voici un bref exemple d'utilisation de ces fonctions pour normaliser les données.

Muter des colonnes spécifiques:

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 

Muter toutes les colonnes:

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
la source
1

Avant de trouver ce fil, j'ai eu le même problème. J'avais des types de colonnes dépendants de l'utilisateur, j'ai donc écrit une forboucle en les parcourant et en obtenant les colonnes nécessaires scale. Il existe probablement de meilleures façons de le faire, mais cela a très bien résolu le problème:

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

as.vectorest une partie nécessaire, car il s'est avéré scaleque la rownames x 1matrice n'est généralement pas ce que vous voulez avoir dans votre data.frame.

Claud H
la source
0

Utilisez le package "recommenderlab". Téléchargez et installez le package. Ce paquet a une commande "Normalize" intégrée. Il vous permet également de choisir l'une des nombreuses méthodes de normalisation, à savoir «centre» ou «score Z». Suivez l'exemple suivant:

## 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
la source
1
Cette réponse ne répond pas à la question.
f0nzie
0

La fonction de normalisation du package BBMisc était le bon outil pour moi car elle peut gérer les valeurs NA.

Voici comment l'utiliser:

Étant donné l'ensemble de données suivant,

    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

des valeurs normalisées peuvent être obtenues comme ceci:

> 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

où la méthode calculée à la main ignore simplement les colmuns contenant des 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 fait une liste de NAs ...)

en ce qui concerne la sélection de colonnes spécifiques pour le calcul, une méthode générique peut être employée comme celle-ci:

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)
user1767316
la source
0

@BBKim a à peu près donné la meilleure réponse, mais cela peut être fait plus rapidement. Je suis surpris que personne ne l’ait encore proposé.

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
la source