【问题标题】:Removal of constant columns in R删除 R 中的常量列
【发布时间】:2013-02-10 17:10:11
【问题描述】:

收到此错误时我正在使用 prcomp 函数

Error in prcomp.default(x, ...) : 
cannot rescale a constant/zero column to unit variance

我知道我可以手动扫描我的数据,但是 R 中是否有任何函数或命令可以帮助我删除这些常量变量? 我知道这是一项非常简单的任务,但我从未遇到过任何执行此操作的函数。

谢谢,

【问题讨论】:

  • 请阅读发布指南,并提供一个小的、可重复的样本x。现在我们甚至不知道您的x 是否是数字,更不用说矩阵了。现在,如果它是一个矩阵,y <- x[,sd(x)!=0] 就足够了。
  • 如果您对数据使用 prcomp,则可能没有必要,但如果您确实有混合列类型,一个简单的解决方案是 x[,apply(x, 2, function(col) { length(unique(col)) > 1 })]

标签: r constants


【解决方案1】:

由于这个问答是一个流行的 Google 搜索结果,但对于大型矩阵来说答案有点慢,而且@raymkchow 版本在 NA 中速度很慢,我建议使用指数搜索和data.table 幂的新版本。

这是我在dataPreparation 包中实现的功能。

首先构建一个示例 data.table,行多于列(通常是这种情况)和 10% 的 NA

ncol = 1000
nrow = 100000
df <- matrix(sample(1:(ncol*nrow),ncol*nrow,replace = FALSE), ncol = ncol)
df <- apply (df, 2, function(x) {x[sample( c(1:nrow), floor(nrow/10))] <- NA; x} ) # Add 10% of NAs
df[,sample(1:ncol,70,replace = FALSE)] <- rep(1,times = nrow) # df is a large matrix
df <- as.data.table(df)

然后对所有方法进行基准测试:

time1 <- system.time(df1 <- df[,apply(df, 2, var, na.rm=TRUE) != 0, with = F]) # the first method
time2 <- system.time(df2 <- df[,!apply(df, MARGIN = 2, function(x) max(x, na.rm = TRUE) == min(x, na.rm = TRUE)), with = F]) # raymkchow
time3 <- system.time(df3 <- df[,apply(df, 2, function(col) { length(unique(col)) > 1 }), with = F]) # Keith's method
time4 <- system.time(df4 <- df[,-which_are_constant(df, verbose=FALSE)]) # My method

结果如下:

time1 # Variance approch
#   user  system elapsed 
#   2.55    1.45    4.07
time2 # Min = max approach
#   user  system elapsed 
#  2.72      1.5    4.22
time3 # length(unique()) approach
#   user  system elapsed 
#    6.7    2.75    9.53
time4 # Exponential search approach
#   user  system elapsed 
#   0.39    0.07    0.45
all.equal(df1, df2)
# [1] TRUE
all.equal(df3, df2)
# [1] TRUE
all.equal(df4, df2)
# [1] TRUE

dataPreparation:which_are_constant 比其他方法快 10 倍。

加上行数越多,使用起来就越有趣。

【讨论】:

    【解决方案2】:

    如果您正在寻求一个在 df 中返回非常量变量的 dplyr 解决方案,我建议您使用以下方法。或者,如果需要列名,您可以添加 %&gt;% colnames()

    library(dplyr)
    df <- data.frame(x = 1:5, y = rep(1,5))
    # returns dataframe
    var_df <- df %>%
      select_if(function(v) var(v, na.rm=TRUE) != 0)  
    var_df %>% colnames() # returns column names
    

    【讨论】:

    • dplyr::select_if 现已被取代。
    【解决方案3】:

    我猜这个问答是一个流行的谷歌搜索结果,但对于大型矩阵来说答案有点慢,而且我没有足够的声誉来评论第一个答案。因此,我发布了该问题的新答案。

    对于一个大矩阵的每一列,检查最大值是否等于最小值就足够了。

    df[,!apply(df, MARGIN = 2, function(x) max(x, na.rm = TRUE) == min(x, na.rm = TRUE))]
    

    这是测试。与第一个答案相比,减少了 90% 以上的时间。它也比对问题的第二条评论的答案更快。

    ncol = 1000000
    nrow = 10
    df <- matrix(sample(1:(ncol*nrow),ncol*nrow,replace = FALSE), ncol = ncol)
    df[,sample(1:ncol,70,replace = FALSE)] <- rep(1,times = nrow) # df is a large matrix
    
    time1 <- system.time(df1 <- df[,apply(df, 2, var, na.rm=TRUE) != 0]) # the first method
    time2 <- system.time(df2 <- df[,!apply(df, MARGIN = 2, function(x) max(x, na.rm = TRUE) == min(x, na.rm = TRUE))]) # my method
    time3 <- system.time(df3 <- df[,apply(df, 2, function(col) { length(unique(col)) > 1 })]) # Keith's method
    
    time1
    #   user  system elapsed 
    # 22.267   0.194  22.626 
    time2
    #   user  system elapsed 
    #  2.073   0.077   2.155 
    time3
    #   user  system elapsed 
    #  6.702   0.060   6.790
    all.equal(df1, df2)
    # [1] TRUE
    all.equal(df3, df2)
    # [1] TRUE
    

    【讨论】:

    • 我重新运行,发现使用 all(x==x[1], na.rm=TRUE) 比计算最大值和最小值快 15%。
    • Position(function(x) !is.na(x), x) 给出第一个非 na 元素的索引位置,如果 x 有一些 na 值,这会花费更多时间。
    【解决方案4】:

    这里的问题是您的列方差等于零。您可以通过这种方式检查数据框的哪一列是常量,例如:

    df <- data.frame(x=1:5, y=rep(1,5))
    df
    #   x y
    # 1 1 1
    # 2 2 1
    # 3 3 1
    # 4 4 1
    # 5 5 1
    
    # Supply names of columns that have 0 variance
    names(df[, sapply(df, function(v) var(v, na.rm=TRUE)==0)])
    # [1] "y" 
    

    所以如果你想排除这些列,你可以使用:

    df[,sapply(df, function(v) var(v, na.rm=TRUE)!=0)]
    

    编辑: 实际上使用apply 更简单。像这样:

    df[,apply(df, 2, var, na.rm=TRUE) != 0]
    

    【讨论】:

    • 这比我在上面评论中的小解决方案更快(或更健壮)吗? -- 除此之外,我正在使用sd 的官方弃用操作:-)
    • @CarlWitthoft 好吧,既然你使用sd(x)时的建议是使用apply(x, 2, sd),我认为如果你按照建议的话,它是一样的:)
    • df[,sapply(df, function(v) var(v, na.rm=TRUE)!=0)] 对我不起作用:“选择了未定义的列”错误
    • 给我列内容但没有列名!
    猜你喜欢
    • 1970-01-01
    • 2012-08-11
    • 2022-01-02
    • 2016-12-18
    • 2018-11-07
    • 2020-02-04
    • 1970-01-01
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多