2
votes

Rechercher des mots triples dans une liste de mots en Python

La forme comparative de l'adjectif big est plus grande et la forme superlative est plus grande. Je voudrais imprimer tous ces triplets (tôt, plus tôt, plus tôt) ou (difficile, plus difficile, plus difficile), ...

J'utilise Python pour ouvrir wordList.txt qui se compose d'environ 5000 mots. J'ai parfaitement testé mon code sur un petit fichier, mais il ne peut pas fonctionner sur le gros fichier car les boucles sont trop longues.

def puzzleA(wordList):
    tempList1 = []
    tempList2 = []
    tempList3 = []
    for word in wordList:
        if word[-2:]=='er':
            tempList1.append(word)
        if word[-3:]=='est':
            tempList2.append(word)            

    for word1 in wordList:
        for word2 in tempList1:
            if word1==word2[:-2]:
                tempList3.append(word1)
    for word1 in tempList3:
        for word2 in tempList2:
            if word1==word2[:-3]:
                print('{}, {}er, {}'.format(word1,word1,word2))      

Pouvez-vous suggérer un autre algorithme pour optimiser l'exécution temps s'il vous plaît!


0 commentaires

3 Réponses :


1
votes

Vous pouvez créer un dict avec la racine des mots comme clé, et toutes les variations d'une liste comme valeur. Ensuite, nous ne conservons que les entrées avec 3 valeurs. De cette façon, nous n'itérons qu'une seule fois sur la liste, et une fois sur le dict que nous avons créé, en gardant tout le processus O (n).

Nous pouvons utiliser un defaultdict pour construire le dict plus facilement. Notez que la fonction root peut avoir besoin d'être améliorée, vérifiez votre liste d'adjectifs anglais!

from collections import defaultdict


def root(word):
    if len(word) < 4:
        return word
    if word[-3:] == 'ier':
        return word[:-3] + 'y'
    elif word[-4:] == 'iest':
        return word[:-4] + 'y'
    elif word[-2:] == 'er':
        return word[:-2]
    elif word[-3:] == 'est':
        return word[:-3]
    else:
        return word

def find_triples(words):
    out_dict = defaultdict(list)
    for word in words:
        out_dict[root(word)].append(word)

    # keep only the lists with 3 distinct values, sorted by length
    out = [sorted(set(values), key=len) for values in out_dict.values() 
                                   if len(set(values))==3]
    return out


data = ['early', 'earlier', 'earliest', 'or', 'hard', 'harder', 'hardest', 'ignored']
print(find_triples(data))
# [['early', 'earlier', 'earliest'], ['hard', 'harder', 'hardest']]


1 commentaires

Merci beaucoup, Thierry. Je poste un commentaire ci-dessous, j'espère que vous pourrez aider.



0
votes

Merci beaucoup d'avoir posté Thierry Lathuille. Cela fait maintenant 4 heures que j'ai regardé votre réponse. Je ne suis pas assez bon pour comprendre vos codes. J'ai ajusté la fonction racine (mot) comme ceci:

out = [sorted(values, key=len) for values in out_dict.values() if len(values)==3]

Mais il y a 2 problèmes pour le moment: Premièrement, la liste de mots contient des mots dupliqués, donc elle produit quelque chose comme [terry, terry, terrier].

Deuxièmement, il est vraiment difficile de trouver un tel triple [grand, plus grand, plus grand]

Le mien produit [whin, whiner, whinner], [willy, willier, willyer], [slat, slater, slatter], ...

Supposons que je ne sois pas autorisé à supprimer les mots en double en premier. Il existe donc un moyen d'accéder à chaque valeur de chaque clé. J'aimerais comparer ces valeurs de paires pour éliminer les résultats indésirables.

Et Thierry, si vous avez le temps, pouvez-vous expliquer ce code s'il vous plaît?

def root(word):
    if len(word) < 4:
        return word
    if word[-3:] == 'ier':
        return word[:-3] + 'y'
    elif word[-4:] == 'iest':
        return word[:-4] + 'y'
    elif word[-2:] == 'er':
        if word[-4:-3]==word[-3:-2]:
            return word[:-3]
        else:
            return word[:-2]
    elif word[-3:] == 'est':
        if word[-4:-3]==word[-5:-4]:
            return word[:-4]        
        return word[:-3]
    else:
        return word

