Encuentra la frase nominal antes y después de un número en un texto

Aug 19 2020

Dado un texto, tengo que encontrar las palabras anteriores a todos los números hasta una palabra vacía que pertenezca a una lista check_words (tipo de palabras vacías).

Mi código:

check_words = ['the', 'a', 'with','to']
mystring = 'the code to find the beautiful words 78 that i have to nicely check 45 with the snippet'
list_of_words = mystring.split()

En ese texto en particular, verificaría antes '78'y '45'retrocederé hasta el punto en que encuentre cualquiera de las palabras en check_words (pero no más de 8 palabras).

El código para hacerlo podría ser:

preceding_chunks = []
for i,word in enumerate(list_of_words):
    if any(char.isdigit() for char in word):
       
        # 8 precedent words (taking into account that I can not slice with 8 back at the beginning)
        preceding_words = list_of_words[max(0,i-8):i]
        preceding_words[::-1]

        # I check from the end of the list towards the start
        for j,sub_word in enumerate(preceding_words[::-1]):
            if  sub_word in check_words:
                # printing out j for checking
                myposition = j
                print(j)
                real_preceding_chunk = preceding_words[len(preceding_words)-j:]
                print(real_preceding_chunk)
                preceding_chunks.append(real_preceding_chunk)
                break

Este código funciona. básicamente compruebo cada palabra pero tengo la impresión (tal vez me equivoque) de que se puede lograr con un par de frases de una sola línea y, por lo tanto, sin bucles. ¿Alguna idea?


NOTA: Esta pregunta se trata de mejorar la legibilidad del código, tratar de deshacerse de los bucles para hacer que el código sea más rápido e intentar que el código sea más agradable, lo cual es parte del Zen de Python.


NOTA 2: Algunas comprobaciones anteriores que hice:

  • Encontrar la posición de un elemento en otra lista a partir de un número en una lista diferente
  • Encontrar el índice de un elemento en una lista
  • Buscar en la lista

Respuestas

MarioIshac Aug 19 2020 at 09:13

Se me ocurrió esto:

import itertools
import re

chunks = (grouped_chunk.split() for grouped_chunk in re.split("\\s+\\d+\\s+", mystring))
preceding_chunks = []

for reversed_chunk in map(reversed, chunks):
    preceding_chunk = list(itertools.takewhile(lambda word: word not in check_words, reversed_chunk))[::-1]
    preceding_chunks.append(preceding_chunk)

Aplicamos itertools.takewhileal reversed_chunkque nos da el trozo anterior en orden inverso. Luego obtenemos el orden correcto preceding_chunkinvirtiendo al final con [::-1].

La expresión regular se divide mystringen función de un número (el escape \d+). Las s escapadas circundantes \s+representan cualquier relleno alrededor del número. Esto hace que este código tenga un comportamiento diferente al suyo si se mezclan dígitos y letras en las mismas palabras (por ejemplo, a1).

Para su código original, haría un par de sugerencias:

  1. Siga PEP 8 . Por ejemplo, agregue espacio después de la coma en i,word.
  2. Elimina la expresión redundante preceding_words[::-1]. Si bien esto se evalúa a la inversa preceding_words, debido a que no está en el lugar, la evaluación no tiene efectos secundarios. Además, ya realiza esta inversión en enumerate(preceding_words[::-1]).