np.where 如何通过正则表达式提高性能?

4

我刚开始学习Python的NumPy和正则表达式。我尝试从每行的Pandas文本列中提取模式。根据我的要求,有许多可能的情况,因此我编写了以下不同的正则表达式。为了迭代并搜索给定的模式,我使用Python的np.where,但我遇到了性能问题。是否有任何方法可以提高性能或任何替代方案以实现以下输出。

x_train['Description'] is my pandas column.

54672 rows in my dataset.


Code:

pattern1 = re.compile(r'\bAGE[a-z]?\b[\s\w]*\W+\d+.*(?:year[s]|month[s]?)',re.I)

pattern2 = re.compile(r'\bfor\b[\s]*age[s]?\W+\d+\W+(?:month[s]?|year[s]?)',re.I)

pattern3 = re.compile(r'\badult[s]?.[\w\s]\d+',re.I)

pattern4 = re.compile(r'\b\d+\W+(?:month[s]?|year[s]?)\W+of\W+age[a-z]?',re.I)

pattern5 = re.compile(r'[a-z][a-z\s]+(?:month[s]?|year[s]?)[\w\s]+age[s]?',re.I) 

pattern6 = re.compile(r'\bage.*?\s\d+[\s]*\+',re.I)

pattern7 = re.compile(r'\bbetween[\s]*age[s]?[\s]*\d+.*(?:month[s]?|year[s]?)',re.I)

pattern8 = re.compile(r'\b\d+[\w+\s]*?(?:\band\sup\b|\band\sabove\b|\band\sold[a-z]*\b)',re.I)

np_time = time.time()

x_train['pattern'] = np.where(x_train['Description'].str.contains(pattern1), x_train['Description'].str.findall(pattern1),

                              np.where (x_train['Description'].str.contains(pattern2), x_train['Description'].str.findall(pattern2),

                              np.where (x_train['Description'].str.contains(pattern3), x_train['Description'].str.findall(pattern3),

                              np.where (x_train['Description'].str.contains(pattern4), x_train['Description'].str.findall(pattern4),  

                              np.where (x_train['Description'].str.contains(pattern5), x_train['Description'].str.findall(pattern5),  

                              np.where (x_train['Description'].str.contains(pattern6), x_train['Description'].str.findall(pattern6),  

                              np.where (x_train['Description'].str.contains(pattern7), x_train['Description'].str.findall(pattern7),  

                              np.where (x_train['Description'].str.contains(pattern8), x_train['Description'].str.findall(pattern8),                                


                                                'NO PATTERN')      

                                                             )))))))


print "pattern extraction ran in = "
print("--- %s seconds ---" % (time.time() - np_time))



pattern extraction ran in = 
--- 99.5106501579 seconds ---

以下是上述代码的示例输入和输出:

        Description                                  pattern     

    0  **AGE RANGE: 6 YEARS** AND UP 10' LONG          AGE RANGE: 6 YEARS 
       STRING OF BEAUTIFUL LIGHTS MULTIPLE 
       LIGHT EFFECTS FADE IN AND OUT

    1  DIMENSIONS   OVERALL HEIGHT - TOP           AGE GROUP: -2 YEARS/3 TO 4 
       TO BOTTOM: 34.5'' OVERALL WIDTH - SIDE      YEARS/5 TO 6 YEARS/7 TO 8 
                                                   YEARS/7 TO 8 YEARS.
       TO SIDE: 20''  OVERALL DEPTH - 
       FRONT TO BACK:      15''  COUNTER TOP 
       HEIGHT - TOP TO BOTTOM: 23''  OVERALL 
       PRODUCT WEIGHT: 38 LBS "   
       **"AGE GROUP: -2 YEARS/3 TO 4 YEARS/5 TO 6 
        YEARS/7 TO 8 YEARS**.

   2   THE FLAME-RETARDANT FOAM ALSO CONTAINS              AGED 1-5 YEARS
       ANTIMICROBIAL PROTECTION, SO IT WON'T GROW 
       MOLD OR BACTERIA IF IT GETS WET. THE 
       BRIGHTLY-COLORED 
       VINYL EXTERIOR IS EASY TO WIPE CLEAN. FOAMMAN 
       IS DESIGNED FOR KIDS **AGED 1-5 YEARS**

你能分享一下输入和输出吗? - piRSquared
当然可以。我已经编辑了带有样本输入和输出的内容。 - niranjan
一年后再次提起这个问题,我遇到了类似的问题,但似乎找不到更简洁的解决方案。 - grrothman
1个回答

1

你可以尝试以下几件事情:

首先,你需要确定哪些正则表达式速度较慢。例如,你可以使用https://regex101.com/观察“步骤”值。

我检查了这些正则表达式,其中5和8是最慢的。

27800 steps = [a-z][a-z\s]+(?:month[s]?|year[s]?)[\w\s]+age[s]?
 4404 steps= \b\d+[\w+\s]*?(?:\band\sup\b|\band\sabove\b|\band\sold[a-z]*\b)

你可以考虑优化这两个正则表达式。

例如,你可以将这个 \b\d+[\w+\s]*?(?:\band\sup\b|\band\sabove\b|\band\sold[a-z]*\b)

重写为这个 \b\d+[\w+\s]*?(?:\band\s(?:up|above|old[a-z]*\b)),它使用了约50%的步骤。

对于另一个正则表达式,有几个选项。你可以将其重写为:

[A-Z][A-LN-XZ\s]+(?:(?:Y(?!EARS?)|M(?!ONTHS?))[A-LN-XZ\s]+)*(?:MONTHS?|YEARS?)[\w\s]+AGE[S]?

这样会稍微快一些。虽然不是很多(27800与23800)。

然而,真正加速的方法似乎是使其区分大小写。

原始的区分大小写的正则表达式只执行了3700步。而经过优化的则只有1470步。

所以您可以将整个字符串转换为大写/小写,并在区分大小写的正则表达式中使用它。根据您的示例,您甚至可能不需要转换字符串为大写。另一个要注意的是测试正则表达式的顺序。如果有一些正则表达式比其他正则表达式更可能匹配,则应首先测试它们。如果您无法确定这样的概率并且认为它们差不多相同,则可以考虑先放置更简单的正则表达式。总之,测试一个难以匹配的复杂正则表达式是浪费时间的。最后,当您有(a|b|c)这样的选项时,出于相同的原因,您可以考虑将最有可能的选项放在开头。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接