Partage
  • Partager sur Facebook
  • Partager sur Twitter

Extraction de valeurs dans des listes et des dict

Anonyme
4 avril 2020 à 21:46:02

Bonsoir à tous,

Je fais un script de parsing d'XML. Il fait un prétraitement avec ElementTree et extrait des phénomènes linguistiques complexes d'un texte. J'ai mis ce texte dans un dictionnaire qui a pour clé un identifiant unique (le numéro d’apparition du mot) et pour valeur le mot.

Exemple :

dico3 = {k: v for k, v in enumerate(l_content)}
#Dictionnaire contenant chaque mot (valeur) associé à l'id (clé/nombre unique) print(dico3)
#{0: 'score-kms:', 1: 'date:', 2: 'source:SAJ.doc', 3: 'section:1', 4: 'organe:www.aidealajeunesse.cfwb.be', 5: 'title:Demande', 6: "d'intervention…", 7: 'Le', 8: 'service', 9: 'de', 10: "l'aide", 11: 'à', 12: 'la', 13: 'jeunesse', 14: '(SAJ)', 15: 'Si', 16: 'vous', 17: 'souhaitez', 18: "qu'une", 19: 'autorité', 20: 'intervienne', 21: 'pour', 22: 'vous', 23: 'venir', 24: 'en', 25: 'aide', 26: 'une', 27: 'aide,', 28: 'un', 29: 'soutien...', 30: ':', 31: 'les', 32: 'Services', 33: 'de', 34: "l'aide", 35: 'à', 36: 'la', 37: 'Jeunesse', 38: '(SAJ)', 39: 'peuvent', 40: 'vous', 41: 'aider.', 42: '-', 43: 'L’exemple', 44: 'd’Aurélie.', 45: 'Aurélie', 46: 'a', 47: '15', 48: 'ans.', 49: 'Nous', 50: 'sommes', 51: 'au', 52: 'mois', 53: 'de', 54: 'novembre', 55: 'et', 56: 'elle', 57: 'est', 58: 'en', 59: 'absentéisme', 60: 'scolaire', 61: 'depuis', 62: '2', 63: 'mois.', 64: 'Le', 65: 'S.A.J.', 66: 'a', 67: 'été', 68: 'averti', 69: 'par', 70: 'l’école.', 71: 'Aurélie', 72: 'répond', 73: 'à', 74: 'une', 75: 'convocation', 76: 'avec', 77: 'sa', 78: 'maman.', 79: 'Après', 80: 'entretien', 81: 'avec', 82: 'la', 83: 'déléguée', 84: 'du', 85: 'SAJ,', 86: 'il', 87: 'apparaît', 88: 'que', 89: 'l’orientation', 90: 'choisie', 91: 'en', 92: 'début', 93: 'd’année', 94: 'est', 95: 'inadaptée.', 96: 'En', 97: 'conséquence,', 98: 'la', 99: 'déléguée', 100: 'oriente', 101: 'la', 102: 'jeune', 103: 'fille', 104: 'vers', 105: 'le', 106: 'SIEP', 107: '(Service', 108: 'd’informations', 109: 'sur', 110: 'les', 111: 'études', 112: 'et', 113: 'les', 114: 'professions).', 115: 'L’action', 116: 'du', 117: 'SAJ', 118: 'est', 119: 'terminée.'}

Pour chaque phénomène linguistique, ex : le segment de phrase "a été averti", je dois obtenir l'id de dico3 du premier mot (pour ce 'a', c'est 66) et du dernier mot (pour 'averti', c'est 68) afin d'avoir une colonne avec ces id. Le problème, c'est que les mots peuvent apparaître plusieurs fois dans mon texte et j'obtiens donc plusieurs id pour chaque mot alors que je veux l'id précis des mots de ce segment de phrase...

On m'a conseillé de transformer dico3 en une liste car il n'y a par exemple pas d'ordre dans un dictionnaire :

dico3 = [{k, v} for k, v in enumerate(l_content)] #OU liste contenant des dictionnaires
dico3 = [[k, v] for k, v in enumerate(l_content)] #OU liste contenant des listes


J'ai déjà essayé plusieurs choses pour extraire l'id, mais je n'y arrive pas dans mes boucles et en utilisant des list comprehensions...

Est-ce que quelqu'un aurait une idée ? Merci beaucoup !

Voici ce que j'essaie d'obtenir :

Filename \t Typetext_Gold \t ID_start_end

SAJ1 \t {'features': 'phenomene;passive'} \t a été averti \t [66, 68]

SAJ1 \t {'features': 'phenomene;passive'} \t est terminée \t [118, 119]

SAJ1 \t {'features': 'phenomene;abreviation'} \t SAJ \t [85]

Voici ce que j'obtiens avec key_list... les id ne sont pas corrects à cause de son emplacement dans la boucle (j'ai aussi essayé de le déplacer à la fin, mais je n'obtiens plus d'id avec key_list).

