【问题标题】:R: How to normalize one dataframe (test set) given the values of a different dataframe (training set)R:如何在给定不同数据帧(训练集)的值的情况下标准化一个数据帧(测试集)
【发布时间】:2019-01-12 02:20:47
【问题描述】:

我有一个代表测试集 T 的数据框和另一个代表训练集 D 的数据框。这两个数据集中的列与从同一个数据框中提取的列完全相同。

我使用以下代码对训练集 D 进行归一化

MaxMinNormalize <- function(num) {
  if (is.factor(num)) num
  else ((num - min(num)) / (max(num) - min(num)))
}

D_n <- as.data.frame(lapply(D, MaxMinNormalize))

数据中的某些列是因子,其他列是数字,这就是归一化函数的原因。

我想在测试集 T 上应用这个标准化步骤,minmax 值取自训练集中的各个列,而不是测试集。我该怎么做呢?

感谢您的任何指点!


编辑:按照@coffeinjunky 的指示,尝试了以下代码来测试使用混合类型列(数字和因子)的能力:

df <- mtcars[,c("mpg", "cyl", "am", "gear")]

df$am <- as.factor(df$am)

df$gear <- as.factor(df$gear)

df1 <- df[1:16,]
df2 <- df[17:32,]

summary(df1)
summary(df2)

new_df <- data.frame(sapply(names(df1), function(col) {
  ifelse(is.factor(df2[[col]]), 
         df2[[col]],
         (df2[[col]]-min(df1[[col]]))/(max(df1[[col]])-min(df1[[col]]))) 

}))

head(new_df)
summary(new_df)

但结果很奇怪:不知何故,函数也存储在数据框中,列名丢失了。

> head(new_df)
     sapply.names.df1...function.col...
mpg                           0.3071429
cyl                           1.0000000
am                            1.0000000
gear                          1.0000000
> summary(new_df)
 sapply.names.df1...function.col...
 Min.   :0.3071                    
 1st Qu.:0.8268                    
 Median :1.0000                    
 Mean   :0.8268                    
 3rd Qu.:1.0000                    
 Max.   :1.0000    

我怀疑处理因子列的ifelse 破坏了数据的结构。

【问题讨论】:

  • TR中的保留字,表示TRUE。请不要调用您的数据框T
  • @coffeinjunky 我明白了,谢谢!
  • 感谢您接受我的回答。我进一步完善了它。看看可能会有所帮助。

标签: r dataframe normalize


【解决方案1】:

可能最简单的方法是使用预先存在的功能,因为它是最方便的。例如,在这里,我们可以使用 caret 包中提供的函数。

为了说明,让我们获取一些玩具数据:

# get some test data:
df <- mtcars[,c("mpg", "cyl")]
df1 <- df[1:16,]  # training data
df2 <- df[17:32,] # test data to be scaled

让我们来看看我们会期待什么。

summary(df1) # some output ommitted
      mpg            cyl     
 Min.   :10.4   Min.   :4.0  
 Max.   :24.4   Max.   :8.0  

summary(df2)
      mpg             cyl       
 Min.   :13.30   Min.   :4.000  
 Max.   :33.90   Max.   :8.000  

我们看到df1mpg 的范围(max - min) 为14,cyl 为4。如果我们查看df2 的最大值,则@ 为33.9 987654329@。从df1 中减去最小值,即 10.4,然后除以 14,应该得到 23.5/14=1.6785。类似的数学适用于其他列和值。

现在,让我们使用caret::preProcess 看看我们是否得到相同的值。

library(caret)
train_stats <- preProcess(df1, method = "range")
new_df1 <- predict(train_stats, df1)
new_df2 <- predict(train_stats, df2)

让我们首先检查 new_df1 是否按比例缩放到 0-1 范围,应该是这样。

summary(new_df1)
# some output omitted:
      mpg              cyl       
 Min.   :0.0000   Min.   :0.000  
 Max.   :1.0000   Max.   :1.000  

现在让我们看看我们是否在测试集上得到了预期值:

summary(new_df2)
# some output omitted:
      mpg              cyl        
 Min.   :0.2071   Min.   :0.0000  
 Max.   :1.6786   Max.   :1.0000  

是的,看起来这行得通。

现在,只是为了展示如何实现这个by hand,考虑我们需要遍历每一列,做一个操作,并返回新的列。这通常可以使用apply-family 的函数来实现。由于两个不同的数据框涉及相同的列名,因此迭代列名似乎是一个想法。例如,

sapply(names(df1), function(x) (...) )

将应用 function 并将 df1 中的每个列名称作为参数。让我们按以下方式使用它:

df2[] <- sapply(names(df1), function(col) {
    if(is.factor(df2[[col]])) df2[[col]] else (df2[[col]]-min(df1[[col]]))/(max(df1[[col]])-min(df1[[col]]))})

让我们看看这是否给出了预期的结果:

summary(df2)
      mpg              cyl        
 Min.   :0.2071   Min.   :0.0000  
 Max.   :1.6786   Max.   :1.0000  

它的作用。

【讨论】:

  • 这适用于所有数值字段,但因子字段都转换为双精度类型。我试图从sapply 更改为lapply,但它不起作用。你有什么指点吗?
  • 只需在函数开头添加控制语句即可。看来您已经在自己的函数中完成了此操作。将sapply 更改为lapply 只会更改输出格式,不会更改正在应用的函数。
  • 谢谢@coffeinjunky,但代码没有按预期工作:这些因素似乎搞砸了处理。有关详细信息,请参阅我在问题中的编辑。谢谢!
  • 太棒了,这行得通!我接受了答案。谢谢!
猜你喜欢
  • 2019-10-14
  • 2016-11-03
  • 1970-01-01
  • 2018-06-04
  • 2019-05-23
  • 2020-02-11
  • 2022-01-11
  • 2020-03-09
  • 2018-01-11
相关资源
最近更新 更多