特定条件下的所有字母/数字组合

Posted

技术标签:

【中文标题】特定条件下的所有字母/数字组合【英文标题】:All combinations of letters/numbers under specific conditions 【发布时间】:2018-08-03 17:15:02 【问题描述】:

我创建了这些向量:

Letters <- c("A","C","E","G","H","J","K")  
Numbers <- c(0,1,2,3,4,6,7,9) 
AlphaNumeric <- c(Letters, Numbers)

我想使用上面提到的所有元素在以下三个条件下接收所有 3 元素组合(例如 AA1、G26 等)的数据框:

1.) 第一个元素是一个字母

2.) 第二个元素是一个数字或与第一个元素相同的字母

3.) 第三个元素是一个数字

方法: 我尝试使用expand.grid() 并成功地获得了包含 3 个元素的所有组合。然后我尝试了expand.grid(x = Letters, y = AlphaNumeric, z = Numbers) 并设法实现了 1.) 和 3.) 但到目前为止未能管理 2.)。

不满意的解决方案: 我已经想出了一个用for循环来做这件事的方法,但我想除了:

   LNN <- expand.grid(x = Letters, y = Numbers, z = Numbers)

   for ( Element in Letters) 
       currentLLN <- expand.grid(x = Element, y = Element, z = Numbers)
       LNN <- merge(LNN, currentLLN, all = TRUE)

任何帮助将不胜感激,谢谢你,克里斯蒂安

【问题讨论】:

我认为@Florian 的答案简单而优雅,绝对是您应该使用的,但我发表评论是因为我还想提及正则表达式。你对他们熟悉吗?它们是一种用于处理和搜索字符串模式的工具,如果您曾经处理过类似的问题,但发现无法详尽地生成您感兴趣的每个模式,它们可能会派上用场。 @HarlandMason:谢谢您的评论,我对它们不熟悉,不(有一段时间没有使用 R,只是刚刚开始使用它)。但我会看看他们,非常感谢! 【参考方案1】:

您可以创建两个数据框,一个是第二个元素是数字,一个是第二个元素与第一个元素相同,然后是 rbind 那些。下面给出了一个示例,请注意,出于说明目的,我对您的示例数据进行了限制。

Letters <- LETTERS[1:3]  
Numbers <- c(1,2)

df1 = expand.grid(v1=Letters,v3=Numbers,stringsAsFactors = F)
df1$v2 = df1$v1
df1 = df1[,c('v1','v2','v3')]
df2 = expand.grid(v1=Letters,v2=as.character(Numbers),v3=Numbers, stringsAsFactors = F)
df = rbind(df1,df2)

输出:

> df
   v1 v2 v3
1   A  A  1
2   B  B  1
3   C  C  1
4   A  A  2
5   B  B  2
6   C  C  2
7   A  1  1
8   B  1  1
9   C  1  1
10  A  2  1
11  B  2  1
12  C  2  1
13  A  1  2
14  B  1  2
15  C  1  2
16  A  2  2
17  B  2  2
18  C  2  2

希望这会有所帮助!


虽然两个答案都运行得非常快,而且 Parfait 的解决方案是解决您问题的好方法,我当然不想抹黑他的答案,但我认为最好指出创建额外的组合和子集化将成为一个更大的问题你的数据更大。下面显示了一个基准。

Letters <- c(LETTERS[1:26],letters[1:4])
Numbers <- seq(30)
AlphaNumeric <- c(Letters, Numbers)


f_flo <- function()

  df1 = expand.grid(v1=Letters,v3=Numbers,stringsAsFactors = F)
  df1$v2 = df1$v1
  df1 = df1[,c('v1','v2','v3')]
  df2 = expand.grid(v1=Letters,v2=as.character(Numbers),v3=Numbers, stringsAsFactors = F)
  df = rbind(df1,df2)


f_parfait <- function()

  df <- expand.grid(x = Letters, y = AlphaNumeric, z = Numbers, stringsAsFactors = FALSE)
  sub <- subset(df,  (x == y | grepl("[0-9]", y)) &  grepl("[0-9]", z) )
  sub <- with(sub, sub[order(x, y, z),])   # SORT DATAFRAME
  rownames(sub) <- NULL                    # RESET ROWNAMES


