【问题标题】:Find variable combinations that makes Primary Key in R查找在 R 中构成主键的变量组合
【发布时间】:2019-04-06 02:34:19
【问题描述】:

这是我的玩具数据框。

df <- tibble::tribble(
  ~var1, ~var2, ~var3, ~var4, ~var5, ~var6, ~var7,
    "A",   "C",    1L,    5L,  "AA",  "AB",    1L,
    "A",   "C",    2L,    5L,  "BB",  "AC",    2L,
    "A",   "D",    1L,    7L,  "AA",  "BC",    2L,
    "A",   "D",    2L,    3L,  "BB",  "CC",    1L,
    "B",   "C",    1L,    8L,  "AA",  "AB",    1L,
    "B",   "C",    2L,    6L,  "BB",  "AC",    2L,
    "B",   "D",    1L,    9L,  "AA",  "BC",    2L,
    "B",   "D",    2L,    6L,  "BB",  "CC",    1L)

如何获得能够唯一标识数据框中的观察结果的最少变量组合,即哪些变量一起可以构成主键

我解决这个问题的方法是找到不同值等于数据框观察次数的变量组合。因此,在这种情况下,这些变量组合会给我 8 个观察结果。我随机试了一下,发现很少:

df %>% distinct(var1, var2, var3)

df %>% distinct(var1, var2, var5)

df %>% distinct(var1, var3, var7)

所以 vars123, vars125, vars137 配得上这里的主键。如何使用 R 以编程方式找到这些变量组合。此外,如果可能的话,应该更优先考虑字符、因子、日期和(也许)整数变量,因为双打不应该成为主键。

输出可以是列表或数据框,说明组合“var1、var2、var3”、“var1、var2、var5”、“var1、var3、var7”。

【问题讨论】:

    标签: r data.table tidyverse


    【解决方案1】:

    可能有更好的方法,但这里有一个蛮力方法

    combs <- lapply(seq(ncol(df)), function(x) combn(names(df), x, simplify = F))
    
    keys <- list()
    for(i in seq_along(combs)){
      keys[[i]] <- combs[[i]][sapply(combs[[i]], function(x) nrow(distinct(df[x])) == nrow(df))]
      if(length(keys[[i]])) stop(paste('Found key of', i, 'columns, stopping'))
    }
    
    
    keys
    
    # [[1]]
    # list()
    # 
    # [[2]]
    # [[2]][[1]]
    # [1] "var1" "var6"
    # 
    # [[2]][[2]]
    # [1] "var4" "var6"
    # 
    # [[2]][[3]]
    # [1] "var4" "var7"
    

    【讨论】:

    • 谢谢!我们可以尝试使用 purrr,将所有名称组合输入 select() %>% n_distinct() 并将输出放在数据框中,以便一列包含变量名称的所有可能组合,另一列包含 n_distinct 值。我们可能需要 rlang 的帮助,在这里。
    【解决方案2】:

    可能的方法:

    library(dplyr)
    
    lst <- c()
    
    for (i in 2:ncol(df)) {
    
      lst_combinations <- combn(names(df), i ,simplify=FALSE)
    
      lst <- c(lst, lst_combinations)
    
    }
    
    lst_results <- c()
    
    for (i in 1:length(lst)) {
    
      nms <- lst[i][[1]]
    
      lgth_df <- df %>% .[, colnames(.) %in% nms] %>% distinct() %>% count()
    
      if (lgth_df$n == nrow(df)) {
    
        nms <- paste(nms, collapse = ", ")
    
        lst_results <- c(lst_results, nms)
    
      }
    
    }
    

    前几个组合(在您的示例中总共找到 80 个):

    [1] "var1, var6"                              
     [2] "var4, var6"                              
     [3] "var4, var7"                              
     [4] "var1, var2, var3"                        
     [5] "var1, var2, var5"                        
     [6] "var1, var2, var6"                        
     [7] "var1, var2, var7"                        
     [8] "var1, var3, var6"                        
     [9] "var1, var3, var7"                        
    [10] "var1, var4, var6"
    

    【讨论】:

    • 谢谢!我们可以尝试使用 purrr,将所有名称组合输入 select() %>% n_distinct() 并将输出放在数据框中,以便一列包含变量名称的所有可能组合,另一列包含 n_distinct 值。我们可能需要 rlang 的帮助,在这里。
    【解决方案3】:

    这是一种枚举所有可能变量组合的蛮力方法。似乎有 80 种可能的组合符合您的条件。

    >df
      var1 var2 var3 var4 var5 var6 var7
    1    A    C    1    5   AA   AB    1
    2    A    C    2    5   BB   AC    2
    3    A    D    1    7   AA   BC    2
    4    A    D    2    3   BB   CC    1
    5    B    C    1    8   AA   AB    1
    6    B    C    2    6   BB   AC    2
    7    B    D    1    9   AA   BC    2
    8    B    D    2    6   BB   CC    1
    
    >n<-ncol(df)
    >combinations<-unlist(lapply(1:n, function(x) unlist(apply(combn(n,x),2,list), recursive=F) ), recursive=F)
    >length(combinations)
    [1] 127
    >count_distinct<-sapply(combinations, function(x){ nrow(unique(df[,x,drop=F])) } )
    >length(which(count_distinct==8))
    [1] 80
    >combinations[which(count_distinct==8)]
    [[1]]
    [1] 1 6
    
    [[2]]
    [1] 4 6
    
    [[3]]
    [1] 4 7
    
    [[4]]
    [1] 1 2 3
    
    [[5]]
    [1] 1 2 5
    
    [[6]]
    [1] 1 2 6
    
    [[7]]
    [1] 1 2 7
    
    [[8]]
    [1] 1 3 6
    
    [[9]]
    [1] 1 3 7
    
    ...
    

    【讨论】:

    • 我们可以用变量名代替它们的数字吗?
    【解决方案4】:

    其他答案略有不同,但这里是请求的表格输出:

    nms <- unlist(lapply(seq_len(length(df)), combn, x=names(df), simplify=FALSE), rec=FALSE)
    out <- data.frame(
      vars = vapply(nms, paste, collapse=",", FUN.VALUE=character(1)),
      counts = vapply(nms, function(x) nrow(unique(df[x])), FUN.VALUE=numeric(1))
    )
    

    然后取最少的变量作为主键:

    out[match(nrow(df), out$counts),]
    #        vars counts
    #12 var1,var6      8
    

    【讨论】:

    • 这是一个非常优雅的解决方案!
    • 按主数据框中的行数过滤计数,然后按使用的最小变量数,应该会获取主键的更好候选者之一。所以...建议进行小修改。
    • 让我们在输出中保留 var4,var6 和 var4,var6,因为它们都提供了作为主键所需的最少变量数。
    【解决方案5】:

    完全借鉴 thelatemail 的答案并将其转换为 purrr:

    library(tidyverse)
    
    m_in_comb <- seq_len(length(df))
    
    var_combs_listoflist <- map(m_in_comb, ~combn(x=names(df), m = .x, simplify=F)) %>% 
      unlist(recursive = F)
    
    var_combs_listofchr  <-  map_chr(var_combs_listoflist, ~paste(.x, collapse = ","))
    
    distinct_obs_per_var_comb = map_int(var_combs_listoflist, ~(select(df, .x) %>% n_distinct()))
    
    keys <- tibble(var_combs = var_combs_listofchr, distinct_count = distinct_obs_per_var_comb)
    
    primarykeys <- keys %>% 
       filter(distinct_count==nrow(df)) %>% 
       mutate(n_vars = str_count(var_combs, ",")+1) %>% 
       filter(n_vars==min(n_vars))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-05
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多