Je suis vraiment mauvais pour lire une compréhension de liste.


6 commentaires

Pour lire les compréhensions de liste, retournez-les: mettez d'abord la partie for , puis la partie if , puis imaginez l'expression avant que le for soit < code> ajouter ed à une liste. par exemple. ce qui précède est équivalent à: out = []; for similar_words in out_dict.values ​​(): if len (similar_words) == 3: out.append (sorted (similar_words, key = len) . N'oubliez pas que chaque valeur du dict est en fait la liste de mots partageant le même racine (c'est un dict-of-lists).


Vous pouvez contourner le problème des mots en double en utilisant set au lieu de list comme conteneur dans le dict; par exemple. utilisez defaultdict (set) au lieu de defaultdict (list) . De cette façon, au lieu d ' ajouter un mot à la liste appropriée dans le dict en fonction de sa racine, vous allez l'ajouter à un set , qui supprimera automatiquement les doublons.


Merci beaucoup Avish.


Quant à votre deuxième problème, vous entrez maintenant dans le problème général de l'enracinement. Pour votre cas, il pourrait être suffisant d'avoir quelques règles de dérivation supplémentaires: supprimez «e» à la fin des mots dans le cadre de leur dérivation, de sorte que «fin, plus fin, le plus fin» aboutisse à «fin»; remplacer "y" par "i" à la fin des mots, de sorte que "minuscule, plus petit, le plus petit" aboutisse à "tini"; supprime les lettres répétées à la fin du mot, de sorte que "grand, plus grand, plus grand" aboutisse à "grand". Notez que peu importe si la racine finit par être un mot valide ou non; vous l'utilisez simplement pour regrouper des mots similaires.


out = [trié (valeurs, clé = len) pour les valeurs dans out_dict.values ​​() si len (valeurs) == 3] Puis-je demander, ces valeurs triées ne fonctionnent pas pour ['ret ',' retest ',' retter '] car les deux ont les mêmes 6 lettres. Pouvons-nous trier par alphabet?


"ret", "retest", "retter" est un triplet invalide: vous devriez avoir "ret", "retter", "rettest" ou "ret", "reter", "retest". Si votre logique de dérivation est correcte, les triplets ne doivent jamais inclure des mots de même longueur: l'un sera la racine, un autre sera avec un suffixe de 2 lettres "er" et un troisième sera avec un suffixe de 3 lettres "est ".



0
votes

Il semble que vous ayez maintenant du mal à trouver des mots pour endiguer les mots de manière cohérente. Pour votre scénario, vous pouvez vous contenter d'une liste de règles de remplacement de motif, c'est-à-dire "si le mot se termine par ce motif, remplacez-le par cela ". En utilisant des expressions régulières, vous pouvez facilement spécifier les modèles et les remplacements, y compris des choses comme "si elle se termine par une lettre répétitive, remplacez-la par une seule instance de cette lettre".

Par exemple:

def root(word):
  pattern_replacements = [
    ("e$", ""),    # fine => fin (to match finer, finest)
    ("y$", "i"),   # tiny => tini (to match tinier, tiniest)
    ("er$", ""),
    ("est$", ""),
    (r"([a-z])\1$", r"\1")  # bigger => big 
  ]

  for pattern, replacement in pattern_replacements:
    word = re.sub(pattern, replacement, word)

  return word


words = "big bigger biggest tiny tinier tiniest fine finer finest same samer samest good gooder goodest".split(" ")
map(root, words)
# ['big', 'big', 'big', 'tini', 'tini', 'tini', 'fin', 'fin', 'fin', 'sam', 'sam', 'sam', 'good', 'good', 'good']


3 commentaires

Vous avez manqué une virgule après ("y $", "i"). La fonction racine fonctionne vraiment bien. J'ai ajusté ma fonction find_triples et les deux fonctionnent maintenant. Merci beaucoup pour votre temps.


Merci, correction de la virgule manquante


Vous êtes les bienvenus. Mais comme il ne s'agit que d'une variation / optimisation de la réponse de @Thierry Lathuille, je pense que cette réponse devrait être marquée comme la meilleure réponse plutôt que celle-ci.