在数据帧中查找/创建候选键

时间:2016-02-04 19:50:54

标签: r key

我正在寻找一种方法来确定data.frame中的候选键。

举个简单的例子,如果我有data.frame

df <- data.frame(Col1 = c("A", "A", "B", "B", "C", "C"), Col2 = c(1, 2, 1, 2, 1, 2))

然后显然Col1或Col2不是单独唯一标识每一行的键,但Col1+Col2的串联将是。

通过比较length(unique(df$column)) == nrow(df)可以找到可以作为密钥的单个列。

但是如果我的data.frame包含许多列而没有单个列是关键字,那么可能会将两列连接在一起。

问题是,我怎样才能找出哪两个会起作用呢?哪三个?我意识到这可能是一个指数级增长的详尽搜索,但我想知道是否有更好的方法。

我编写代码至少搜索所有可能的2列组合,但这非常麻烦。

1 个答案:

答案 0 :(得分:1)

虽然我并不完全清楚以这种方式寻找钥匙的动机是什么,但我认为确定哪些特征组合可以唯一地识别人口中的个体是有趣的。

正如您所指出的,详尽的搜索可能非常昂贵,因为您需要检查2^kk个变量的子集。不过,它很容易编码并为运行时提供基准:

all.keys <- function(dat) {
  combos <- tail(expand.grid(sapply(dat, function(x) c(F, T), simplify=FALSE)), -1)
  nunique <- unlist(apply(combos, 1, function(x) nrow(unique(dat[,x,drop=FALSE]))))
  combos[nunique == nrow(dat),]
}

对于11列mtcars数据集,这将在大约半秒内运行,并返回可用作键的1,276种不同的列组合;没有单个列可以用作密钥,但可以使用9对列。

dim(all.keys(mtcars))
# [1] 1276   11
head(all.keys(mtcars))
#     mpg   cyl  disp    hp  drat   wt  qsec    vs    am  gear  carb
# 34 TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# 36 TRUE  TRUE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# 38 TRUE FALSE  TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# 40 TRUE  TRUE  TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# 42 TRUE FALSE FALSE  TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# 44 TRUE  TRUE FALSE  TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
table(rowSums(all.keys(mtcars)))
#   2   3   4   5   6   7   8   9  10  11 
#   9  52 148 266 322 266 148  53  11   1 

对于具有多列的数据集,可能无法有效地计算所有可能的密钥,因为有效密钥的数量可能会以变量的数量呈指数级增长。我们可能有效地找到最小的密钥大小(在这种情况下,大小为2的密钥)。一种直接的方法是循环键大小,并在找到该大小的有效密钥后停止:

small.keys <- function(dat) {
  for (size in 1:ncol(dat)) {
    keys <- combn(names(dat), size)
    nunique <- apply(keys, 2, function(x) nrow(unique(dat[,x,drop=FALSE])))
    if (sum(nunique == nrow(dat)) > 0) {
      return(t(keys[,nunique == nrow(dat)]))
    }
  }
  return(NULL)
}

这在我的计算机上运行不到10毫秒(比mtcars的详尽方法快50倍)并返回大小为2的9个键:

small.keys(mtcars)
#       [,1]   [,2]  
#  [1,] "mpg"  "wt"  
#  [2,] "mpg"  "qsec"
#  [3,] "cyl"  "qsec"
#  [4,] "disp" "qsec"
#  [5,] "hp"   "qsec"
#  [6,] "drat" "qsec"
#  [7,] "wt"   "qsec"
#  [8,] "qsec" "am"  
#  [9,] "qsec" "carb"

当然,如果唯一有效的密钥很大或者没有有效密钥,这仍然会表现不佳,因为在这种情况下我们仍需要详尽检查所有变量子集。