【问题标题】:Generate new unique ID numbers while excluding previously generated ID numbers in R生成新的唯一 ID 号,同时排除 R 中先前生成的 ID 号
【发布时间】:2020-09-29 20:21:39
【问题描述】:

我想为我的数据库中的行生成唯一 ID。我将持续向该数据库添加条目,因此我需要同时生成新的 ID。虽然我的数据库相对较小,而且复制随机 ID 的可能性很小,但我仍然想构建一个程序化的故障安全机制,以确保我永远不会生成过去已经使用过的 ID。

对于初学者,这里有一些我可以用来启动示例数据库的示例数据:

library(tidyverse)
library(ids)
library(babynames)
    
database <- data.frame(rid = random_id(5, 5), first_name = sample(babynames$name, 5))

print(database)
          rid first_name
1  07282b1da2      Sarit
2  3c2afbb0c3        Aly
3  f1414cd5bf    Maedean
4  9a311a145e    Teriana
5  688557399a    Dreyton

这里有一些示例数据,我可以使用它们来表示将附加到现有数据库中的新数据:

new_data <- sample(babynames$name, 5)

print(new_data)

 first_name
1    Hamzeh
2   Mahmoud
3   Matelyn
4    Camila
5     Renae

现在,我想要的是使用 random_id 函数绑定随机生成的新 ID 列,同时检查以确保新生成的 ID 不匹配 database 对象中的任何现有 ID。如果生成器创建了一个相同的 ID,那么理想情况下它会生成一个新的替换,直到创建一个真正唯一的 ID。

任何帮助将不胜感激!

更新

我想到了一种可能会有所帮助但仍然有限的可能性。我可以生成新的 ID,然后使用for() 循环来测试现有数据库中是否存在任何新生成的 ID。如果是这样,那么我会重新生成一个新的 ID。比如……

new_data$rid <- random_id(nrow(new_data), 5)

for(i in 1:nrow(new_data)){
  if(new_data$rid[i] %in% unique(database$rid)){
    new_data$rid[id] = random_id(1, 5)
  }
}

这种方法的问题是我需要构建无穷无尽的嵌套if 语句流,以再次针对原始数据库持续测试新生成的值。我需要一个流程来继续测试,直到生成原始数据库中没有的真正唯一值。

【问题讨论】:

  • 也许您正在寻找ids::uuid() 函数。 cran.r-project.org/web/packages/ids/vignettes/ids.html
  • @manotheshark,我看不出ids::uuid() 函数将如何解决我的问题。它只是一个用于生成不同类型 ID 的函数,它不一定会根据预先存在的 ID 向量检查新生成的 ID。

标签: r dplyr uniqueidentifier unique-key ids


【解决方案1】:

使用ids::uuid() 可能会避免检查重复的 id 值。事实上,如果你要生成 10 万亿个 uuid,那么每个What is a UUID?

这是一个无需进行任何迭代即可快速检查重复值的基本函数:

anyDuplicated(1:4)
[1] 0

anyDuplicated(c(1:4,1))
[1] 5

上面的第一个结果显示没有重复值。第二个显示元素 5 是重复的,因为 1 被使用了两次。下面是如何在不迭代的情况下进行检查,new_data 复制了database$rid,因此所有五个都是重复的。这将重复,直到所有 rid 都是唯一的,但请注意,它假定所有现有的 database$rid 都是唯一的。

library(ids)
set.seed(7)
new_data$rid <- database$rid
repeat {
  duplicates <- anyDuplicated(c(database$rid, new_data$rid))
  if (duplicates == 0L) {
    break
  }
  new_data$rid[duplicates - nrow(database)] <- random_id(1, 5)
}

所有new_data$rid 都已替换为唯一值。

rbind(database, new_data)

          rid first_name
1  07282b1da2      Sarit
2  3c2afbb0c3        Aly
3  f1414cd5bf    Maedean
4  9a311a145e    Teriana
5  688557399a    Dreyton
6  52f494c714     Hamzeh
7  ac4f522860    Mahmoud
8  ffe74d535b    Matelyn
9  e3dccc4a8e     Camila
10 e0839a0d34      Renae

【讨论】:

  • 虽然这很有趣,并为我的解决方案提供了一个解决方法,但它不一定能解决我提出的问题的初衷。您提供了一个减少冲突和替换重复的机会的机会。但是,重点是在排除旧 ID 的同时生成新 ID。您的流程仍然没有捕捉到被替换的副本也与之前生成的 ID 匹配的情况。
  • 我生成的 ID 会经常被其他人输入。所以,我必须妥协一点,减少唯一 ID 的长度,增加 ID 生成冲突的机会。这意味着我确实需要一个在时尚上有点迭代的过程,以根据先前生成的 ID 的旧向量验证新 ID,并创建新 ID,直到创建真正唯一的 ID。
  • @Craig 如果您要在有重复条目的情况下以编程方式替换它,为什么还要让用户创建一个 ID?第一段以外的所有内容都集中在您的问题上。最后一个示例被扩展为重复,直到new_data$rid中没有重复条目
  • 我从未说过用户会创建 ID,只是会经常输入它们。感谢您扩展您的答案;这正是我所需要的。
【解决方案2】:

这个答案的灵感来自 @manotheshark 的答案,有 2 个主要变化:

  1. 这是一个函数。
  2. 我更改了替换副本的机制。不是像@manotheshark 那样在每次迭代中循环和替换一个重复项,而是将它们替换为更大的块。
library(ids)

generate_random_unique_ids <- function(n) {
  vec_ids <- ids::random_id(n = n, bytes = 4, use_openssl = FALSE)
  repeat {
    duplicates <- duplicated(vec_ids)
    if (!any(duplicates)) {
      break
    }
    vec_ids[duplicates] <- ids::random_id(n = sum(duplicates), bytes = 4, use_openssl = FALSE)
  }
  vec_ids
}

例如一些时间

library(tictoc)

tic()
v_1e6 <- generate_random_unique_ids(1e6)
toc()
#> 7.14 sec elapsed

tic()
v_3e7 <- generate_random_unique_ids(3e7)
toc()
#> 296.42 sec elapsed

很想知道是否有办法优化此功能以获得更快的执行时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-09
    • 1970-01-01
    • 2015-09-25
    相关资源
    最近更新 更多