Filename \t Type \t text_Gold \t ID

SAJ1 \t {'features': 'phenomene;passive'} \t a été averti \t [67]

SAJ1 \t {'features': 'phenomene;passive'} \t est terminée \t [57, 94, 118]

SAJ1 \t {'features': 'phenomene;abreviation'} \t SAJ \t [57, 94, 118]

Et voici mon code complet :

import re
import xml.etree.ElementTree as ET

filename = str(input("Fichier : ")) #Nom du fichier à traiter
xmlfile = open(filename, "r", encoding="utf-8")
b = filename.strip(".xml").split('-')
textname = b[0] #Récupération du nom du texte

tree = ET.parse(xmlfile)
root = tree.getroot()
resultfile = open("result.txt", "w", encoding="utf-8")

dico1 = [] #Liste contenant les labels des balises (type de phénomènes)
dico2 = [] #Liste contenant le segment annoté

for line in xmlfile:
    re.sub(r"'","'", line) #Prétraitement des erreurs

for segment in root.iter('segment'):
    dico1.append(segment.attrib) #Liste contenant un dico
    dico2.append(segment.text) #Liste contenant des strings

all_phenomene = ["\tFilename", "Type", "text_Gold", "ID", "\n"] #Ajouter le ID (début-fin) + 

type_phenomene = ["passive", "abreviation", "subordonnee", "subordonnee;relatives", "subordonnee;completives", "subordonnee;autres"]

#"""
#Test pour l'alignement sur la base d'un id pour chaque mot du texte source sans annotation (fonctionne)
sourcefile = open("SAJ1.txt", "r", encoding="utf-8")
content = sourcefile.read()
l_content = content.split()
dico3 = {k: v for k, v in enumerate(l_content)} #Dictionnaire contenant chaque mot (valeur) associé à l'id (clé/nombre unique)
#dico3 = [{k, v} for k, v in enumerate(l_content)] #OU liste contenant des dictionnaires
#dico3 = [[k, v] for k, v in enumerate(l_content)] #OU liste contenant des listes
print(dico3) #Ex. : {0: 'score-kms:', 1: 'date:', 2: 'source:SAJ.doc', 3: 'section:1', 4: 'organe:www.aidealajeunesse.cfwb.be', 5: 'title:Demande', 6: "d'intervention…", 7: 'Le', 8: 'service', 9: 'de', 10: "l'aide", 11: 'à', 12: 'la', 13: 'jeunesse', 14: '(SAJ)', 15: 'Si', 16: 'vous', 17: 'souhaitez', ...}
sourcefile.close()

key_list = []
mot_segment = str(dico2).split() #Les segments de phrases annotés et +- tokenisés dans une liste
#"""

