R代码:快速查找模式及其在字符串向量中的重复

R代码:快速查找模式及其在字符串向量中的重复,r,algorithm,R,Algorithm,我有一个数字向量,我想把它们作为字符来分析。对于每一组数字,即3412123401234,我想知道模式是什么,优先考虑模式长度超过1时最常出现的模式。在3412123401234的例子中,我希望它将34识别为模式,将引用识别为3 我有一个非常低效的算法,它需要永远循环一个100000多个数字集的列表。需要一种更有效的方法以矢量方式处理它 repeats = function(string){ out = c() check.till = floor(nchar(string)/2)

我有一个数字向量,我想把它们作为字符来分析。对于每一组数字,即3412123401234,我想知道模式是什么,优先考虑模式长度超过1时最常出现的模式。在3412123401234的例子中,我希望它将34识别为模式,将引用识别为3

我有一个非常低效的算法,它需要永远循环一个100000多个数字集的列表。需要一种更有效的方法以矢量方式处理它

repeats = function(string){
  out = c()
  check.till = floor(nchar(string)/2)
  for (start in 1:nchar(string)){
    for (end in 1:check.till){
      pat = substr(string,start,start+end)
      repeats = length(gregexpr(pat,string)[[1]])
      length = nchar(pat)
      if (length >= 2) out = rbind(out, c(pat, repeats, length))
    }
  }
  out = as.data.frame(out, stringsAsFactors = F)
  colnames(out) = c("Pattern","Repeats","Pat.Length")
  out$Repeats = as.integer(out$Repeats)
  out$Pat.Length = as.integer(out$Pat.Length)
  out = out[out$Repeats > 1,]
  out = out[order(out[,2],out[,3],decreasing = T),]
  out = out[1,]
  out[is.na(out)] = 0
  return(out)
}

我不完全确定这是否是一个解决方案,但我尝试的是将向量拆分为所有唯一的组合

我的想法是,所有组合都包含在对角矩阵的上半部分:

d <- c("3412123401234")
# create function
make.matrix <- function(x,y) {
 vl <- nchar(d)
 OUT <- seq(x,nchar(y),1)
 if(length(OUT) < vl) OUT <- c(OUT,rep(NA,vl-length(OUT)))
 OUT
}
mm <- sapply(1:nchar(d), make.matrix, d)  
mm

    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
tmp    1    2    3    4    5    6    7    8    9    10    11    12    13
tmp    2    3    4    5    6    7    8    9   10    11    12    13    NA
tmp    3    4    5    6    7    8    9   10   11    12    13    NA    NA
tmp    4    5    6    7    8    9   10   11   12    13    NA    NA    NA
tmp    5    6    7    8    9   10   11   12   13    NA    NA    NA    NA
tmp    6    7    8    9   10   11   12   13   NA    NA    NA    NA    NA
tmp    7    8    9   10   11   12   13   NA   NA    NA    NA    NA    NA
tmp    8    9   10   11   12   13   NA   NA   NA    NA    NA    NA    NA
tmp    9   10   11   12   13   NA   NA   NA   NA    NA    NA    NA    NA
tmp   10   11   12   13   NA   NA   NA   NA   NA    NA    NA    NA    NA
tmp   11   12   13   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA
tmp   12   13   NA   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA
tmp   13   NA   NA   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA
最后是唯一模式的计数:

counts <- sort(table(mm2),decreasing = T)
您想要的输出:

res <- data.frame("nlength"=sapply(names(counts),nchar),"counts"=counts)
head(res)
     nlength counts
12         2     3
34         2     3
123        3     2
1234       4     2
23         2     2
234        3     2

我不完全确定这是否是一个解决方案,但我尝试的是将向量拆分为所有唯一的组合

我的想法是,所有组合都包含在对角矩阵的上半部分:

d <- c("3412123401234")
# create function
make.matrix <- function(x,y) {
 vl <- nchar(d)
 OUT <- seq(x,nchar(y),1)
 if(length(OUT) < vl) OUT <- c(OUT,rep(NA,vl-length(OUT)))
 OUT
}
mm <- sapply(1:nchar(d), make.matrix, d)  
mm

    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
tmp    1    2    3    4    5    6    7    8    9    10    11    12    13
tmp    2    3    4    5    6    7    8    9   10    11    12    13    NA
tmp    3    4    5    6    7    8    9   10   11    12    13    NA    NA
tmp    4    5    6    7    8    9   10   11   12    13    NA    NA    NA
tmp    5    6    7    8    9   10   11   12   13    NA    NA    NA    NA
tmp    6    7    8    9   10   11   12   13   NA    NA    NA    NA    NA
tmp    7    8    9   10   11   12   13   NA   NA    NA    NA    NA    NA
tmp    8    9   10   11   12   13   NA   NA   NA    NA    NA    NA    NA
tmp    9   10   11   12   13   NA   NA   NA   NA    NA    NA    NA    NA
tmp   10   11   12   13   NA   NA   NA   NA   NA    NA    NA    NA    NA
tmp   11   12   13   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA
tmp   12   13   NA   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA
tmp   13   NA   NA   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA
最后是唯一模式的计数:

counts <- sort(table(mm2),decreasing = T)
您想要的输出:

res <- data.frame("nlength"=sapply(names(counts),nchar),"counts"=counts)
head(res)
     nlength counts
12         2     3
34         2     3
123        3     2
1234       4     2
23         2     2
234        3     2

正如其他人所指出的,一个2克的模式总是至少是最常见的,所以只考虑2克就足够了。

只是为了好玩,请考虑这个Python 3解决方案:

# Solution
from collections import Counter
def most_common_2gram(row):
    pairs = [tuple(row[j:j+2]) for j in range(len(row)-1)]
    return Counter(pairs).most_common(1) # or most_common(k) as desired

# Testing Data
from random import randint
nrow = int(1e6)
ncol = 10
x = [[randint(0,9) for j in range(ncol)] for i in range(nrow)]

# Performance Test
%timeit [most_common_2gram(row) for row in x]

# Time result: 21 seconds (from %paste into IPython)
使用20列运行的示例:

x[0:5]
Output: 
 [[0, 8, 1, 7, 9, 7, 7, 3, 8, 5, 1, 5, 4, 7, 8, 5, 4, 2, 3, 5],
 [6, 5, 4, 0, 9, 0, 0, 4, 4, 3, 7, 1, 7, 0, 7, 5, 1, 9, 0, 5],
 [2, 4, 8, 5, 8, 9, 5, 9, 0, 8, 8, 2, 4, 8, 1, 6, 3, 7, 8, 1],
 [5, 6, 6, 7, 1, 7, 9, 3, 3, 0, 7, 9, 5, 7, 6, 3, 8, 5, 3, 3],
 [4, 6, 3, 9, 1, 5, 5, 4, 9, 0, 2, 1, 5, 3, 4, 2, 4, 1, 4, 5]]

out[0:5]
Output: [[((5, 4), 2)], [((9, 0), 2)], [((4, 8), 2)], [((3, 3), 2)], [((1, 5), 2)]]
如果需要所有模式,而不仅仅是最常见的模式,可以将计数器对象转换为列表并按计数排序。例如:

counts = Counter({(0,2): 2, (3,4): 3, (1,6): 1})
sorted(counts.items(),key=lambda pc: pc[1],reverse=True)
Output: [((3, 4), 3), ((0, 2), 2), ((1, 6), 1)]

*唯一可能的例外是,如果有一个3克或更多的频率与2克相同,而你希望3克+是首选。这种情况可以用更复杂的代码来检测和处理。正如其他人所指出的那样,<2/>>P>,2克模式总是至少是最常见的,所以只考虑2克就足够了。*

只是为了好玩,请考虑这个Python 3解决方案:

# Solution
from collections import Counter
def most_common_2gram(row):
    pairs = [tuple(row[j:j+2]) for j in range(len(row)-1)]
    return Counter(pairs).most_common(1) # or most_common(k) as desired

# Testing Data
from random import randint
nrow = int(1e6)
ncol = 10
x = [[randint(0,9) for j in range(ncol)] for i in range(nrow)]

# Performance Test
%timeit [most_common_2gram(row) for row in x]

# Time result: 21 seconds (from %paste into IPython)
使用20列运行的示例:

x[0:5]
Output: 
 [[0, 8, 1, 7, 9, 7, 7, 3, 8, 5, 1, 5, 4, 7, 8, 5, 4, 2, 3, 5],
 [6, 5, 4, 0, 9, 0, 0, 4, 4, 3, 7, 1, 7, 0, 7, 5, 1, 9, 0, 5],
 [2, 4, 8, 5, 8, 9, 5, 9, 0, 8, 8, 2, 4, 8, 1, 6, 3, 7, 8, 1],
 [5, 6, 6, 7, 1, 7, 9, 3, 3, 0, 7, 9, 5, 7, 6, 3, 8, 5, 3, 3],
 [4, 6, 3, 9, 1, 5, 5, 4, 9, 0, 2, 1, 5, 3, 4, 2, 4, 1, 4, 5]]

out[0:5]
Output: [[((5, 4), 2)], [((9, 0), 2)], [((4, 8), 2)], [((3, 3), 2)], [((1, 5), 2)]]
如果需要所有模式,而不仅仅是最常见的模式,可以将计数器对象转换为列表并按计数排序。例如:

counts = Counter({(0,2): 2, (3,4): 3, (1,6): 1})
sorted(counts.items(),key=lambda pc: pc[1],reverse=True)
Output: [((3, 4), 3), ((0, 2), 2), ((1, 6), 1)]

*唯一可能的例外是,如果有一个3克或更多的频率与2克相同,而你希望3克+是首选。这种情况可以通过更复杂的代码检测和处理。

base R中的另一种方法离实际算法不远,主要是使用正则表达式来获得长度至少为2的可能匹配:

str<-c("3412123401234") # example entries

replength<-function(pat,s) {
  length( gregexpr(pat,s)[[1]] )
}

repeats<-function(s) {
  r<-sapply(
    unique(
      regmatches( s, gregexpr("(\\d{2,})(?=.*\\1)", s, perl=T) )[[1]]
    ),
    replength,s=s )
  pat<-names(r[r==max(r)][1])
  data.frame("Pattern"=pat,"Repeats"=unname(r[r==max(r)][1]),"Pat.length"=nchar(pat),stringsAsFactors=FALSE)
}
它将跳过一些中间匹配,在第一个示例中是123,这取决于它们在字符串中的显示方式。然而,如果它们不匹配,那么应该是它们被另一个匹配或同等匹配“取代”,我没有发现一个测试用例证明它是错误的

代码的基准测试很长,包含所有函数,因此我对其进行了详细的分析:


base R中的另一种方法与实际算法并不遥远,主要是使用正则表达式来获得长度至少为2的可能匹配:

str<-c("3412123401234") # example entries

replength<-function(pat,s) {
  length( gregexpr(pat,s)[[1]] )
}

repeats<-function(s) {
  r<-sapply(
    unique(
      regmatches( s, gregexpr("(\\d{2,})(?=.*\\1)", s, perl=T) )[[1]]
    ),
    replength,s=s )
  pat<-names(r[r==max(r)][1])
  data.frame("Pattern"=pat,"Repeats"=unname(r[r==max(r)][1]),"Pat.length"=nchar(pat),stringsAsFactors=FALSE)
}
它将跳过一些中间匹配,在第一个示例中是123,这取决于它们在字符串中的显示方式。然而,如果它们不匹配,那么应该是它们被另一个匹配或同等匹配“取代”,我没有发现一个测试用例证明它是错误的

代码的基准测试很长,包含所有函数,因此我对其进行了详细的分析:


当我尝试你的函数时,它输出模式34,重复次数=3,而你的问题想要1234作为模式,出现次数为2。哪一个是理想的答案?图案长度是否有其他限制?感谢您花时间。对不起,我把问题写错了。我确实想优先考虑重复的次数,然后是模式的长度。我将编辑它。谢谢注:除34外,图案12也出现3次。此外,如果1234出现n次:那么12123、23234、34也都出现n次。因此,您也可以搜索xx模式,因为这些模式总是会更频繁地出现。实际上,编号的标识仅用于检查目的,我只需要获取长度和重复次数的属性。对所有答案进行向下投票的人能否至少对原因进行评论?当我尝试您的函数时,它输出模式34,重复次数=3,而您的问题希望1234作为模式,出现次数为2。哪一个是理想的答案?图案长度是否有其他限制?感谢您花时间。对不起,我把问题写错了。我确实想优先考虑重复的次数,然后是模式的长度。我将编辑它。谢谢注:除34外,图案12也出现3次。此外,如果1234出现n次:那么12123、23234、34也都出现n次。因此,您也可以搜索xx模式,因为这些模式总是会更频繁地出现。实际上,编号的标识仅用于检查目的,我只需要获得长度属性和重复次数。可以吗 对所有答案投否决票的人至少要评论一下原因?