如何构造用于创建虚拟变量的函数?

How to construct a function for creating dummy variables?

我有一个数据框,它提供以下输出来创建虚拟变量。

library(dummies)
df1 <- data.frame(id = 1:4, year = 1991:1994)
df1 <- cbind(df1, dummy(df1$year, sep = "_"))
df1
#    id year df1_1991 df1_1992 df1_1993 df1_1994
#1  1 1991        1        0        0        0
#2  2 1992        0        1        0        0
#3  3 1993        0        0        1        0
#4  4 1994        0        0        0        1

我必须尝试创建一个函数式编程来实现相同的目的。

dummy_df <- function(dframe, x){
    dframe <- cbind(dframe, dummy(dframe$x, sep = "_"))
    return(dframe)
}

然而,当我 运行 输出时,出现以下错误。

dummy_df(df1, year)
#Error in `[[.default`(x, 1) : subscript out of bounds

如何纠正这个错误并创建一个用于创建虚拟变量的自动函数?此外,如果该函数提供是保留还是丢弃被分离以创建虚拟变量的初始列的选项,那就更好了。例如,在上述数据帧的情况下,保留或丢弃的选项应应用于列 year.

此问题是在观察到此处的类似问题后发布的。 Pass a data.frame column name to a function

问题在于,year不加引号传递时,是代表变量的符号,而不是字符串,变量名。获取字符串的标准技巧是使用 deparse(substitute(.))。然后提取器 [[ 工作。

dummy_df <- function(dframe, x){
    x <- deparse(substitute(x))
    dframe <- cbind(dframe, dummy(dframe[[x]], sep = "_"))
    return(dframe)
}

dummy_df(df1, year)
#  id year df1_1991 df1_1992 df1_1993 df1_1994
#1  1 1991        1        0        0        0
#2  2 1992        0        1        0        0
#3  3 1993        0        0        1        0
#4  4 1994        0        0        0        1
#Warning message:
#In model.matrix.default(~x - 1, model.frame(~x - 1), contrasts = FALSE) :
#  non-list contrasts argument ignored

如果x列可以被引用,把上面的函数改成as.character(substitute(.))。该函数将接受带引号和不带引号的 x.

dummy_df <- function(dframe, x){
    x <- as.character(substitute(x))
    dframe <- cbind(dframe, dummy(dframe[[x]], sep = "_"))
    return(dframe)
}

dummy_df(df1, year)
dummy_df(df1, "year")

编辑

之后,保留或删除列 x 可以通过额外的函数参数 keep 来解决,默认为 TRUE.

dummy_df <- function(dframe, x, keep = TRUE){
    x <- as.character(substitute(x))
    if(keep){
        dftmp <- dframe
    } else {
        i <- grep(x, names(dframe))
        if(length(i) == 0) stop(paste(sQuote(x), "is not a valid column"))
        dftmp <- dframe[-i]
    }
    dframe <- cbind(dftmp, dummy(dframe[[x]], sep = "_"))
    return(dframe)
}

dummy_df(df1, year)
dummy_df(df1, "year")

dummy_df(df1, year, keep = FALSE)
dummy_df(df1, month, keep = FALSE)