sapply + if - 保留列名
sapply + if - retain column names
尽管它与 sapply - retain column 个名字有关,但我在那里找不到答案...
我有一个简单的函数可以在 0 和 1 之间缩放数据,并保留列名:
scale <- function(x){apply(x, 2, function(y) ((y)-min(y, na.rm=TRUE))/(max(y, na.rm=TRUE)-min(y, na.rm=TRUE)))}
现在我需要为 max(y) = min(y) 的情况添加一个 if 子句并像这样更改函数:
scale <- function(x){apply(x, 2, function(y) if(min(y, na.rm=TRUE)==max(y, na.rm=TRUE)) {0.5} else {((y)-min(y, na.rm=TRUE))/(max(y, na.rm=TRUE)-min(y, na.rm=TRUE))})}
像这样在输入数据帧上使用这些函数...
as.data.frame(scale(input[sapply(input,is.numeric)]))
生成不同的列名,其中原始函数保留名称,新函数以括号或连字符替换为点的方式修改它们:
示例列名称 w/o IF:
INL_Avg(S-B0-ETC-CDS-06C~PM_CD1_D_B0_SI_P0V_B.NM)
带有 IF 的示例列名称:
INL_Avg.S.B0.ETC.CDS.06C.PM_CD1_D_B0_SI_P0V_B.NM.
虽然我确实意识到这些列名称并不理想,但它是我需要使用的名称,我希望得到有关如何避免这种特殊字符替换的提示(将 USE.NAMES=TRUE 添加到 sapply won帮忙...)。
谢谢,马克
找到解决方案:
as.data.frame(scale(input[sapply(input,is.numeric)]),check.names = FALSE)
问题的根源在于您在数据框上使用 apply
。 apply
是为处理矩阵而构建的,所以它做的第一件事就是将你的数据框转换为矩阵,这是不必要的,然后当你转换回来时默认的数据框方法“修复”列名你不喜欢的方式。您可以通过将 check.names = FALSE
添加到 as.data.frame()
调用来解决此问题,但更好的方法是在数据框上使用 lapply
,在矩阵上使用 apply
,甚至如果我们给它一个向量输入,它就可以工作了。
我还强烈建议不要 用类似但不同的函数覆盖内置的scale
函数。这很容易导致错误。我重写了你的函数,调用它 scale01()
以明确区分。
我也修改了,如果输入是常量向量,有缺失值,只用0.5
填充非缺失值,这样看起来更安全
我使用 S3 分派基于输入 class 适当地工作,该方法建立在适用于数字向量的 default
方法之上。在这里,在向量 data.frame 和矩阵输入上进行了演示:
## defining the functions
scale01 = function(x, ...) {
UseMethod("scale01")
}
scale01.numeric = function(x, ...) {
minx = min(x, na.rm = TRUE)
maxx = max(x, na.rm = TRUE)
if(minx == maxx) {
x[!is.na(x)] = 0.5
return(x)
}
(x - minx) / (maxx - minx)
}
scale01.data.frame = function(x, ...) {
x[] = lapply(x, scale01)
x
}
scale01.matrix = function(x, ...) {
apply(x, MARGIN = 2, FUN = scale01)
}
## demonstrating usage
scale01(rnorm(5))
# [1] 0.0000000 1.0000000 0.4198958 0.6104154 0.2108150
scale01(mtcars[1:5, ])
# mpg cyl disp hp drat wt qsec vs am gear carb
# Mazda RX4 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.2678571 0.0000000 0 1 1 1.0000000
# Mazda RX4 Wag 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.4955357 0.1879195 0 1 1 1.0000000
# Datsun 710 1.0000000 0.0 0.0000000 0.0000000 0.93902439 0.0000000 0.7214765 1 1 1 0.0000000
# Hornet 4 Drive 0.6585366 0.5 0.5952381 0.2073171 0.00000000 0.7991071 1.0000000 1 0 0 0.0000000
# Hornet Sportabout 0.0000000 1.0 1.0000000 1.0000000 0.08536585 1.0000000 0.1879195 0 0 0 0.3333333
scale01(as.matrix(mtcars[1:5, ]))
# mpg cyl disp hp drat wt qsec vs am gear carb
# Mazda RX4 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.2678571 0.0000000 0 1 1 1.0000000
# Mazda RX4 Wag 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.4955357 0.1879195 0 1 1 1.0000000
# Datsun 710 1.0000000 0.0 0.0000000 0.0000000 0.93902439 0.0000000 0.7214765 1 1 1 0.0000000
# Hornet 4 Drive 0.6585366 0.5 0.5952381 0.2073171 0.00000000 0.7991071 1.0000000 1 0 0 0.0000000
# Hornet Sportabout 0.0000000 1.0 1.0000000 1.0000000 0.08536585 1.0000000 0.1879195 0 0 0 0.3333333
weird_name_df = data.frame(`weird column` = rnorm(5), `INL_Avg(S-B0-ETC-CDS-06C~PM_CD1_D_B0_SI_P0V_B.NM)` = rnorm(5), check.names = FALSE)
scale01(weird_name_df)
# weird column INL_Avg(S-B0-ETC-CDS-06C~PM_CD1_D_B0_SI_P0V_B.NM)
# 1 0.6135744 0.2237905
# 2 0.0000000 0.4086837
# 3 1.0000000 1.0000000
# 4 0.7061441 0.2803262
# 5 0.7693184 0.0000000
如果要转换数据框的所有数字列,我建议:
## base version
numeric_cols = sapply(your_data, is.numeric)
your_data[numeric_cols] = scale01(your_data[numeric_cols])
## dplyr version
library(dplyr)
your_data %>%
mutate(across(where(is.numeric), scale01))
尽管它与 sapply - retain column 个名字有关,但我在那里找不到答案...
我有一个简单的函数可以在 0 和 1 之间缩放数据,并保留列名:
scale <- function(x){apply(x, 2, function(y) ((y)-min(y, na.rm=TRUE))/(max(y, na.rm=TRUE)-min(y, na.rm=TRUE)))}
现在我需要为 max(y) = min(y) 的情况添加一个 if 子句并像这样更改函数:
scale <- function(x){apply(x, 2, function(y) if(min(y, na.rm=TRUE)==max(y, na.rm=TRUE)) {0.5} else {((y)-min(y, na.rm=TRUE))/(max(y, na.rm=TRUE)-min(y, na.rm=TRUE))})}
像这样在输入数据帧上使用这些函数...
as.data.frame(scale(input[sapply(input,is.numeric)]))
生成不同的列名,其中原始函数保留名称,新函数以括号或连字符替换为点的方式修改它们:
示例列名称 w/o IF: INL_Avg(S-B0-ETC-CDS-06C~PM_CD1_D_B0_SI_P0V_B.NM)
带有 IF 的示例列名称: INL_Avg.S.B0.ETC.CDS.06C.PM_CD1_D_B0_SI_P0V_B.NM.
虽然我确实意识到这些列名称并不理想,但它是我需要使用的名称,我希望得到有关如何避免这种特殊字符替换的提示(将 USE.NAMES=TRUE 添加到 sapply won帮忙...)。
谢谢,马克
找到解决方案
as.data.frame(scale(input[sapply(input,is.numeric)]),check.names = FALSE)
问题的根源在于您在数据框上使用 apply
。 apply
是为处理矩阵而构建的,所以它做的第一件事就是将你的数据框转换为矩阵,这是不必要的,然后当你转换回来时默认的数据框方法“修复”列名你不喜欢的方式。您可以通过将 check.names = FALSE
添加到 as.data.frame()
调用来解决此问题,但更好的方法是在数据框上使用 lapply
,在矩阵上使用 apply
,甚至如果我们给它一个向量输入,它就可以工作了。
我还强烈建议不要 用类似但不同的函数覆盖内置的scale
函数。这很容易导致错误。我重写了你的函数,调用它 scale01()
以明确区分。
我也修改了,如果输入是常量向量,有缺失值,只用0.5
填充非缺失值,这样看起来更安全
我使用 S3 分派基于输入 class 适当地工作,该方法建立在适用于数字向量的 default
方法之上。在这里,在向量 data.frame 和矩阵输入上进行了演示:
## defining the functions
scale01 = function(x, ...) {
UseMethod("scale01")
}
scale01.numeric = function(x, ...) {
minx = min(x, na.rm = TRUE)
maxx = max(x, na.rm = TRUE)
if(minx == maxx) {
x[!is.na(x)] = 0.5
return(x)
}
(x - minx) / (maxx - minx)
}
scale01.data.frame = function(x, ...) {
x[] = lapply(x, scale01)
x
}
scale01.matrix = function(x, ...) {
apply(x, MARGIN = 2, FUN = scale01)
}
## demonstrating usage
scale01(rnorm(5))
# [1] 0.0000000 1.0000000 0.4198958 0.6104154 0.2108150
scale01(mtcars[1:5, ])
# mpg cyl disp hp drat wt qsec vs am gear carb
# Mazda RX4 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.2678571 0.0000000 0 1 1 1.0000000
# Mazda RX4 Wag 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.4955357 0.1879195 0 1 1 1.0000000
# Datsun 710 1.0000000 0.0 0.0000000 0.0000000 0.93902439 0.0000000 0.7214765 1 1 1 0.0000000
# Hornet 4 Drive 0.6585366 0.5 0.5952381 0.2073171 0.00000000 0.7991071 1.0000000 1 0 0 0.0000000
# Hornet Sportabout 0.0000000 1.0 1.0000000 1.0000000 0.08536585 1.0000000 0.1879195 0 0 0 0.3333333
scale01(as.matrix(mtcars[1:5, ]))
# mpg cyl disp hp drat wt qsec vs am gear carb
# Mazda RX4 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.2678571 0.0000000 0 1 1 1.0000000
# Mazda RX4 Wag 0.5609756 0.5 0.2063492 0.2073171 1.00000000 0.4955357 0.1879195 0 1 1 1.0000000
# Datsun 710 1.0000000 0.0 0.0000000 0.0000000 0.93902439 0.0000000 0.7214765 1 1 1 0.0000000
# Hornet 4 Drive 0.6585366 0.5 0.5952381 0.2073171 0.00000000 0.7991071 1.0000000 1 0 0 0.0000000
# Hornet Sportabout 0.0000000 1.0 1.0000000 1.0000000 0.08536585 1.0000000 0.1879195 0 0 0 0.3333333
weird_name_df = data.frame(`weird column` = rnorm(5), `INL_Avg(S-B0-ETC-CDS-06C~PM_CD1_D_B0_SI_P0V_B.NM)` = rnorm(5), check.names = FALSE)
scale01(weird_name_df)
# weird column INL_Avg(S-B0-ETC-CDS-06C~PM_CD1_D_B0_SI_P0V_B.NM)
# 1 0.6135744 0.2237905
# 2 0.0000000 0.4086837
# 3 1.0000000 1.0000000
# 4 0.7061441 0.2803262
# 5 0.7693184 0.0000000
如果要转换数据框的所有数字列,我建议:
## base version
numeric_cols = sapply(your_data, is.numeric)
your_data[numeric_cols] = scale01(your_data[numeric_cols])
## dplyr version
library(dplyr)
your_data %>%
mutate(across(where(is.numeric), scale01))