Найдите комбинации переменных, которые делают первичный ключ в R

Вот мой игрушечный фреймворк.

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».

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
0
1 060
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Может быть способ получше, но вот метод грубой силы

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.

Geet 01.11.2018 23:03

Возможный подход:

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.

Geet 01.11.2018 23:02

Вот метод грубой силы, который перечисляет все возможные комбинации переменных. Кажется, есть 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

...

Можно ли использовать имена переменных вместо их номеров?

Geet 01.11.2018 23:07
Ответ принят как подходящий

Небольшая вариация других ответов, но вот запрошенный табличный вывод:

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

Очень элегантное решение!

Geet 01.11.2018 23:46

Фильтрация счетчика по количеству строк в основном фрейме данных, а затем по минимальному количеству используемых переменных должна выбрать одного из лучших кандидатов для первичного ключа. Итак ... предлагаю небольшое изменение.

Geet 02.11.2018 00:08

Давайте оставим var4, var6 и var4, var6 также в выходных данных, поскольку оба обеспечивают наименьшее количество переменных, необходимых для использования в качестве первичного ключа.

Geet 02.11.2018 00:36

Заимствуя полностью из ответа 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))

Другие вопросы по теме