【问题标题】:Standardize data columns in R标准化 R 中的数据列
【发布时间】:2013-02-19 08:40:39
【问题描述】:

我有一个名为 spam 的数据集,其中包含 58 列和大约 3500 行与垃圾邮件相关的数据。

我计划将来对此数据集运行一些线性回归,但我想事先进行一些预处理并将列标准化以具有零均值和单位方差。

有人告诉我最好的方法是使用 R,所以我想问一下如何使用 R 实现标准化?我已经正确加载了数据,我只是在寻找一些包或方法来执行此任务。

【问题讨论】:

    标签: r normalization


    【解决方案1】:

    下面的代码可能是实现这一目标的最短方法。

    dataframe <- apply(dataframe, 2, scale)
    

    【讨论】:

      【解决方案2】:

      collapse 包提供了最快的缩放功能 - 使用 Welfords 在线算法在 C++ 中实现:

      dat <- data.frame(x = rnorm(1e6, 30, .2), 
                        y = runif(1e6, 3, 5),
                        z = runif(1e6, 10, 20))
      
      library(collapse)
      library(microbenchmark)
      microbenchmark(fscale(dat), scale(dat))
      
      Unit: milliseconds
              expr       min       lq      mean    median        uq      max neval cld
       fscale(dat)  27.86456  29.5864  38.96896  30.80421  43.79045 313.5729   100  a 
        scale(dat) 357.07130 391.0914 489.93546 416.33626 625.38561 793.2243   100   b
      

      此外:fscale 是 S3 通用的向量、矩阵和数据帧,还支持分组和/或加权缩放操作,以及缩放到任意均值和标准差。

      【讨论】:

        【解决方案3】:

        @BBKim 几乎给出了最好的答案,但它可以做得更短。我很惊讶还没有人想出它。

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

        【讨论】:

          【解决方案4】:

          意识到问题已经过时并且一个答案被接受,我将提供另一个答案以供参考。

          scale 受限于它可以缩放所有变量。下面的解决方案允许仅缩放特定的变量名称,同时保持其他变量不变(并且变量名称可以动态生成):

          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
          

          这给了我这个:

          > 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
          

          > 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):针对 Julian 的评论:scale 的输出是 Nx1 矩阵,因此理想情况下我们应该添加一个 as.vector 以将矩阵类型转换回向量类型。谢谢朱利安!

          EDIT 2 (2019):引用 Duccio A. 的评论:对于最新的 dplyr(0.8 版),您需要使用列表更改 dplyr::funcs,例如 dat %&gt;% mutate_each_(list(~scale(.) %&gt;% as.vector), vars=c("y","z"))

          EDIT 3 (2020):感谢@mj_whales:旧的解决方案已被弃用,现在我们需要使用mutate_at

          【讨论】:

          • @weber85,它是一个“管道”运算符(来自函数式编程)。与其写f(g(x)),不如写x %&gt;% g %&gt;% f 会更好看。换句话说,dat %&gt;% mutate_each_(funs(scale),vars=c("y","z")) 就是mutate_each_(dat,funs(scale),vars=c("y","z"))。当链很长时,运算符会很有帮助,因为f(g(h(i(j(x))))) 可能很难阅读。
          • 最新的dplyr(0.8版)你需要把dplyr::funcs改成list,比如dat %&gt;% mutate_each_(list(~scale(.) %&gt;% as.vector), vars=c("y","z"))
          • mutate_each_() 现在已弃用。您可以改用mutate_at()。新的方法是:dat2 &lt;- dat %&gt;% mutate_at(c("y", "z"), scale)
          • dplyr 格局再次发生变化。在dplyr 1.0.0(开发中)mutate(across(x:y, scale)) 现在似乎是正确的解决方案。
          • ... 或者您可以直接使用 dat[columns] &lt;- scale(dat[columns]),它在过去 20 年中一直有效;-)
          【解决方案5】:

          BBMisc 包中的 normalize 函数对我来说是正确的工具,因为它可以处理 NA 值。

          这里是如何使用它:

          给定以下数据集,

              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
          

          标准化值可以这样获得:

          > 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
          

          手工计算的方法只是忽略包含 NA 的 colmun:

          > 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 是一个 NA 列表 ...)

          关于计算特定列的选择,可以采用这样的通用方法:

          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)
          

          【讨论】:

            【解决方案6】:

            dplyr 包有两个函数可以做到这一点。

            > require(dplyr)
            

            要改变数据表的特定列,您可以使用函数mutate_at()。要改变所有列,您可以使用mutate_all

            以下是使用这些函数标准化数据的简要示例。

            改变特定的列:

            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 
            

            改变所有列:

            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 
            

            【讨论】:

              【解决方案7】:

              再一次,尽管这是一个老问题,但它非常相关!而且我找到了一种无需任何包即可规范化某些列的简单方法:

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

              例如

              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)
              

              您将看到 y 和 z 列已标准化。不需要包:-)

              【讨论】:

                【解决方案8】:

                使用dplyr v0.7.4,所有变量都可以使用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
                

                可以使用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
                

                reprex package (v0.2.0) 于 2018 年 4 月 24 日创建。

                【讨论】:

                  【解决方案9】:

                  比例可用于完整数据框和特定列。 对于特定的列,可以使用以下代码:

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

                  完整的数据框

                  trainingSet <- scale(trainingSet)
                  

                  【讨论】:

                    【解决方案10】:

                    在我碰巧找到这个帖子之前,我遇到了同样的问题。我有用户相关的列类型,所以我编写了一个for 循环遍历它们并获取所需的列scale'd。可能有更好的方法可以做到这一点,但这很好地解决了问题:

                     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 是一个必需的部分,因为事实证明 scale 执行 rownames x 1 矩阵,这通常不是您希望在 data.frame 中拥有的。

                    【讨论】:

                      【解决方案11】:

                      'Caret' 包提供了预处理数据的方法(例如居中和缩放)。您也可以使用以下代码:

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

                      更多详情:http://www.inside-r.org/node/86978

                      【讨论】:

                        【解决方案12】:

                        当我使用 Dason 所述的解决方案时,我没有得到数据框,而是得到了一个数字向量(我的 df 的缩放值)。

                        如果有人遇到同样的问题,您必须将 as.data.frame() 添加到代码中,如下所示:

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

                        我希望这对有同样问题的人有用!

                        【讨论】:

                        • 不错的解决方案!如果有人想从缩放中排除列,您可以这样做:train_dt[-24] &lt;- scale(train_dt[-24]) 其中“24”是要排除的列号
                        【解决方案13】:

                        这是 3 岁。不过,我觉得我必须添加以下内容:

                        最常见的归一化是 z 变换,您减去平均值并除以变量的标准差。结果将具有 mean=0 和 sd=1。

                        为此,您不需要任何软件包。

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

                        就是这样。

                        【讨论】:

                        • 完全是执行此操作的简单方法。谢谢
                        • 让 dplyr 使用起来更容易:mutate(var = (var - mean(var))/sd(var)).
                        • 但这可以用来获取两个变量的 z 分数吗?
                        • 去规范化myVar &lt;- (zVar * sd(zVar)) + mean(zVar),对吧?
                        • @Artur_Indio 几乎:newVar &lt;- (zVar * sd(myVar)) + mean(myVar)。您必须使用原始均值/标准差。正如你写的那样,你将乘以 sd(zVar)=1 并加上 mean(zVar)=0,所以什么都不会改变 :)
                        【解决方案14】:

                        您也可以使用 clusterSim 包中的 data.Normalization 函数轻松地对数据进行标准化。它提供了不同的数据规范化方法。

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

                        参数

                        x
                        向量、矩阵或数据集 类型
                        标准化类型: n0 - 没有归一化

                        n1 - 标准化 ((x-mean)/sd)

                        n2 - 位置标准化 ((x-median)/mad)

                        n3 - 单元化 ((x-mean)/range)

                        n3a - 位置单元化 ((x-median)/range)

                        n4 - 最小化为零 ((x-min)/range)

                        n5 - 范围内的归一化 ((x-mean)/max(abs(x-mean)))

                        n5a - 范围内的位置归一化 ((x-median)/max(abs(x-median)))

                        n6 - 商变换 (x/sd)

                        n6a - 位商变换 (x/mad)

                        n7 - 商变换(x/范围)

                        n8 - 商变换(x/max)

                        n9 - 商变换(x/mean)

                        n9a - 位置商变换(x/中位数)

                        n10 - 商变换 (x/sum)

                        n11 - 商变换 (x/sqrt(SSQ))

                        n12 - 归一化 ((x-mean)/sqrt(sum((x-mean)^2)))

                        n12a - 位置归一化 ((x-median)/sqrt(sum((x-median)^2)))

                        n13 - 以零为中心点的归一化 ((x-midrange)/(range/2))

                        标准化
                        "column" - 按变量归一化,"row" - 按对象归一化

                        【讨论】:

                        • 此包不适用于 R 版本 3.4.3
                        【解决方案15】:

                        使用包“recommenderlab”。下载并安装软件包。 这个包有一个内置的命令“Normalize”。它还允许您选择多种标准化方法之一,即“中心”或“Z 分数” 按照下面的例子:

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

                        【讨论】:

                        • 这个答案没有解决问题。
                        【解决方案16】:

                        我必须假设您的意思是说您想要平均值为 0 和标准差为 1。如果您的数据在数据框中并且所有列都是数字,您可以简单地对数据调用 scale 函数做你想做的事。

                        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)
                        

                        使用内置函数是优雅的。喜欢这只猫:

                        【讨论】:

                        • 是的,我的意思是 0 意思是我的错误。那是一只非常优雅的猫
                        • +1 使用 apply 也会像这只肥猫一样慢:) (colMeans here)
                        • @agstudy 够公平的。我应该养成更多使用 colMeans/colSums 的习惯。我想我不会想到它,除非我处于真正重要的情况......
                        • 这个网站需要更多的猫 +1
                        • 警告:scale 也会将数据框转换为矩阵
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2020-08-02
                        • 2014-12-31
                        • 2021-02-28
                        • 1970-01-01
                        • 2016-09-18
                        • 2018-05-19
                        相关资源
                        最近更新 更多