2
votes

Structure de données personnalisée via le comptage des mots par paragraphe en Python 3.7

Je dois accomplir les tâches suivantes:

  • Pour un mot ou un jeton donné, déterminez le nombre de paragraphes dans lesquels il apparaît (appelé fréquence de document)
  • Créer une structure de données (dict, dataframe pandas, etc.) qui contient le mot, sa fréquence de collecte (globale) et la fréquence du document

Un exemple d'ensemble de données ressemble à ceci:

file = re.sub('<P ID=(\d+)>', '', file)
file = re.sub('</P>', '', file)

Un "paragraphe" est défini par l'existence du

balises

Ce dont j'ai besoin est de créer une structure de données qui ressemble un peu à ceci (j'imagine que c'est un dict ):

XXX

ou, probablement un dataframe pandas qui ressemble à ceci:

Collection frequency:  {'i': 4, 'have': 3, 'always': 2, 'wanted': 2, 
'to': 4, 'try': 2, 'like': 1, 'multiple': 1, 'different': 1,
'rasteraunts': 1, 'not': 2, 'quite': 1, 'sure': 1, 'which': 1,
'kind': 1, 'maybe': 1, 'burgers': 2, 'nice': 1, 'love': 1,
'cheeseburgers': 1, 'too': 1, 'you': 1, 'ever': 1, 'gone': 1, 'a': 1,
'diner': 2, 'type': 1, 'restauraunt': 1, 'every': 1, 'in': 1, 'the': 2,
'country': 1, 'am': 1, 'related': 1, 'rest': 1, 'of': 1, 'these': 1, 
'paragraphs': 1, 'at': 1, 'all': 1}

Actuellement, je suis en mesure de trouver la fréquence du contenu sans problème en utilisant le code ci-dessous.

import nltk
import string
from nltk.tokenize import word_tokenize, RegexpTokenizer
import csv
import numpy
import operator
import re

# Requisite
def get_input(filepath):
    f = open(filepath, 'r')
    content = f.read()
    return content

# 1
def normalize_text(file):
    file = re.sub('<P ID=(\d+)>', '', file)
    file = re.sub('</P>', '', file)
    tokenizer = RegexpTokenizer(r'\w+')
    all_words = tokenizer.tokenize(file)
    lower_case = []
    for word in all_words:
        curr = word.lower()
        lower_case.append(curr)

    return lower_case

# Requisite for 3
# Answer for 4
def get_collection_frequency(a):
    g = {}
    for i in a:
        if i in g: 
            g[i] +=1
        else: 
            g[i] =1
    return g

myfile = get_input('example.txt')
words = normalize_text(myfile)

## ANSWERS
collection_frequency = get_collection_frequency(words)
print("Collection frequency: ", collection_frequency)

qui renvoie:

| Word | Content Frequency | Document Frequency |
|   i  |         4         |          3         |
| have |         3         |          2         |
| etc  |         etc       |          etc       |

Cependant, je suis en train de supprimer les "titres" d'un paragraphe dans la fonction normalize_text avec les lignes:

{'i': X Y, 'have': X Y, etc}

car je ne veux pas de P , ID , 1 , 2 , 3 à compter dans mon dictionnaire car ce ne sont que des en-têtes de paragraphe. p>

Comment puis-je relier l'occurrence d'un mot à son occurrence dans un paragraphe qui me donnerait les résultats souhaités ci-dessus? Je ne suis même pas sûr de la logique de tenter de créer une telle structure de données.


4 commentaires

Votre exemple d'ensemble de données est représentatif? Il y aura toujours des balises d'ouverture et de fermeture P ?


Oui. Il est généré à partir d'un rapport que notre société exécute. Je crois qu'il pourrait y avoir le cas où rien n'est dans les balises, mais je n'ai jamais vu cela. Quoi qu'il en soit, les balises elles-mêmes existeront toujours. Je n'ai copié que trois exemples pour en faire un sous-ensemble représentatif, mais il y en a plus de 20000 dans certains fichiers (tous avec des balises ouvertes et fermées) @wwii