library(dplyr)
one_letter <- function(l) 
  expand.grid(l, c(l, Numbers), Numbers, stringsAsFactors = FALSE)


f_stibu <- function()
  df <- bind_rows(lapply(Letters, one_letter))



library(microbenchmark)
library(ggplot2)

run_times = microbenchmark(f_flo(),f_parfait(),f_stibu())
autoplot(run_times)

结果:

Unit: milliseconds
        expr        min         lq       mean     median         uq       max neval cld
     f_flo()   1.900719   2.047591   3.666935   2.314258   3.922053  78.74793   100  a 
 f_parfait() 138.028364 142.529904 152.876116 144.159444 146.835958 246.92318   100   b
   f_stibu()   4.130464   4.333130   5.169664   4.585028   6.209233  10.23139   100  a 

【讨论】:

非常感谢您的回答@Florian,我没有考虑过。我喜欢这种方法。 良好的基准。事实上,正则表达式总是有足迹。此外,运行您的示例,您的输出实际上会呈现 NA。 谢谢@Parfait,我试着取字母表的前 30 个大写字母。我想知道NA是从哪里来的,哈哈。我更新了基准。【参考方案2】:

使用grepl 调用简单地对您的expand.grid() 数据框进行子集化:

df <- expand.grid(x = Letters, y = AlphaNumeric, z = Numbers, stringsAsFactors = FALSE)

sub <- subset(df,  (x == y | grepl("[0-9]", y)) )

sub <- with(sub, sub[order(x, y, z),])   # SORT DATAFRAME
rownames(sub) <- NULL                    # RESET ROWNAMES

head(sub, 10)    
#    x y z
# 1  A 0 0
# 2  A 0 1
# 3  A 0 2
# 4  A 0 3
# 5  A 0 4
# 6  A 0 6
# 7  A 0 7
# 8  A 0 9
# 9  A 1 0

【讨论】:

哇,非常感谢@Parfait,我还没有达到 grepl() 的水平,我的功能知识的有用扩展,谢谢! 没问题!很高兴帮助 R 事业掌握其学习曲线。编码愉快! 我认为grepl("[0-9]", z) 不需要,因为z 列仅包含数字。【参考方案3】:

只有一个字母,问题很容易解决:第二列是那个字母或任何数字,第三列是一个数字:

one_letter <- function(l) 
  expand.grid(l, c(l, Numbers), Numbers, stringsAsFactors = FALSE)

然后,您只需将该函数应用于每个字母,并将生成的数据框合并为一个:

library(dplyr)
df <- bind_rows(lapply(Letters, one_letter))
head(df)
##   Var1 Var2 Var3
## 1    A    A    0
## 2    A    0    0
## 3    A    1    0
## 4    A    2    0
## 5    A    3    0
## 6    A    4    0

之所以使用包dplyr,是因为它提供了函数bind_rows(),将数据帧列表组合成一个数据帧。

【讨论】:

【参考方案4】:

只使用前 3 个字母和前 2 个数字。那么你会得到如下结果:

> Numbers=c(0,1)
> Letters=c("A","C")
> A=outer(Letters,outer(Numbers,Numbers,paste0),paste0)
> B=outer(paste0(Letters,Letters),Numbers,paste0)
> sort(c(A,B))
 [1] "A00" "A01" "A10" "A11" "AA0" "AA1" "C00" "C01" "C10" "C11" "CC0" "CC1" "E00" "E01" "E10"
[16] "E11" "EE0" "EE1"

【讨论】:

以上是关于特定条件下的所有字母/数字组合的主要内容,如果未能解决你的问题,请参考以下文章

每天AC系列:电话号码的字母组合

java字母和数字排列组合后

满足特定条件的列表的所有组合

8-16位密码,数字、大小写字母组合、符号至少包含两种,是啥意思?

对数字列表进行所有可能的操作组合以查找特定数字

LeetCode第十七题-电话号码的字母组合