for phenomene in type_phenomene:
    for a, b in zip(dico1, dico2): #a est un dict ; b est un string / dico1 une liste ; dico2 une liste
        if a.get("features") == f"phenomene;{phenomene}":
            
            all_phenomene.append(textname) #Le nom du fichier
            del a['id'] #C'est l'id donné par CorpusTool (l'ordre des annotations), cela peut être utile de conserver pour retrouver le 'parent' (sub imbriquées)
            del a['state']
            all_phenomene.append(str(a)) #La balise (id de CorpusTool et type de phénomène)        
            all_phenomene.append(b) #Le segment de phrase
            
            #"""
            #Suite du test pour l'alignement (fonctionne en partie)
            c = b.split() #Problème, pas d'id pour les mots avant le split
            #print(c, len(c))
            for word in c: #Parcourt les mots des segments
                #print(mot_segment, len(mot_segment))
                if word in mot_segment: #Si le mot se trouve dans le dico3
                    #key_list = ([(k, val) for k, val in [dico3{k, val}.items()] if val == word]) #Pour chaque mot, donne tous les id possibles or on veut celui du segment en cours
                    key_list = [k  for (k, val) in dico3.items() if val == word] #Pour chaque mot, donne tous les id possibles or on veut celui du segment en cours
                    #del key_list[1:] 
                    print(key_list, word)
            all_phenomene.append(str(key_list))
            #L'idée était de comparer ces nombres et de récupérer les valeurs (mot) dont les int(clés) seraient donc une suite de nombres continue avec len>n à déterminer
            #"""
            
            all_phenomene.append("\n") #Le retour à la ligne  
  