Devez-vous utiliser nltk ?


Non, en dehors des deux balles, rien n'est "requis". J'ai utilisé nltk parce que c'était facile pour un novice comme moi. @wwii Je peux utiliser ou ne pas utiliser à peu près tout ce que je veux (en supposant que nous n'avons rien à payer)


3 Réponses :


1
votes

Essayez ceci:

def find_paragraph_occurrences(filepath):
    with open(filepath, 'r') as f:
        file = f.read()
    word_list = normalize_text(file)
    data = file.replace('</P>','').lower().split('<P ID=')
    result = {}
    for word in word_list:
        result[word] = 0
        for p in data:
            if word in p:
                result[word] += 1

    print(result)
    return result

Si vous voulez grouper par paragraphe, puis par occurrences de mots:

def find_words(filepath):
    with open(filepath, 'r') as f:
        file = f.read()
    word_list = normalize_text(file)
    data = file.replace('</P>','').split('<P ID=')
    result = {}
    for p in data:
        if p:
            result[f'paragraph_{p[0]}'] = {}
            for word in word_list:
                result[f'paragraph_{p[0]}'][word] = p[2:].count(word)


    print(result)
    return result 

C'est encore un peu difficile à lire cependant. Si la jolie impression de l'objet est importante pour vous, vous pouvez essayer d'utiliser un un joli package d'impression a>.

Pour trouver le nombre de paragraphes dans lesquels un mot apparaît:

import re
from nltk.tokenize import word_tokenize, RegexpTokenizer

def normalize_text(file):
    file = re.sub('<P ID=(\d+)>', '', file)
    file = re.sub('</P>', '', file)
    tokenizer = RegexpTokenizer(r'\w+')
    all_words = tokenizer.tokenize(file)
    lower_case = []
    for word in all_words:
        curr = word.lower()
        lower_case.append(curr)

    return lower_case

def find_words(filepath):
    with open(filepath, 'r') as f:
        file = f.read()
    word_list = normalize_text(file)
    data = file.replace('</P>','').split('<P ID=')
    result = {}
    for word in word_list:
        result[word] = {}
        for p in data:
            if p:
                result[word][f'paragraph_{p[0]}'] = p[2:].count(word)
    print(result)
    return result

find_words('./test.txt')


6 commentaires

