【问题标题】:Is there a way to remove almost duplicated columns in R有没有办法删除 R 中几乎重复的列
【发布时间】:2021-02-04 23:58:17
【问题描述】:

dplyr 中是否有删除几乎重复的列的方法?例如,我想删除以下 tibble 中重复率大于 75% 的列。这将删除 b 列和 c 列,但不会删除 d 列,因为它与任何其他列只有 60% 相同。

tibble(
       a = c(1,2,3,4,5),
       b = c(1,2,3,4,5),
       c = c(1,2,3,4,4),
       d = c(1,2,3,6,6)
)

【问题讨论】:

  • 您应该澄清“几乎重复”的概念。当两列相似时,应该删除哪一列?
  • 好问题。我希望它删除其中一个冗余列。几乎重复我的意思是跨行的大部分等效案例。例如,[3,3] 等价于 [4,3],但 [4,3] 不等价于 [4,4]。如果这是所有数据,则匹配率为 50%。我基本上想指定 > n% 匹配应该被识别,以便可以删除其中一列。

标签: r dplyr duplicates


【解决方案1】:

老实说,我认为最好不要在dplyr 中这样做。其中许多功能可以用purrr/tidyr-variants 替换,但我认为dplyr 中没有这样简单/高效的方法。

Z <- tibble(
       a = c(1,2,3,4,5),
       b = c(1,2,3,4,5),
       c = c(1,2,3,4,4),
       d = c(1,2,3,6,6)
)
eg <- expand.grid(seq_len(ncol(Z)), seq_len(ncol(Z)))
eg <- eg[ eg$Var1 < eg$Var2, ]
eg
#    Var1 Var2
# 5     1    2
# 9     1    3
# 10    2    3
# 13    1    4
# 14    2    4
# 15    3    4
eg$similarity <- mapply(function(i,j) sum(Z[,i] == Z[,j]) / nrow(Z), eg$Var1, eg$Var2)
eg
#    Var1 Var2 similarity
# 5     1    2        1.0
# 9     1    3        0.8
# 10    2    3        0.8
# 13    1    4        0.6
# 14    2    4        0.6
# 15    3    4        0.6
unique(eg$Var2[ eg$similarity >= 0.75 ])
# [1] 2 3
Z[, -unique(eg$Var2[ eg$similarity >= 0.75 ]) ]
# # A tibble: 5 x 2
#       a     d
#   <dbl> <dbl>
# 1     1     1
# 2     2     2
# 3     3     3
# 4     4     6
# 5     5     6

【讨论】:

    【解决方案2】:

    这是一种计算不同列彼此相似程度的方法。然后,您可以检查它并决定丢弃哪些列。

    num_cols = ncol(my_data)
    
    similarity_matrix = matrix(nrow = num_cols, ncol = num_cols)
    
    for(ii in 1:num_cols){
      col1_name = colnames(my_data)[ii]
      for(jj in 1:num_cols){
          col2_name = colnames(my_data)[jj]
    
        calc = my_data %>%
            ungroup() %>%
            mutate(diff = ifelse(!!sym(col1_name) == !!sym(col2_name), 1, 0)) %>%
            summarise(num_diffs = sum(diff))
    
        similarity_matrix[ii,jj] = calc / num_cols
      }
    }
    

    那么similarity_matrixii,jj-th条目会告诉你ii-th和jj-th列中相同条目的比例。

    请注意,!!sym(col1_name) 是 dplyr 获取包含列名称的文本并在代码中将其用作列名称的方式:

    select(df, my_favourite_column)
    

    相当于:

    column_text = "my_favourite_column"
    select(df, !!sym(column_text))
    

    【讨论】:

    • 感谢您的建议。我应该用我包含的小标题替换语法中除 my_data 之外的任何内容吗?当我尝试使用上面的 tibble 代替 my_data 时,它给了我错误...数值表达式有 4 个元素:只有第一个 usedError 在 1:foo : NA/NaN 参数。如果我做错了什么,我深表歉意。
    【解决方案3】:

    此功能相当有效且可操作。该函数返回要删除的列的逻辑向量。它要求所有列都是数字。

    因为“相同”是一个松散的术语,所以我使用了stringdist 包,它具有比较字符串和数字向量的相似性的功能。您可以自定义使用哪种算法,具体取决于您的目标。

    示例

    # Which columns are similar with a cutoff of 0.7?
    similar_columns(df, 0.7)
    ## [1]  TRUE  TRUE  TRUE FALSE
    
    # Remove only exactly identical columns
    df[, !similar_columns(df, 1)]
    
    #       c     d
    #   <dbl> <dbl>
    # 1     1     1
    # 2     2     2
    # 3     3     3
    # 4     4     6
    # 5     4     6
    

    功能

    # Get stringdist package if you don't already have it
    if (!require(stringdist)) {
        install.packages('stringdist')
        library(stringdist)
    }
    
    # --------------
    
    similar_columns <- function(df, cutoff) {
    
      # function for determining similarity of columns
      nearly_identical <- function(df, cola, colb, cutoff) {
        if (stringdist::seq_sim(list(df[[cola]]), list(df[[colb]])) >= cutoff) return(TRUE)
        else return(FALSE)
      }
      
      # generate a similarity matrix
      sim_matrix <- matrix(nrow = ncol(df), ncol = ncol(df))
      for (i in 1:ncol(df)) {
        for (j in 1:ncol(df)) {
          sim_matrix[i, j] = nearly_identical(df, names(df)[i], names(df)[j], cutoff)
        }
      }
      
      # Vector of columns to drop
      col_drop <- vector(length = ncol(df))
      for (i in seq_len(ncol(df))) {
        if (sum(sim_matrix[i, ], na.rm = TRUE) > 1) col_drop[i] <- TRUE
        
        else col_drop[i] <- FALSE
      }
      return(col_drop)
    }
    

    截止值会根据您使用的算法而变化,默认值为 0 到 1 比例(1 表示完全相同)。在截止值为 0.7 的情况下,应删除列 abc,因为它们至少有一个几乎相同的列。

    【讨论】:

      猜你喜欢
      • 2019-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-01
      • 1970-01-01
      相关资源
      最近更新 更多