Usando la mappa () per ottenere il numero di elementi della lista volte esiste in una stringa in Python

StackOverflow https://stackoverflow.com/questions/1168517

  •  19-09-2019
  •  | 
  •  

Domanda

Sto cercando di ottenere il numero di volte in cui ogni elemento in un elenco è in una stringa in Python:

paragraph = "I eat bananas and a banana"

def tester(x): return len(re.findall(x,paragraph))

map(tester, ['banana', 'loganberry', 'passion fruit'])

Consegna [2, 0, 0]

Quello che mi piacerebbe fare, tuttavia, è estendere questo modo che io possa nutrire il valore paragrafo nella funzione map (). In questo momento, la funzione di tester () ha comma hardcoded. Qualcuno ha un modo per farlo (magari fare una lista n-lunghezza dei valori di paragrafo)? Tutte le altre idee qui?

Ricordate che ognuno dei valori di matrice avrà un peso ad un certo punto in futuro -. Da qui la necessità di mantenere i valori di una lista piuttosto che macinare tutti insieme

UPDATE: Il punto sarà spesso 20K e la lista avrà spesso 200+ membri. Il mio pensiero è che mappa funziona in parallelo -. Così sarà molto più efficiente di tutti i metodi di serie

È stato utile?

Soluzione

Ecco una risposta al movimento dei pali ( "Probabilmente bisogno l'espressione regolare, perché avrò bisogno delimitatori di parola in un prossimo futuro"):

Questo metodo analizza il testo una volta per ottenere un elenco di tutte le "parole". Ogni parola viene cercato in un dizionario delle parole di destinazione, e se è una parola bersaglio è conteggiato. Il tempo impiegato è O (P) + O (T), dove P è la dimensione del punto e T è il numero di parole target. Tutte le altre soluzioni fino ad oggi (compresa la soluzione attualmente accettato) tranne la mia soluzione Aho-Corasick sono O (PT).

def counts_all(targets, paragraph, word_regex=r"\w+"):
    tally = dict((target, 0) for target in targets)
    for word in re.findall(word_regex, paragraph):
        if word in tally:
            tally[word] += 1
    return [tally[target] for target in targets]

def counts_iter(targets, paragraph, word_regex=r"\w+"):
    tally = dict((target, 0) for target in targets)
    for matchobj in re.finditer(word_regex, paragraph):
        word = matchobj.group()
        if word in tally:
            tally[word] += 1
    return [tally[target] for target in targets] 

La versione finditer è un uomo di paglia -. È molto più lento rispetto alla versione findall

Ecco la soluzione attualmente accettato espressa in forma standardizzata e aumentata con delimitatori di parola:

def currently_accepted_solution_augmented(targets, paragraph):
    def tester(s): 
        def f(x):
            return len(re.findall(r"\b" + x + r"\b", s))
        return f
    return map(tester(paragraph), targets)

che va in mare su chiusure e potrebbe essere ridotto a:

# acknowledgement:
# this is structurally the same as one of hughdbrown's benchmark functions
def currently_accepted_solution_augmented_without_extra_closure(targets, paragraph):
    def tester(x):
        return len(re.findall(r"\b" + x + r"\b", paragraph))
    return map(tester, targets)

Tutte le variazioni sulla soluzione attualmente accettato sono O (PT). A differenza della soluzione attualmente accettato, la ricerca regex con delimitatori di parola non è equivalente ad un semplice paragraph.find(target). Perché il motore ri non usa la "ricerca rapida" in questo caso, l'aggiunta della parola delimitatori cambia fron lento a molto lenta.

Altri suggerimenti

Una chiusura sarebbe una soluzione rapida:

paragraph = "I eat bananas and a banana"

def tester(s): 
    def f(x):
        return len(re.findall(x,s))
    return f

print map(tester(paragraph), ['banana', 'loganberry', 'passion fruit'])
targets = ['banana', 'loganberry', 'passion fruit']
paragraph = "I eat bananas and a banana"

print [paragraph.count(target) for target in targets]

Non ho idea perché si userebbe mappa () qui.

So che non hai chiesto di lista, ma qui è in ogni caso:

paragraph = "I eat bananas and a banana"
words = ['banana', 'loganberry', 'passion fruit']
[len(re.findall(word, paragraph)) for word in words]

Questo restituisce     [2, 0, 0] pure.

Questo è fondamentalmente solo andare dal tuo modo per evitare di lista, ma se vi piace la programmazione stile funzionale, allora vi piacerà functools.partial .

>>> from functools import partial
>>> def counter(text, paragraph):
    return len(re.findall(text, paragraph))

>>> tester = partial(counter, paragraph="I eat bananas and a banana")
>>> map(tester, ['banana', 'loganberry', 'passion fruit'])
[2, 0, 0]

Per Q parole di query di media lunghezza L byte su grandi testi di dimensioni T byte, è necessario qualcosa che non è O (QLT). Avete bisogno di un approccio in stile DFA che può dare O (T) ... dopo costi di installazione. Se il set di query è piuttosto statico, allora il costo di installazione può essere ignorato.

es. http://en.wikipedia.org/wiki/Aho-Corasick_algorithm
che punta ad un C-estensione per Python:
http://hkn.eecs.berkeley.edu/~dyoo/python/ahocorasick/

Ecco la mia versione.

paragraph = "I eat bananas and a banana"

def tester(paragraph, x): return len(re.findall(x,paragraph))

print lambda paragraph: map(
    lambda x: tester(paragraph, x) , ['banana', 'loganberry', 'passion fruit']
        )(paragraph)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top