查找字符串中的重复图案

时间:2018-10-25 06:18:51

标签: python string pattern-recognition

假设我有一个长字符串:

AX 90 10 20 AX 30 14 50 ER 40 50 68 ...

我需要的是

['AX 90 10 20', 'AX 30 14 50', 'ER 40 50 68',...]

我不想使用正则表达式,因为我会得到不同的重复模式,例如下面的示例,其中上面的正则表达式不适用于下面的

WE 12 (09/09) ER 14 (12/56) TO 90 (45/67) ...

对于第一个示例,我首先创建结构表示形式(将[A-Z]转换为“ A”,将[0-9]转换为“ 9”)

AX 90 10 20 AX 30 14 50 ER 40 50 68 ...
to
AA 99 99 99 AA 99 99 99 AA 99 99 99 ...

我的问题是,如何通过实时识别每个字符串中的模式然后获得匹配项?

注意: 模式未知,但已知某些字符会在一段时间后重复出现

我不想使用手动编写的正则表达式。如果系统生成正则表达式,那就没问题了。

2 个答案:

答案 0 :(得分:3)

您可以尝试使用time series analysis的季节性来依次获得相似的模式

为此,您可以尝试将字符串转换为整数,然后 使用statsmodels应用seasonal_decompose,然后您就可以从图形中观察重复模式的周期。

from matplotlib import pyplot
from statsmodels.tsa.seasonal import seasonal_decompose
a = 'AX 90 10 20 AX 30 14 50 ER 40 50 68'
a = list(map(ord,a))
series = pd.Series(a ,index = pd.date_range('1-1-2011',pd.to_datetime('1-1-2011')+np.timedelta64(len(a)-1,'D'),freq='D'))
result = seasonal_decompose(series, model='additive')

result.observed.plot()
result.trend.plot()
pyplot.rcParams["figure.figsize"] = (20,3)
pyplot.show()

观察到的季节性和序列趋势 enter image description here

然后在观察到的时间段内分割序列

修改

要在无需视觉检查的情况下找到序列的周期性,

我们可以使用autocorrelation以及信号的相关滞后来发现信号的周期性,从而显示出周期性。这样我们就可以对模式进行切片以检索相似的模式

def autocorr(x):
    n = x.size
    norm = (x - np.mean(x))
    result = np.correlate(norm, norm, mode='same')
    acorr = result[n//2 + 1:] / (x.var() * np.arange(n-1, n//2, -1))
    lag = np.abs(acorr).argmax() + 1
    return lag
period = autocorr(np.array(a))



#Here the pattern is repeating for each period of 12 values, you can pick the period also 
period = 12
for i in range(0,len(a),period):
    print(''.join(map(chr,a[i:i+period])))

出局:

AX 90 10 20 
AX 30 14 50 
ER 40 50 68

答案 1 :(得分:1)

如果您知道目标字符串都以N长度(此处为2个)大写字母开头,那么我不确定这是否那么复杂。

以下是可能的解决方案:

import re # only used in is_token, but not technically needed

def is_token(t):
    return re.match(r'^[A-Z]+$', t)

def get_token_candidate_at(s, idx):
    return s[idx:idx+2]

def emit_items(s):
    delim_start = -1
    for i,_ in enumerate(s):
        token = get_token_candidate_at(s, i)

        if is_token(token):
            if delim_start >= 0:
                yield s[delim_start:i]
            delim_start = i

    if delim_start > 0: # get the last one
        yield s[delim_start:]

> list(emit_items("WE 12 (09/09) ER 14 (12/56) TO 90 (45/67)"))
  ['WE 12 (09/09) ', 'ER 14 (12/56) ', 'TO 90 (45/67)']

> list(emit_items("WE12(09/09)ER14(12/56)TO90(45/67)"))
  ['WE12(09/09)', 'ER14(12/56)', 'TO90(45/67)']

> list(emit_items("AZ893249EE886342TT125435"))
  ['AZ893249', 'EE886342', 'TT125435']

如果他们有不同的开始,则可以更改is_tokenget_token_candidate_at以满足这些不同的要求。

如果模式确实是周期性的,那么您也许可以摆脱类似于频率分析的问题,但随后您必须了解有关“什么”是周期性的(例如“非数字是周期”),然后希望字符串足够长以提供有意义的周期性信号。这就是@zwer所得到的...“您期望的模式的属性是什么”。