2016-03-04 4 views
3

Si j'ai un collection of strings est-il une structure de données ou une fonction qui pourrait améliorer la vitesse de vérification si l'un des éléments des collections sont substrings sur ma chaîne principale ?Python3 rapide pour trouver si des éléments dans les collections sont sous-chaîne de chaîne

En ce moment je suis en boucle à travers mon tableau de chaînes et en utilisant l'opérateur in. Y at-il un moyen plus rapide?

import timing 

## string match in first do_not_scan 
## 0:00:00.029332 

## string not in do_not_scan 
## 0:00:00.035179 
def check_if_substring(): 
    for x in do_not_scan: 
     if x in string: 
      return True 
    return False 

## string match in first do_not_scan 
## 0:00:00.046530 

## string not in do_not_scan 
## 0:00:00.067439 
def index_of(): 
    for x in do_not_scan: 
     try: 
      string.index(x) 
      return True 
     except: 
      return False 

## string match in first do_not_scan 
## 0:00:00.047654 

## string not in do_not_scan 
## 0:00:00.070596 
def find_def(): 
    for x in do_not_scan: 
     if string.find(x) != -1: 
      return True 
    return False 

string = '/usr/documents/apps/components/login' 
do_not_scan = ['node_modules','bower_components'] 

for x in range(100000): 
    find_def() 
    index_of() 
    check_if_substring() 
+0

Est-il possible que vous ayez collé quelque chose de mal ici?Ou est 'string = 'a'' juste un échantillon. Parce que 'node_modules' ne sera jamais dans' string'. Cela dit, pouvez-vous utiliser une carte. Où les clés sont les éléments de 'do_not_scan'. Alors la recherche est O (1) – Cripto

+0

juste un échantillon pour démontrer 'string' ne peut contenir aucun élément de' do_not_scan'. Je n'ai jamais utilisé de carte avant comment vous y prendrez-vous? – ClickThisNick

+0

Voulez-vous l'analogue de 'grep -l -Ff collections_of_strings main_string'? où le fichier 'collections_of_strings' contient des collections de chaînes (une par ligne) et le fichier' main_string' contient la chaîne principale (telle quelle). – jfs

Répondre

2

Non, il n'y a pas moyen intégré plus rapide.

Si vous avez de grandes quantités de chaînes à tester, il est préférable d'utiliser un package tiers Aho-Corasick, comme le montre J.F. Sebastian's answer.


À l'aide des méthodes intégrées, le pire scénario est le suivant: il n'y a pas de correspondance, ce qui signifie que vous avez testé tous les éléments de la liste et presque tous les décalage dans chaque article.

Heureusement, l'opérateur in est très rapide (au moins en CPython) et a été plus rapide de près d'un facteur de trois dans mes tests:

0.3364804992452264 # substring() 
0.867534976452589 # any_substring() 
0.8401796016842127 # find_def() 
0.9342398950830102 # index_of() 
2.7920695478096604 # re implementation 

Voici le script que j'utilisé pour le test:

from timeit import timeit 
import re 

def substring(): 
    for x in do_not_scan: 
     if x in string: 
      return True 
    return False 

def any_substring(): 
    return any(x in string for x in do_not_scan) 

def find_def(): 
    for x in do_not_scan: 
     if string.find(x) != -1: 
      return True 
    return False 

def index_of(): 
    for x in do_not_scan: 
     try: 
      string.index(x) 
      return True 
     except: 
      return False 

def re_match(): 
    for x in do_not_scan: 
     if re.search(string, x): 
      return True 
    return False 

string = 'a' 
do_not_scan = ['node_modules','bower_components'] 

print(timeit('substring()', setup='from __main__ import substring')) 
print(timeit('any_substring()', setup='from __main__ import any_substring')) 
print(timeit('find_def()', setup='from __main__ import find_def')) 
print(timeit('index_of()', setup='from __main__ import index_of')) 
print(timeit('re_match()', setup='from __main__ import re_match')) 
+0

c'est incorrect. Vous pouvez faire mieux que 'O (n * m)' par exemple, [algorithme Aho-Corasick] (https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm) est 'O (n + m) 'à temps. ['grep' peut l'utiliser pour les chaînes fixes] (http://stackoverflow.com/questions/35803016/python3-fast-way-to-find-if-any-elements-in-collections-are-substring-of- string # comment59300275_35803016) – jfs

+0

@JFSebastian: Correction, merci. –

2
def check(): 
    if any(w in string for w in do_not_scan): 
     return True 
    else: 
     return False 

Ou plus simple:

def check(): 
    return any(w in string for w in do_not_scan) 

comme mentionné par @ Two-Bit Alchemist

+0

chaîne de correspondance en premier do_not_scan = 0: 00: 00.085493 | Chaîne pas dans do_not_scan = 0: 00: 00.074540 – ClickThisNick

+0

Plus simple: 'return any (w dans string pour w dans do_not_scan)' –

+0

'any' est aussi lent que' find_def' et 'index_of'. –

2

Je n'ai pas un grand ensemble de données pour essayer:

Mais maybes quelque chose comme ça va fonctionner?

python3

from builtins import any 
import timeit 

do_not_scan = ['node_modules', 'bower_components'] 
string = 'a' 


def check_if_substring(): 
    return any(string in x for x in do_not_scan) 


result = timeit.Timer("check_if_substring()", "from __main__ import check_if_substring") 
count = 10000 
print(result.timeit(count)/count) 

Ou l'inverse:

def check_if_substring(): 
    return any(x in string for x in do_not_scan) 

Mes résultats: 6.48119201650843e-07

+0

Juste curieux - pourquoi vous renommez tout et pourquoi? –

+0

C'était une copie et un ancien code. Cela n'a pas de sens dans ce contexte. Ill réparer – Cripto

+0

'any' est aussi lent que' find_def' et 'index_of'. –

2

Oui, il y a un moyen plus rapide pour effectuer found = any(s in main_string for s in collection_of_strings) par exemple, il y a Aho-Corasick_algorithm cela permet d'améliorer any()-based O(n*m*k) algorithme à O(n + m*k) dans l'opération de temps où n est len(main_string), m est len(collections_of_strings), et k représente les longueurs individuelles des chaînes dans la collection.

#!/usr/bin/env python 
import noaho # $ pip install noaho 

trie = noaho.NoAho() 
for s in collection_of_strings: 
    trie.add(s) 
found = trie.find_short(main_string)[0] is not None 

Note: il n'y a pas de point pour mesurer la performance du temps sur les chaînes minuscules tels que string = 'a' si vous êtes intéressé par le comportement Big-O. Utilisez un échantillon plus représentatif pour le benchmark ou vous n'avez pas besoin d'un algorithme plus rapide (asymptotiquement) dans votre cas.

+0

Pouvez-vous donner des indications sur l'utilisation de l'algorithme Aho-Corasick plutôt que sur «in»? –

+0

Seul votre profileur sait. Les facteurs constants dépendent de la qualité de l'implémentation, par exemple, ['str.translate()' dans Python 3.5+ sur entrée ASCII seulement peut être 50 fois plus rapide que le même code sur les versions précédentes de Python 3] (http: // stackoverflow. com/q/34287893/4279). – jfs