R:如何在给定不同数据帧(训练集)的值的情况下对一个数据帧(测试集)进行归一化
R: How to normalize one dataframe (test set) given the values of a different dataframe (training set)
我有一个代表测试集 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 上应用这个归一化步骤,min
和 max
值取自训练集中的各个列,而不是测试集。我应该怎么做?
谢谢指点!
Edits:按照@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
处理因子列破坏了数据的结构。
可能最简单的方法是使用预先存在的功能,因为它最方便。例如,在这里,我们可以使用 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
我们看到 df1
中的范围 (max - min
) 对于 mpg
是 14,对于 cyl
是 4。如果我们查看最大值对于 df2
,对于 mpg
是 33.9。从 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
,考虑到我们需要遍历每一列,进行操作,然后 return 新列。这通常可以使用 apply
系列的函数来实现。由于两个不同的数据框涉及相同的列名,因此迭代列名似乎是一个想法。例如,
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
确实如此。
我有一个代表测试集 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 上应用这个归一化步骤,min
和 max
值取自训练集中的各个列,而不是测试集。我应该怎么做?
谢谢指点!
Edits:按照@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
处理因子列破坏了数据的结构。
可能最简单的方法是使用预先存在的功能,因为它最方便。例如,在这里,我们可以使用 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
我们看到 df1
中的范围 (max - min
) 对于 mpg
是 14,对于 cyl
是 4。如果我们查看最大值对于 df2
,对于 mpg
是 33.9。从 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
,考虑到我们需要遍历每一列,进行操作,然后 return 新列。这通常可以使用 apply
系列的函数来实现。由于两个不同的数据框涉及相同的列名,因此迭代列名似乎是一个想法。例如,
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
确实如此。