nbre_line = (len(all_phenomene)//5)-1 #Diviser par le nbre d'éléments différents dans la liste de phénomènes, -1 à cause de l'entête des colonnes
print(f"Nombre de phénomènes dans le fichier : {nbre_line}")

"""
Autre début de piste pour l'alignement, mais sans arriver à obtenir des id...
phrases, words =[], []
for text_Gold in all_phenomene[2::4]:
    token = text_Gold.split()
    phrases.append(token)
    for word in token:
        words.append(word)

print("Phrases :", phrases)
print("Words :", words)
"""

new_all_phenomene = "\t".join(all_phenomene)

resultfile.write(new_all_phenomene)

xmlfile.close()
resultfile.close()

-
Edité par Anonyme 5 avril 2020 à 10:36:28

  • Partager sur Facebook
  • Partager sur Twitter
5 avril 2020 à 11:40:09

On t'a proposé une liste à juste titre, parce qu'un ID peut-être comparé à un index de liste.

Créer un dictionnaire en mettant un entier comme clé représentant des ID (ou index) ne sert strictement à rien...

Ta liste est facilement définissable avec un simple words = [w for w in l_content] mais tu ferais bien d'apprendre les boucles simples si tu ne maîtrises pas les boucles complexes, c'est suffisant.

Pour avoir un mot par rapport à un ID, word = words[ID]

Pour avoir l'ID par rapport à un mot, ID = words.index(word)

-
Edité par fred1599 5 avril 2020 à 11:40:31

  • Partager sur Facebook
  • Partager sur Twitter

Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

Anonyme
5 avril 2020 à 15:42:36

Merci pour ta réponse. Je rencontre néanmoins d'autres problèmes.

phrases, words, ID =[], [], []

for text_Gold in all_phenomene[2::4]:
    token = text_Gold.split()
    phrases.append(token)
    for word in token:
        word1 = list(word)
        #words.append(word)
        words = [w for w in l_content]
        print(words, "word:", word, "word1:", word1)
        print(type(words), "word:", type(word), "word1:", type(word1))
        ID = words.index(word)
        print(ID)

Que j'écrive ID = words.index(word) ou ID = words.index(word1), j'obtiens une ValueError comme quoi word ou word1 n'est pas une liste... et ce, même quand je le transforme explicitement en liste comme dans cette capture d'écran :


De plus, je pensais que la méthode .index(string) renvoie uniquement l'index de la première occurrence du mot. Si le mot du segment est le deuxième dans 'content', cela peut-il fonctionner ? 

-
Edité par Anonyme 5 avril 2020 à 15:45:41

  • Partager sur Facebook
  • Partager sur Twitter
5 avril 2020 à 16:07:08

Le print, c'est plus pour token qui m'intéresse.

Pourquoi générer les mots à chaque token alors que tu auras toujours le même résultat pour words ?

Pour ta dernière question, c'était une demande dans ton 1er topic ? Pour index regarde la doc...

Merci d'être plus clair dans la demande et d'utiliser les balises code du forum.

  • Partager sur Facebook
  • Partager sur Twitter

Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

Anonyme
5 avril 2020 à 17:25:01 - Message modéré pour le motif suivant : Merci d'utiliser le bouton code du forum pour insérer votre code


5 avril 2020 à 17:34:40

Bonjour,

Merci de colorer votre code à l'aide du bouton Code

Les forums d'Openclassrooms disposent d'une fonctionnalité permettant de colorer et mettre en forme les codes source afin de les rendre plus lisibles et faciles à manipuler par les intervenants. Pour cela, il faut utiliser le bouton Code de l'éditeur, choisir un des langages proposés et coller votre code dans la zone prévue. Si vous utilisez l'éditeur de messages en mode Markdown, il faut utiliser les balises <pre class="brush: python;">Votre code ici</pre>.

Merci de modifier votre message d'origine en fonction.

Liens conseillés

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
5 avril 2020 à 17:54:46

Ce message n'avait pas besoin d'être modéré... fred1599 m'avait simplement demandé un screenshot de print. Il n'y avait aucun code en plus par rapport à mon précédent post. Bon, je reprends puisque je ne peux pas le modifier.

Désolée, je suis encore débutante en Python, j'espère que c'est plus clair. :D

Voici le résultat de la variable que tu m'as demandé. En fait, je fais une boucle afin de parcourir les mots qui se trouvent dans la variable token afin d'associer à ces mots l'index de ces mots (le premier et le dernier) dans la liste l_content. J'ai testé plusieurs choses, mais j'obtiens toujours une ValueError à cause d'une liste.

print(l_content)
for text_Gold in all_phenomene[2::4]:
    token = text_Gold.split()
    print(token)
    for word in token:
        words = [w for w in l_content]
        ID = words.index(word)
        #print(ID, l_content)

Et oui, j'avais bien compris ce que faisait la méthode index. Le problème, c'est qu'un mot de mon texte peut apparaître plusieurs fois. Par exemple, "'à', 'la', 'Jeunesse', ..., 'à', 'une', 'convocation'". Si mon segment est le trigramme "à une convocation", la méthode sur le mot "à" va me renvoyer l'index du premier mot alors que je veux le deuxième.

list.index(x)

Retourne la position du premier élément de la liste ayant la valeur x. Une exception est levée s’il n’existe aucun élément avec cette valeur.

-
Edité par Anonyme 5 avril 2020 à 18:01:05

  • Partager sur Facebook
  • Partager sur Twitter
5 avril 2020 à 18:02:11

Alors déjà pour la modération il avait raison et il aura encore raison de virer tes screenshots illisibles.

Si je veux faire des petits tests de mon côté, il me faut la sortie de ton print(token) dans un texte que je puisse copier coller.

Il me faudra de la même manière l_content avec un petit code de ta part testable, c'est à dire que quand j'exécute, je dois reproduire la même erreur que toi... en simplement le copiant collant.

Ensuite le plus simple est de donner le format que tu attends (plusieurs exemples c'est top) qu'on puisse comparer notre code test et les résultats attendus.

Voilà normalement comment tu dois présenter ton code. Merci pour nos yeux !

  • Partager sur Facebook
  • Partager sur Twitter

Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

5 avril 2020 à 18:23:25

Galami a écrit:

Ce message n'avait pas besoin d'être modéré... fred1599 m'avait simplement demandé un screenshot de print. Il n'y avait aucun code en plus par rapport à mon précédent post. Bon, je reprends puisque je ne peux pas le modifier.

Bonjour,

Si ce message avais besoin d'être modéré. Je ne vois pas dans les messages de fred qu'il vous aie demander un screenchot, ce qui m’étonnerais d’ailleurs puisqu’il connait les règles du forum.

  Dans cette image il y a du code https://user.oc-static.com/upload/2020/04/05/15861018128331_print.png on vous demande ne pas poster de code en image, cela ne permet pas les copier/coller pour teste éventuelle et c'est difficilement lisible.

Merci de prendre connaissance des règles du forum et de les respecter.

Je ferme ce sujet pour non respect des règles du forum et non respect d'une demande de la modération.

Si vous voulez me parler en privé je suis disponible pour toutes questions en rapport avec les règles du forum. 

Liens conseillés

-
Edité par AbcAbc6 5 avril 2020 à 18:24:29

  • Partager sur Facebook
  • Partager sur Twitter