c'est incroyable. Je ne peux pas comprendre comment créer une telle structure. Comment puis-je créer quelque chose d'un peu différent, cependant. Idéalement, je ne voudrais pas rechercher un mot spécifique, mais plutôt compter la fréquence des mots qui existent (c'est-à-dire les résultats de ma fonction normalize_text ). Je cherche à trouver le nombre de paragraphes dans lesquels ils apparaissent chacun, pas nécessairement les paragraphes dans lesquels ils apparaissent (puisque certains fichiers contiennent plus de 20 000 paragraphes). J'ai essayé de refléter ce besoin dans ma question. S'il vous plaît laissez-moi savoir si je ne suis pas clair. Je suis désespéré.


Désolé, je suis lent. J'ai remplacé le ['Cheeseburgers', 'is'] par mon objet de liste mots et cela fonctionne. Mais, aussi élégant que le format «word»: «paragraph_x»: y_occurrences », il est très difficile à lire. Comment puis-je obtenir à la place le nombre total de paragraphes, pas le nombre total d'occurrences groupe par paragraphe?


vous pouvez échanger les boucles contre pour ce faire. Une seconde, j'ajouterai une autre mise à jour. J'ai également ajouté une mise à jour en utilisant votre fonction normalize_text


@JerryM. si vous n'aimez toujours pas le format et si les ID de paragraphe commencent toujours par 1, vous pouvez donner à chaque mot un tableau d'occurrences pour chaque paragraphe. Par exemple {"i": [6, 1, 2, 0]}


Merci beaucoup Caleb. J'espère accepter votre réponse comme réponse, et j'ai enregistré toutes les variantes. Est-il possible de compter simplement le nombre de paragraphes dans lesquels un mot apparaît? c'est-à-dire {'i': 3, 'have': 2, etc}


continuons cette discussion dans le chat .



1
votes
document: Counter({'i': 4, 'to': 4, 'have': 3, 'always': 2, 'wanted': 2, 'try': 2, 'not': 2,'burgers': 2, 'diner': 2, 'the': 2, 'like': 1, 'multiple': 1, 'different': 1, 'rasteraunts':1, 'quite': 1, 'sure': 1, 'which': 1, 'kind': 1, 'maybe': 1, 'nice': 1, 'love': 1, 'cheeseburgers': 1, 'too': 1, 'you': 1, 'ever': 1, 'gone': 1, 'a': 1, 'type': 1, 'restauraunt': 1, 'every': 1, 'in': 1, 'country': 1, 'am': 1, 'related': 1, 'rest': 1, 'of': 1, 'these': 1, 'paragraphs': 1, 'at': 1, 'all': 1})
paragraph:  Counter({'to': 3, 'i': 3, 'try': 2, 'have': 2, 'burgers': 2, 'wanted': 2, 'always': 2, 'not': 2, 'the': 2, 'which': 1, 'multiple': 1, 'quite': 1, 'rasteraunts': 1, 'kind': 1, 'like': 1, 'maybe': 1, 'sure': 1, 'different': 1, 'love': 1, 'too': 1, 'in': 1, 'restauraunt': 1, 'every': 1, 'nice': 1, 'cheeseburgers': 1, 'diner': 1, 'ever': 1, 'a': 1, 'type': 1, 'you': 1, 'country': 1, 'gone': 1, 'at': 1, 'related': 1, 'paragraphs': 1, 'rest': 1, 'of': 1,'am': 1, 'these': 1, 'all': 1})

0 commentaires

1
votes

Comment puis-je relier l'occurrence d'un mot à son occurrence dans un paragraphe qui me donnerait les résultats souhaités ci-dessus?

Divisez le processus en deux parties: recherche de paragraphes et recherche de mots

from bs4 import BeautifulSoup
soup = BeautifulSoup(text,"html.parser")
for para in soup.find_all('p'):
    tokens = [word.lower() for word in words.tokenize(para.text)]
    print(tokens)
##    col_freq.update(tokens)
##    doc_freq.update(set(tokens))

Lors de l'analyse, conservez deux dictionnaires: un pour la fréquence de collecte et un pour la fréquence des documents.

d = {word:(col_freq[word], doc_freq[word]) for word in col_freq}

Itérer sur les paragraphes; obtenir les mots dans un paragraphe; alimentez les mots dans le dict col_freq, et alimentez un ensemble de mots dans le dict doc_freq

for para in paras.tokenize(text):
    tokens = [word.lower() for word in words.tokenize(para)]
    col_freq.update(tokens)
    doc_freq.update(set(tokens))

Combinez les deux dictionnaires.

col_freq = collections.Counter()
doc_freq = collections.Counter()

6 commentaires

Les collections ne sont-elles pas indéfinies dans ce sens?


?? oui, j'ai oublié la déclaration d'importation - édition maintenant - thnx


Je voulais m'assurer avant de voter et d'accepter. C'est une excellente idée et c'est exactement ce que je recherchais et répond parfaitement aux exigences (aussi, c'est assez rapide, pourrais-je ajouter).


Bonus: importez les pandas en tant que pd; df = pd.DataFrame (d, index = ('collection', 'document')). T


J'ai tellement à apprendre. C'est incroyable. Je poserai une question si nécessaire, mais cela semble avoir tout ce dont je pourrais avoir besoin. J'espère que c'était une bonne question pour vous


Dès le début de votre question, il semble que vous ayez identifié que vous aviez deux exigences / processus similaires mais distincts, mais que vous avez ensuite rédigé une solution pour l'un d'entre eux sans prévoir l'autre. Peut-être que si vous aviez passé plus de temps à réfléchir aux similitudes et aux différences, vous ne vous seriez pas écrit dans un coin. Parfois, il est utile d'écrire des idées sur papier et de les revoir avant d'écrire un code.